วิธีเขียนการทดสอบหน่วยสำหรับ Angular / TypeScript สำหรับวิธีการส่วนตัวด้วย Jasmine


197

คุณจะทดสอบฟังก์ชั่นส่วนตัวในเชิงมุม 2 ได้อย่างไร

class FooBar {

    private _status: number;

    constructor( private foo : Bar ) {
        this.initFooBar();

    }

    private initFooBar(){
        this.foo.bar( "data" );
        this._status = this.fooo.foo();
    }

    public get status(){
        return this._status;
    }

}

ทางออกที่ฉันพบ

  1. วางรหัสทดสอบไว้ในส่วนปิดหรือเพิ่มรหัสภายในส่วนที่ปิดซึ่งเก็บการอ้างอิงถึงตัวแปรโลคัลบนวัตถุที่มีอยู่ในขอบเขตด้านนอก

    ถอดแถบทดสอบออกโดยใช้เครื่องมือในภายหลัง http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/

โปรดแนะนำวิธีที่ดีกว่าในการแก้ปัญหานี้หากคุณทำสิ่งใดไปแล้ว?

PS

  1. คำตอบส่วนใหญ่สำหรับคำถามที่คล้ายกันเช่นนี้ไม่ได้แก้ปัญหานั่นคือเหตุผลที่ฉันถามคำถามนี้

  2. นักพัฒนาส่วนใหญ่บอกว่าคุณไม่ได้ทดสอบฟังก์ชั่นส่วนตัว แต่ฉันไม่ได้บอกว่ามันผิดหรือถูก แต่มีความจำเป็นที่เคสของฉันจะต้องทดสอบความเป็นส่วนตัว


11
การทดสอบควรทดสอบอินเทอร์เฟซสาธารณะเท่านั้นไม่ใช่การใช้งานส่วนตัว การทดสอบที่คุณทำบนส่วนต่อประสานสาธารณะควรครอบคลุมส่วนที่เป็นส่วนตัวด้วย
toskv

16
ฉันชอบที่คำตอบครึ่งความเห็นควรเป็นอย่างไร OP ถามคำถามคุณ X อย่างไร คำตอบที่ได้รับการยอมรับจริง ๆ แล้วบอกคุณว่าจะทำอย่างไร X แล้วคนอื่น ๆ ส่วนใหญ่หันไปพูดว่าไม่เพียง แต่ฉันจะไม่บอกคุณ X (ซึ่งเป็นไปได้ชัดเจน) แต่คุณควรจะทำ Y. เครื่องมือทดสอบหน่วยส่วนใหญ่ พูดถึง JavaScript ที่นี่) สามารถทดสอบฟังก์ชั่น / วิธีการส่วนตัวได้ ฉันจะอธิบายต่อไปว่าทำไมเพราะดูเหมือนว่าจะหลงทางในดินแดน JS (เห็นได้ชัดว่าได้รับคำตอบครึ่งหนึ่ง)
Quaternion

13
เป็นแนวปฏิบัติที่ดีในการเขียนโปรแกรมเพื่อแยกปัญหาออกเป็นงานที่จัดการได้ดังนั้นฟังก์ชั่น "foo (x: type)" จะเรียกฟังก์ชันส่วนตัว a (x: type), b (x: type), c (y: another_type) และ d ( Z: yet_another_type) ตอนนี้เนื่องจาก foo จัดการการโทรและรวมเข้าด้วยกันมันสร้างความปั่นป่วนเช่นด้านหลังของหินในลำธารเงาที่ยากมากที่จะตรวจสอบทุกช่วง ดังนั้นจึงเป็นเรื่องง่ายที่จะตรวจสอบให้แน่ใจว่าแต่ละชุดย่อยนั้นมีความถูกต้องหากคุณพยายามทดสอบผู้ปกครอง "foo" เพียงอย่างเดียวการทดสอบช่วงจะซับซ้อนมากในบางกรณี
Quaternion

