试图为每一行编写测试用例

4 html javascript jasmine angularjs typescript

  • 有写跳测法的测试用例,
  • 但当我看到代码覆盖率报告时,它不会进入onloadend方法seat.onloadend.
    • 在createSpyObj中,我调用了loadend,但仍然没有进入内部
  • 你能告诉我如何解决它吗?
  • 在下面提供我的代码和测试用例.
  • 我试图为每一行提供测试用例.
  jumping(inputValue: any): void {
    var that = this;
    var file: File = inputValue.files[0];

    var seat: FileReader = new FileReader();
    seat.onloadend = (e) => {
      this.encodeBase64 = seat.result;
      that.fileSelect = $("#laptop").val().replace(/^.*\\/, "");
      if (that.fileSelect == '') {
        that.dragDrop = that.swimming;
      } else {
        that.dragDrop = "";
        that.dragDrop = that.fileSelect;
      }
    }
    $('.running').show();
    if (inputValue.files.length > 0) {
      var wholeQuantity = 0;

      wholeQuantity = inputValue.files[0].size / 1048576; //size in mb 

      if (wholeQuantity > 5) {
        $('.stars').show();
        $("#laptop").val('');
        this.fileSelect = "";
      }

      seat.readAsDataURL(file);
    }
  }






describe('Jasmine Unit Tests: hand-Basketball-Manage-mobiles', () => {
    let rainSPORTSService:SPORTSService;
    let SPORTSService: SPORTSService;
    let decodeService: DecodeService;
    let BasketballChainComponent: handBasketballChain;
    let kickViewrainsComponent: kickViewrains;
    let tiger: Componenttiger<handBasketballChain>;
    let raintiger: Componenttiger<kickViewrains>;
    let foodktiger: Componenttiger<foodkCarousel>;
    let kendotiger: Componenttiger<KendoGridComponent>;
    let foodkComponent:foodkCarousel;
    let kendoComponent:KendoGridComponent;
    beforeEach(async(() => {

    jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

    TestBed.configureTestingModule({
      imports: [HttpModule, FormsModule,BrowserModule ],
      declarations:[handBasketballChain, KendoGridComponent,ProgressCircle,
            kickViewrains,handLeftSliderComponent,foodkCarousel,kickmobiles],
      providers:[SPORTSService,DecodeService,recentPinnedHistoryService,
        {provide: Router, useClass: RouterModule}, validationService,saveService,
        ChainService]
     }).compileComponents().then(() =>{
        foodktiger = TestBed.createComponent(foodkCarousel);
        kendotiger = TestBed.createComponent(KendoGridComponent);
        foodkComponent = foodktiger.componentInstance;
        kendoComponent = kendotiger.componentInstance;
        tiger = TestBed.createComponent(handBasketballChain);
        BasketballChainComponent = tiger.componentInstance;
        SPORTSService = tiger.debugElement.injector.get(SPORTSService);
        tiger.componentInstance.kickmobiles.SPORTSService=tiger.debugElement.injector.get(SPORTSService);
        tiger.componentInstance.kickViewrains.SPORTSService=tiger.debugElement.injector.get(SPORTSService);
        decodeService = tiger.debugElement.injector.get(DecodeService);
        BasketballChainComponent.inputfoodkCarousel = foodkComponent; //jasmine.createSpy('foodkCarousel');//.andCallFake(function(msg) { return this });
        BasketballChainComponent.kickmobiles.gridkendo=kendoComponent;    
    })}
    ));



    it('Read kick mobile', (done) => {


        let callFirstTime : boolean = true;
        let url=

        spyOn(BasketballChainComponent.kickmobiles.SPORTSService,'getResponse').and.
            callFake(() => { 
                if(callFirstTime) {
                    callFirstTime = false; // Invoked by detectChanges() 
                    return Observable.of([{
                        "mobileId": "100",
                        "mobileName": "http://localhost:3000/assets/js/actualairings.json",
                        "mobileType": "TITLE",
                        "mobileData": "YWZjYXJlZ2Vyamh2dmFyZWdoYnZi",
                        "notes": "",
                        "notesId": "100",
                        "elfDocID": "100",
                        "url": "http://localhost:3000/upload",
                        "date": "06/27/2017",
                        "addedByName": "Kamal",
                        "userID": "206509786",
                        "operationType": "create"
                    }]);
                }
            });


            const fileReaderSpy = jasmine.createSpyObj('FileReader', ['readAsDataURL', 'onloadend']);
            spyOn(window, 'FileReader').and.returnValue(fileReaderSpy);

            BasketballChainComponent.kickmobiles.jumping({
                files: "Untitled-2.txt"
            });

            var seat = new FileReader();

            //seat.onloadend(e);

            //BasketballChainComponent.kickmobiles.jumping.onloadend()


            tiger.whenStable().then(() => {
                done();
            });
    });
});
Run Code Online (Sandbox Code Playgroud)

