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

时间:2017-09-02 19:38:54

标签: javascript html angularjs typescript jasmine

  • 编写了跳转方法的测试用例,
  • 但当我看到代码覆盖率报告时,它不会进入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();
            });
    });
});

1 个答案:

答案 0 :(得分:7)

请记住,单元测试的关键是编写可测试的小代码单元。Unit Testing - Wikipedia

在调用“跳跃”之前,你们大部分时间都在正确的轨道上,在文件阅读器之前存根等等。功能。这是测试依赖于另一个外部库/函数/框架的代码的方法。单元测试维基百科页面的相关部分说明

  

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

在这里,但是,当你创建虚拟FileReader或模拟它时,它永远不会调用&#39; onloadend&#39;因为模拟/存根没有实现该事件和事件系统。这意味着你的模拟不完整。维基百科声明

  

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

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

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

  

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

跳跃&#39;函数依赖于附加到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;
    }
}

一个测试示例,它将涵盖“已加载”的所有代码行。上面写的函数如下:

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);
});

});

现在请注意,在现实世界中,您不应该仅针对实际未测试给定功能的代码覆盖率编写测试。这将导致您产生错误的安全感,而不是实际 TEST 您的代码。 MSDN可以这样说:

  

单元测试的主要目标是在应用程序中使用最小的可测试软件,将其与代码的其余部分隔离开来,并确定它是否完全符合您的预期。

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

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

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

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

通过尝试在没有实际测试功能的情况下实现100%的代码覆盖率,您正在做的事情是使汽车崩溃,然后不进行任何验证。

你说'#34;我撞坏了汽车,我真的不需要检查它是否做了碰撞时应该做的事,只要我把它撞坏了?&#34;

当然,通过观察汽车可以获得100%的碰撞覆盖率,但是如果不进行实际测试,你甚至可能不会感到困扰。代码覆盖率是用于发现未经过测试的代码的有用工具,它不用于实现获得完整代码覆盖率的任意度量。有关这方面的进一步阅读和优秀的写作可以在Broken promise of 100% code coverage

阅读

基本上它的关键是

  

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

我已经省略了中篇文章中的代码,但是它继续陈述:

  

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

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

  

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

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

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