18
นี่ไม่ได้เป็นการบอกว่าคุณไม่ได้ทดสอบอินเทอร์เฟซสาธารณะแน่นอนว่าคุณทำ แต่การทดสอบวิธีการส่วนตัวช่วยให้คุณทดสอบชุดของชิ้นส่วนที่จัดการได้สั้น ๆ (ด้วยเหตุผลเดียวกับที่คุณเขียนไว้ตั้งแต่แรกทำไมคุณเลิกทำ สิ่งนี้เมื่อมันมาถึงการทดสอบ) และเพียงเพราะการทดสอบในส่วนต่อประสานสาธารณะที่ถูกต้อง (บางทีฟังก์ชั่นการโทร จำกัด ช่วงอินพุต) ไม่ได้หมายความว่าวิธีการส่วนตัวไม่ได้มีข้อบกพร่องเมื่อคุณเพิ่มตรรกะขั้นสูงเพิ่มเติม ฟังก์ชั่นหลักใหม่,
Quaternion

5
หากคุณทดสอบพวกเขาอย่างถูกต้องด้วย TDD คุณจะไม่พยายามคิดว่าคุณกำลังทำอะไรในภายหลังเมื่อคุณควรทดสอบพวกเขาอย่างถูกต้อง
Quaternion

คำตอบ:


345

ฉันอยู่กับคุณแม้ว่าจะเป็นเป้าหมายที่ดีในการ "ทดสอบเฉพาะหน่วย API สาธารณะ" มีบางครั้งที่ดูเหมือนจะไม่ง่ายและคุณรู้สึกว่าคุณกำลังเลือกระหว่างการประนีประนอมกับ API หรือการทดสอบหน่วย คุณรู้เรื่องนี้อยู่แล้วเพราะนั่นคือสิ่งที่คุณขอให้ทำดังนั้นฉันจะไม่เข้าไป :)

ใน TypeScript ฉันได้ค้นพบสองสามวิธีที่คุณสามารถเข้าถึงสมาชิกส่วนตัวเพื่อการทดสอบหน่วย พิจารณาคลาสนี้:

class MyThing {

    private _name:string;
    private _count:number;

    constructor() {
        this.init("Test", 123);
    }

    private init(name:string, count:number){
        this._name = name;
        this._count = count;
    }

    public get name(){ return this._name; }

    public get count(){ return this._count; }

}

แม้ว่าการเข้าถึง TS ข้อกำหนดด้านให้กับสมาชิกระดับการใช้private, protected, public, รวบรวม JS ไม่มีสมาชิกส่วนตัวตั้งแต่นี้ไม่ได้เป็นสิ่งใน JS มันใช้หมดจดสำหรับคอมไพเลอร์ TS ดังนั้น:

  1. คุณสามารถยืนยันanyและหลีกเลี่ยงคอมไพเลอร์จากเตือนคุณเกี่ยวกับข้อ จำกัด การเข้าถึง:

    (thing as any)._name = "Unit Test";
    (thing as any)._count = 123;
    (thing as any).init("Unit Test", 123);

    ปัญหาของวิธีนี้คือคอมไพเลอร์ไม่ทราบว่าคุณกำลังทำอะไรอยู่anyดังนั้นคุณจะไม่ได้รับข้อผิดพลาดประเภทที่ต้องการ:

    (thing as any)._name = 123; // wrong, but no error
    (thing as any)._count = "Unit Test"; // wrong, but no error
    (thing as any).init(0, "123"); // wrong, but no error

    สิ่งนี้จะทำให้การปรับโครงสร้างทำได้ยากขึ้น

  2. คุณสามารถใช้การเข้าถึงอาร์เรย์ ( []) เพื่อรับสมาชิกส่วนตัว:

    thing["_name"] = "Unit Test";
    thing["_count"] = 123;
    thing["init"]("Unit Test", 123);

    ในขณะที่มันดูขี้ขลาด TSC จะตรวจสอบประเภทจริง ๆ ราวกับว่าคุณเข้าถึงพวกเขาโดยตรง:

    thing["_name"] = 123; // type error
    thing["_count"] = "Unit Test"; // type error
    thing["init"](0, "123"); // argument error

    บอกตามตรงฉันไม่รู้ว่าทำไมมันถึงใช้ได้ เห็นได้ชัดว่าเป็น"ช่องทางหนี" ที่ตั้งใจให้คุณเข้าถึงสมาชิกส่วนตัวโดยไม่สูญเสียความปลอดภัย นี่คือสิ่งที่ฉันคิดว่าคุณต้องการสำหรับการทดสอบหน่วยของคุณ