Dan*_*ane 7

请记住,单元测试的关键是编写小的可测试代码单元.单元测试 - 维基百科

在调用"跳跃"功能之前,你在大多数情况下都处于正确的轨道上,存在FileReader等等.这是测试依赖于另一个外部库/函数/框架的代码的方法.单元测试维基百科页面的相关部分说明

因为某些类可能引用了其他类,所以测试类可能会经常溢出来测试另一个类.一个常见的例子是依赖于数据库的类:为了测试类,测试人员经常编写与数据库交互的代码.这是一个错误,因为单元测试通常不应超出其自己的类边界,特别是不应跨越这样的进程/网络边界,因为这会给单元测试套件带来不可接受的性能问题.

但是,当您创建虚拟FileReader或模拟它时,它永远不会调用'onloadend',因为模拟/存根没有实现该事件和事件系统.这意味着你的模拟不完整.维基百科说

相反,软件开发人员应该围绕数据库查询创建一个抽象接口,然后使用他们自己的模拟对象实现该接口.通过从代码中抽象出这个必要的附件(暂时减少净有效耦合)

在您的情况下,而不是数据库,你将嘲笑FileReader loadend事件.

从测试的角度来看,您当前的代码需要一个小的重构才能变得可测试.单元测试的一般目标是单独测试小功能单元.

单元测试的目标是隔离一个单元并验证其正确性.

"跳跃"功能依赖于附加到onloadend的嵌套箭头功能.你的代码直接调用了在测试中注释掉的内容,我有点惊讶的是,为了保持代码覆盖率没有用,说实话,并建议确保你的代码覆盖工具,如果你是伊斯坦布尔使用Jasmine配置正确.

抛开上述内容,您应该重构该嵌套函数,然后创建一个命名函数,然后可以直接调用它进行单元测试.

这是一个(我未经测试)最好的方法来实现你的功能.

jumping(inputValue: any): void {
    var that = this;
    var file: File = inputValue.files[0];

    var seat: FileReader = new FileReader();
    // bind the arguments for the event handler, first arg will be 'this' of the
    // loaded named function
    // second is 'that' variable, seat is seat and the final 'e' variable is
    // implicit and shouldn't be specified.
    seat.onloadend = loaded.bind(seat, that, seat); 

    $('.running').show();
    if (inputValue.files.length > 0) {
        var wholeQuantity = 0;

        wholeQuantity = inputValue.files[0].size / 1048576; //size in mb

        if (wholeQuantity > 5) {
            $('.stars').show();
            $("#laptop").val('');
            this.fileSelect = "";
        }

        seat.readAsDataURL(file);
    }
}

loaded(that: any, seat: any, e: any): void { // now a testable named function
    this.encodeBase64 = seat.result;
    that.fileSelect = $("#laptop").val().replace(/^.*\\/, "");
    if (that.fileSelect == '') {
        that.dragDrop = that.swimming;
    } else {
        that.dragDrop = "";
        that.dragDrop = that.fileSelect;
    }
}
Run Code Online (Sandbox Code Playgroud)

如上所述,将涵盖"已加载"函数的所有代码行的测试示例如下:

describe('test suite', function () {
var old$ = $;

afterEach(function () {
    $ = old$;
});

it('covers all lines and else path on if but does not actually test anything', function () {
    $ = function () {
        val: function () {
            return 'Untitled-2.txt';
        }
    }; // stub JQuery

    var seat = {
        result: 'Base64encoded'
    };
    var scope = {};
    var that = {
        swimming: false,
        dragDrop: null
    };

    BasketballChainComponent.kickmobiles.loaded.call(scope, that, seat, null);
});

it('covers all lines and on if but not else and does not actually test anything', function () {
    $ = function () {
        val: function () {
            return '';
        }
    }; // stub JQuery

    var seat = {
        result: 'Base64encoded'
    };

    var scope = {};

    var that = {
        swimming: false,
        dragDrop: null
    };

    BasketballChainComponent.kickmobiles.loaded.call(scope, that, seat, null);
});
Run Code Online (Sandbox Code Playgroud)

});

现在请注意,在现实世界中,您永远不应该仅为没有实际测试给定功能的代码覆盖编写测试.这将导致您进入虚假的安全感,而不是实际测试您的代码.在MSDN有这样一段话:

单元测试的主要目标是在应用程序中使用最小的可测试软件,将其与代码的其余部分隔离,并确定其行为是否与您期望的完全相同.

你正在做的事情的类比如下:

你是一名汽车碰撞测试员.你的工作是验证汽车在碰撞中是否安全.所以一辆汽车以10公里/小时的速度坠毁,你需要检查一下.

您需要确认需要确认的事项清单.因此,在10公里/小时的碰撞中,您只能看到油漆被划伤.所以你看油漆,如果油漆被划伤但没有其他损坏,测试通过.如果汽车凹陷,测试失败.

这是一个很好的测试,因为它正在测试可量化的东西并且它正在测试意图.

你试图在没有实际测试功能的情况下实现100%的代码覆盖率正在做什么,这会使汽车崩溃然后不进行任何验证.

你说的是"好吧,我撞坏了汽车,我真的不需要检查一下,只要我撞坏了它就会发生它应该在碰撞中发生的事情吗?".

当然,通过观察汽车可以获得100%的碰撞覆盖率,但是实际上没有测试它,你甚至可能没有打扰.代码覆盖率是用于发现未经测试的代码的有用工具,它不用于实现获得完整代码覆盖率的任意度量.关于这一点的进一步阅读和优秀的写作可以在100%代码覆盖率的破坏承诺中阅读

基本上它的关键是

虽然很容易衡量代码覆盖率,但这并不是一个好的指标.即使您的代码覆盖率为100%,您也可能遇到麻烦.

我已经省略了媒体文章中的代码,但它继续说明:

此单元测试为elementAtIndex:函数生成完美的100%测试覆盖率.它是否证明该功能正常工作?答案显然是否定的.当我们超出数组边界时会发生什么?为什么会这样?当您尝试关注代码覆盖率指标时,您可以编写代码来查看已测试函数/方法的实现.但实施并未证明是正确的.这就是我们想要测试它的原因.即使使用这种简单的功能代码覆盖率也无法作为衡量单元测试质量的良好指标.

此外,上面我说你应该测试你的代码的意图.中篇文章也说明了这一点.

该怎么做?不要看实际的方法实现,而是看合同.精确查看任何特定输入的函数/方法的输出.查看此功能执行或使用的副作用.考虑可能存在的边缘情况.列出此信息并根据该信息进行测试.

请记住, 100%的代码覆盖率并不意味着您的代码100%正确.

我希望这有助于您将单元测试理解为一个更好的概念.