นี่คือตัวอย่างการทำงานใน typescript สนามเด็กเล่น

แก้ไขสำหรับ TypeScript 2.6

ตัวเลือกอื่นที่มีลักษณะคล้ายกันคือการใช้// @ts-ignore( เพิ่มใน TS 2.6 ) ซึ่งจะไม่แสดงข้อผิดพลาดทั้งหมดในบรรทัดต่อไปนี้:

// @ts-ignore
thing._name = "Unit Test";

ปัญหาของเรื่องนี้คือมันจะหยุดข้อผิดพลาดทั้งหมดในบรรทัดต่อไปนี้:

// @ts-ignore
thing._name(123).this.should.NOT.beAllowed("but it is") = window / {};

ฉันคิดว่า@ts-ignoreเป็นการใช้รหัสกลิ่นและเอกสารบอกว่า:

เราขอแนะนำให้คุณใช้ความคิดเห็นนี้มากเท่าที่จำเป็น [เน้นต้นฉบับ]


45
ดีมากที่ได้ยินจุดยืนที่สมจริงในการทดสอบหน่วยพร้อมกับการแก้ปัญหาที่เกิดขึ้นจริงมากกว่าคุณทดสอบมาตรฐานหน่วย dogma
d512

2
คำอธิบายบางอย่าง "อย่างเป็นทางการ" ของพฤติกรรม (ซึ่งยังอ้างถึงการทดสอบหน่วยเป็นกรณีการใช้งาน): github.com/microsoft/TypeScript/issues/19335
Aaron Beall

1
เพียงแค่ใช้ `// @ ts-ละเว้น 'ตามที่อธิบายไว้ด้านล่าง เพื่อบอกให้ผู้ให้เช่าเพิกเฉยต่อการเข้าถึงส่วนตัว
Tommaso

1
@ Tomaso ใช่นั่นเป็นอีกตัวเลือกหนึ่ง แต่ก็มีข้อเสียเหมือนกันกับการใช้งานas any: คุณสูญเสียการตรวจสอบประเภททั้งหมด
Aaron Beall

2
คำตอบที่ดีที่สุดที่ฉันเห็นในขณะที่ขอบคุณ @AaronBeall และขอขอบคุณ tymspy ที่ถามคำถามเดิม
nicolas.leblanc

27

คุณสามารถโทรหาวิธีเอกชน หากคุณพบข้อผิดพลาดต่อไปนี้:

expect(new FooBar(/*...*/).initFooBar()).toEqual(/*...*/);
// TS2341: Property 'initFooBar' is private and only accessible within class 'FooBar'

เพียงใช้// @ts-ignore:

// @ts-ignore
expect(new FooBar(/*...*/).initFooBar()).toEqual(/*...*/);

นี่ควรจะอยู่ด้านบน!
jsnewbie

2
นี่เป็นอีกตัวเลือกหนึ่งอย่างแน่นอน มันมีปัญหาจากปัญหาเดียวกับas anyที่คุณสูญเสียการตรวจสอบชนิดใด ๆ จริง ๆ แล้วคุณสูญเสียการตรวจสอบชนิดในบรรทัดทั้งหมด
Aaron Beall

20

เนื่องจากนักพัฒนาซอฟต์แวร์ส่วนใหญ่ไม่แนะนำให้ทดสอบฟังก์ชั่นส่วนตัวทำไมไม่ลองทดสอบดู

เช่น.

YourClass.ts

export class FooBar {
  private _status: number;

  constructor( private foo : Bar ) {
    this.initFooBar({});
  }

  private initFooBar(data){
    this.foo.bar( data );
    this._status = this.foo.foo();
  }
}

TestYourClass.spec.ts

describe("Testing foo bar for status being set", function() {

...

//Variable with type any
let fooBar;

fooBar = new FooBar();

...
//Method 1
//Now this will be visible
fooBar.initFooBar();

//Method 2
//This doesn't require variable with any type
fooBar['initFooBar'](); 
...
}

ขอบคุณ @Aaron, @Thierry Templier


1
ฉันคิดว่า typescript ให้ข้อผิดพลาด linting เมื่อคุณพยายามเรียกวิธีการส่วนตัว / ป้องกัน
Gudgip

1
@Gudgip มันจะให้ข้อผิดพลาดประเภทและจะไม่รวบรวม :)
Tymspy

10

อย่าเขียนการทดสอบสำหรับวิธีการส่วนตัว นี่คือจุดของการทดสอบหน่วยเอาชนะ

  • คุณควรทดสอบ API สาธารณะของชั้นเรียนของคุณ
  • คุณไม่ควรทดสอบรายละเอียดของชั้นเรียนของคุณ

ตัวอย่าง

class SomeClass {

  public addNumber(a: number, b: number) {
      return a + b;
  }
}

การทดสอบสำหรับวิธีนี้ไม่จำเป็นต้องเปลี่ยนหากภายหลังการเปลี่ยนแปลงการใช้งาน แต่behaviourAPI สาธารณะยังคงเหมือนเดิม

class SomeClass {

  public addNumber(a: number, b: number) {
      return this.add(a, b);
  }

  private add(a: number, b: number) {
       return a + b;
  }
}

อย่าทำให้วิธีการและคุณสมบัติเป็นสาธารณะเพียงเพื่อทดสอบ ซึ่งมักจะหมายความว่า:

  1. คุณกำลังพยายามทดสอบการใช้งานมากกว่า API (ส่วนต่อประสานสาธารณะ)
  2. คุณควรย้ายตรรกะที่เป็นปัญหาไปยังชั้นเรียนของตนเองเพื่อให้การทดสอบง่ายขึ้น

3
อาจจะอ่านโพสต์ก่อนแสดงความคิดเห็น ฉันระบุอย่างชัดเจนและแสดงให้เห็นว่าการทดสอบแบบส่วนตัวเป็นกลิ่นของการทดสอบการใช้งานมากกว่าพฤติกรรมซึ่งนำไปสู่การทดสอบที่เปราะบาง
Martin

1
ลองนึกภาพวัตถุที่ให้ตัวเลขสุ่มระหว่าง 0 ถึงทรัพย์สินส่วนตัว x หากคุณต้องการทราบว่า x ถูกตั้งค่าอย่างถูกต้องโดยนวกรรมิกมันง่ายกว่ามากในการทดสอบค่าของ x มากกว่าทำการทดสอบเป็นร้อยเพื่อตรวจสอบว่าตัวเลขที่คุณได้รับอยู่ในช่วงที่เหมาะสมหรือไม่
Galdor

1
@ user3725805 นี่เป็นตัวอย่างของการทดสอบการใช้งานไม่ใช่พฤติกรรม มันจะเป็นการดีกว่าที่จะแยกว่าหมายเลขส่วนตัวมาจากไหน: ค่าคงที่ค่ากำหนดคอนสตรัคเตอร์ - และทดสอบจากที่นั่น หากส่วนตัวไม่ได้มาจากแหล่งอื่น ๆ ก็จะตกอยู่ใน "หมายเลขมายากล" antipattern
Martin

1
และทำไมจึงไม่อนุญาตให้ทดสอบการใช้งาน การทดสอบหน่วยดีเพื่อตรวจจับการเปลี่ยนแปลงที่ไม่คาดคิด เมื่อด้วยเหตุผลบางอย่างคอนสตรัคลืมการตั้งค่าจำนวนการทดสอบล้มเหลวทันทีและเตือนฉัน เมื่อมีคนเปลี่ยนแปลงการใช้งานการทดสอบก็ล้มเหลวเช่นกัน แต่ฉันชอบที่จะใช้การทดสอบหนึ่งครั้งมากกว่าที่จะมีข้อผิดพลาดที่ตรวจไม่พบ
Galdor

2
+1 คำตอบที่ดี @TimJames การบอกการปฏิบัติที่ถูกต้องหรือชี้ให้เห็นถึงข้อบกพร่องเป็นจุดประสงค์ของ SO แทนที่จะค้นหาหนทางที่เปราะบางเพื่อให้ได้ตามที่ OP ต้องการ
Syed Aqeel Ashiq

4

ประเด็นของการ "ไม่ทดสอบวิธีส่วนตัว" จริงๆคือทดสอบชั้นเหมือนใครบางคนที่ใช้มันการทดสอบชั้นเหมือนใครบางคนที่ใช้มัน

หากคุณมี API สาธารณะด้วย 5 วิธีผู้บริโภคในชั้นเรียนของคุณสามารถใช้สิ่งเหล่านี้ได้ดังนั้นคุณควรทดสอบพวกเขา ผู้บริโภคไม่ควรเข้าถึงวิธีการ / คุณสมบัติส่วนตัวของชั้นเรียนของคุณซึ่งหมายความว่าคุณสามารถเปลี่ยนสมาชิกส่วนตัวเมื่อฟังก์ชั่นการเปิดเผยสาธารณะยังคงเหมือนเดิม


หากคุณอาศัยการทำงานขยายภายในใช้แทนprotected โปรดทราบว่ายังคงเป็นAPI สาธารณะ (!)เพียงใช้แตกต่างกันprivate
protected

class OverlyComplicatedCalculator {
    public add(...numbers: number[]): number {
        return this.calculate((a, b) => a + b, numbers);
    }
    // can't be used or tested via ".calculate()", but it is still part of your public API!
    protected calculate(operation, operands) {
        let result = operands[0];
        for (let i = 1; i < operands.length; operands++) {
            result = operation(result, operands[i]);
        }
        return result;
    }
}

คุณสมบัติการป้องกันการทดสอบหน่วยในลักษณะเดียวกับที่ผู้บริโภคจะใช้พวกมันผ่านคลาสย่อย:

it('should be extensible via calculate()', () => {
    class TestCalculator extends OverlyComplicatedCalculator {
        public testWithArrays(array: any[]): any[] {
            const concat = (a, b) => [].concat(a, b);
            // tests the protected method
            return this.calculate(concat, array);
        }
    }
    let testCalc = new TestCalculator();
    let result = testCalc.testWithArrays([1, 'two', 3]);
    expect(result).toEqual([1, 'two', 3]);
});


2

ขออภัยสำหรับ necro ในโพสต์นี้ แต่ฉันรู้สึกถูกบังคับให้ต้องคำนึงถึงบางสิ่งที่ดูเหมือนจะไม่ได้รับการสัมผัส

สิ่งสำคัญอันดับแรก - เมื่อเราพบว่าตัวเองต้องการเข้าถึงสมาชิกส่วนตัวในชั้นเรียนในระหว่างการทดสอบหน่วยโดยทั่วไปเป็นธงสีแดงขนาดใหญ่ที่เราได้ทำผิดพลาดในเชิงกลยุทธ์หรือยุทธวิธีและมีการละเมิดหลักความรับผิดชอบโดยไม่ตั้งใจ พฤติกรรมที่ไม่ได้เป็นของ การรู้สึกถึงความจำเป็นในการเข้าถึงวิธีการที่ไม่มีอะไรมากไปกว่ารูทีนย่อยแบบแยกของกระบวนการก่อสร้างเป็นหนึ่งในเหตุการณ์ที่เกิดขึ้นบ่อยที่สุด อย่างไรก็ตามมันก็เหมือนกับเจ้านายของคุณที่คาดหวังว่าคุณจะปรากฏตัวขึ้นสำหรับการทำงานพร้อมไปและยังมีความจำเป็นที่จะต้องรู้ว่ากิจวัตรประจำวันตอนเช้าที่คุณผ่านมาเพื่อให้คุณเข้าสู่สถานะที่ ...

อินสแตนซ์อื่น ๆ ที่พบบ่อยที่สุดของเหตุการณ์นี้คือเมื่อคุณพบว่าตัวเองพยายามทดสอบ "ชั้นพระเจ้า" ที่เป็นสุภาษิต มันเป็นปัญหาแบบพิเศษที่เกิดขึ้นในตัวของมันเอง แต่ก็มีปัญหาพื้นฐานเดียวกันกับที่จำเป็นต้องรู้รายละเอียดของกระบวนการอย่างใกล้ชิด

ในตัวอย่างที่เฉพาะเจาะจงนี้เราได้มอบหมายความรับผิดชอบในการกำหนดค่าเริ่มต้นอย่างสมบูรณ์วัตถุบาร์ให้กับตัวสร้างของ FooBar คลาส ในการเขียนโปรแกรมเชิงวัตถุหนึ่งในผู้เช่าหลักคือว่าตัวสร้างคือ "ศักดิ์สิทธิ์" และควรได้รับการปกป้องจากข้อมูลที่ไม่ถูกต้องซึ่งจะทำให้สถานะภายในของตนเองเป็นโมฆะและปล่อยให้มันล้มเหลวที่อื่นลง (ในสิ่งที่อาจลึกมาก ท่อ.)

เราไม่สามารถทำสิ่งนี้ได้โดยการอนุญาตให้วัตถุ FooBar ยอมรับบาร์ที่ยังไม่พร้อมในเวลาที่สร้าง FooBar และชดเชยด้วยการ "แฮ็ค" วัตถุ FooBar เพื่อให้ความสำคัญกับตัวเอง มือ.

นี่คือผลของความล้มเหลวในการยึดติดกับการเขียนโปรแกรมเชิงวัตถุอื่น ๆ (ในกรณีของ Bar) ซึ่งเป็นสถานะของวัตถุที่จะเริ่มต้นได้อย่างเต็มที่และพร้อมที่จะจัดการกับสายเรียกเข้าใด ๆ กับสมาชิกสาธารณะทันทีหลังจากการสร้าง ตอนนี้ไม่ได้หมายความว่าทันทีหลังจากที่นวกรรมิกถูกเรียกในทุกกรณี เมื่อคุณมีวัตถุที่มีสถานการณ์การก่อสร้างที่ซับซ้อนจำนวนมากดังนั้นจะเป็นการดีกว่าที่จะให้ setters แก่สมาชิกที่เป็นตัวเลือกไปยังวัตถุที่ถูกนำไปใช้ตามรูปแบบการออกแบบการสร้าง (Factory, Builder, ฯลฯ ... ) กรณีหลัง

ในตัวอย่างของคุณคุณสมบัติ "สถานะ" ของบาร์ดูเหมือนจะไม่อยู่ในสถานะที่ถูกต้องซึ่ง FooBar สามารถยอมรับได้ดังนั้น FooBar จึงทำบางสิ่งเพื่อแก้ไขปัญหานั้น

ปัญหาที่สองที่ฉันเห็นคือปรากฏว่าคุณกำลังพยายามทดสอบรหัสของคุณแทนที่จะฝึกพัฒนาโดยใช้การทดสอบ นี่เป็นความเห็นของฉันในเวลานี้ แต่การทดสอบประเภทนี้เป็นรูปแบบต่อต้านจริง ๆ สิ่งที่คุณทำคือตกหลุมพรางของการตระหนักว่าคุณมีปัญหาการออกแบบหลักที่ป้องกันไม่ให้โค้ดของคุณทดสอบได้จริงหลังจากเขียนแบบทดสอบที่คุณต้องการแล้วเขียนโปรแกรมไปทดสอบ ไม่ว่าคุณจะมาที่ปัญหาอย่างไรคุณก็ควรจบด้วยการทดสอบจำนวนมากและบรรทัดของรหัสที่คุณได้รับการใช้งานที่แท้จริง เหตุใดจึงต้องลองทำวิศวกรรมย้อนกลับให้เป็นรหัสที่สามารถทดสอบได้เมื่อคุณสามารถจัดการกับปัญหาเมื่อเริ่มต้นความพยายามในการพัฒนาของคุณ

ถ้าคุณทำอย่างนั้นแล้วคุณจะรู้ก่อนหน้านี้มากว่าคุณจะต้องเขียนโค้ดที่ค่อนข้างลำบากเพื่อทดสอบกับการออกแบบของคุณและจะมีโอกาสก่อนที่จะปรับวิธีการของคุณโดยปรับพฤติกรรมการใช้งาน ทดสอบได้ง่าย


2

ฉันเห็นด้วยกับ @toskv: ฉันจะไม่แนะนำให้ทำเช่นนั้น :-)

แต่ถ้าคุณต้องการทดสอบวิธีส่วนตัวของคุณจริงๆคุณสามารถทราบได้ว่าโค้ดที่เกี่ยวข้องสำหรับ TypeScript นั้นสอดคล้องกับวิธีการของฟังก์ชั่นต้นแบบของตัวสร้าง ซึ่งหมายความว่าสามารถใช้ที่รันไทม์ (ในขณะที่คุณอาจมีข้อผิดพลาดในการรวบรวม)

ตัวอย่างเช่น:

export class FooBar {
  private _status: number;

  constructor( private foo : Bar ) {
    this.initFooBar({});
  }

  private initFooBar(data){
    this.foo.bar( data );
    this._status = this.foo.foo();
  }
}

จะถูกเปลี่ยนเป็น:

(function(System) {(function(__moduleName){System.register([], function(exports_1, context_1) {
  "use strict";
  var __moduleName = context_1 && context_1.id;
  var FooBar;
  return {
    setters:[],
    execute: function() {
      FooBar = (function () {
        function FooBar(foo) {
          this.foo = foo;
          this.initFooBar({});
        }
        FooBar.prototype.initFooBar = function (data) {
          this.foo.bar(data);
          this._status = this.foo.foo();
        };
        return FooBar;
      }());
      exports_1("FooBar", FooBar);
    }
  }
})(System);

ดู plunkr นี้: https://plnkr.co/edit/calJCF?p=preview


1

ตามที่หลายคนได้กล่าวไปแล้วมากเท่าที่คุณต้องการทดสอบวิธีการส่วนตัวที่คุณไม่ควรแฮกรหัสหรือ transpiler ของคุณเพื่อให้มันทำงานได้สำหรับคุณ TypeScript สมัยใหม่จะปฏิเสธการแฮ็กส่วนใหญ่ทั้งหมดที่ผู้คนให้มา


สารละลาย

TLDR ; หากวิธีการควรทดสอบแล้วคุณควร decoupling รหัสในชั้นเรียนที่คุณสามารถเปิดเผยวิธีการเป็นสาธารณะที่จะทดสอบ

เหตุผลที่คุณมีเมธอดไพรเวตคือเนื่องจากฟังก์ชันไม่จำเป็นต้องเป็นของคลาสนั้นและถ้าหากฟังก์ชันไม่ได้อยู่ที่นั่นก็ควรถูกแยกออกเป็นคลาสของตัวเอง

ตัวอย่าง

ฉันวิ่งข้ามบทความนี้ซึ่งเป็นงานที่ดีในการอธิบายวิธีที่คุณควรจัดการกับวิธีการทดสอบส่วนตัว มันครอบคลุมวิธีการบางอย่างที่นี่และทำไมพวกเขาถึงใช้งานไม่ดี

https://patrickdesjardins.com/blog/how-to-unit-test-private-method-in-typescript-part-2

บันทึก : รหัสนี้ถูกยกจากบล็อกที่ลิงค์ด้านบน (ฉันกำลังทำซ้ำในกรณีที่เนื้อหาที่อยู่เบื้องหลังการเปลี่ยนแปลงการเชื่อมโยง)

ก่อน
class User{
    public getUserInformationToDisplay(){
        //...
        this.getUserAddress();
        //...
    }

    private getUserAddress(){
        //...
        this.formatStreet();
        //...
    }
    private formatStreet(){
        //...
    }
}
หลังจาก
class User{
    private address:Address;
    public getUserInformationToDisplay(){
        //...
        address.getUserAddress();
        //...
    }
}
class Address{
    private format: StreetFormatter;
    public format(){
        //...
        format.ToString();
        //...
    }
}
class StreetFormatter{
    public toString(){
        // ...
    }
}

1

เรียกวิธีส่วนตัวโดยใช้วงเล็บเหลี่ยม

ไฟล์ Ts

class Calculate{
  private total;
  private add(a: number) {
      return a + total;
  }
}

ไฟล์ spect.ts

it('should return 5 if input 3 and 2', () => {
    component['total'] = 2;
    let result = component['add'](3);
    expect(result).toEqual(5);
});

0

คำตอบของแอรอนนั้นดีที่สุดและทำงานให้ฉัน :) ฉันจะลงคะแนน แต่น่าเสียดายที่ฉันไม่สามารถ (ขาดชื่อเสียง)

ฉันจะบอกว่าการทดสอบวิธีการส่วนตัวเป็นวิธีเดียวที่จะใช้พวกเขาและมีรหัสที่สะอาดในด้านอื่น ๆ

ตัวอย่างเช่น:

class Something {
  save(){
    const data = this.getAllUserData()
    if (this.validate(data))
      this.sendRequest(data)
  }
  private getAllUserData () {...}
  private validate(data) {...}
  private sendRequest(data) {...}
}

มันสมเหตุสมผลมากที่จะไม่ทดสอบวิธีการเหล่านี้ทั้งหมดในครั้งเดียวเพราะเราจำเป็นต้องจำลองวิธีการส่วนตัวเหล่านั้นซึ่งเราไม่สามารถเยาะเย้ยได้เพราะเราไม่สามารถเข้าถึงได้ ซึ่งหมายความว่าเราต้องการการกำหนดค่าจำนวนมากสำหรับการทดสอบหน่วยเพื่อทดสอบทั้งหมด

สิ่งนี้กล่าวว่าวิธีที่ดีที่สุดในการทดสอบวิธีการข้างต้นกับการอ้างอิงทั้งหมดคือการทดสอบแบบครบวงจรเนื่องจากที่นี่จำเป็นต้องมีการทดสอบการรวม แต่การทดสอบ E2E จะไม่ช่วยคุณหากคุณกำลังฝึกซ้อม TDD (Test Driven Development) แต่การทดสอบ วิธีการใดจะ


0

เส้นทางนี้ฉันใช้เป็นที่ที่ฉันสร้างฟังก์ชั่นนอกห้องเรียนและกำหนดฟังก์ชั่นให้กับวิธีการส่วนตัวของฉัน

export class MyClass {
  private _myPrivateFunction = someFunctionThatCanBeTested;
}

function someFunctionThatCanBeTested() {
  //This Is Testable
}

ตอนนี้ฉันไม่ทราบว่ากฎ OOP ประเภทใดที่ฉันทำลาย แต่เพื่อตอบคำถามนี่คือวิธีที่ฉันทดสอบวิธีส่วนตัว ฉันยินดีต้อนรับทุกคนให้คำแนะนำเกี่ยวกับข้อดีข้อเสียของเรื่องนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.