在使用 jasmine 测试我的代码时,我注意到了一个奇怪的行为。当与我的规范中的其他测试一起执行时,一个测试失败。当单独调用时,测试通过。

该测试断言脚本 A.js 依赖于提供“创建”方法的脚本 B.js。我在测试中为“创建”创建了一个 spy 并调用脚本 A.js (A.init),它将加载一些数据(loadData 再次返回一个 promise ),然后调用“创建”方法 5 次(一旦 loadData - promise 已解决)。 A.init() 返回另一个 promise !

当我使用 Jasmine 的“runs”方法并等待 promise-init 被解析时,我喜欢断言 B.Create 被调用了 5 次。

在执行测试时,同一规范中的其他一些测试将为 B.Create 方法设置自己的 spy 。所以我认为这会在某种程度上造成竞争条件。

注释:每个测试为创建方法( var createSpy = spyOn(B, "Create" )创建自己的 spy ;

所以一切都归结为以下问题:

  • 我是否面临竞争条件?
  • 如何防止这个问题?时间模拟(jasmine.Clock.useMock)不是一个真正的解决方案,因为我用假 promise 模拟了“loadData”方法。

  • 更新-1 :
    Richard Dingwall 在他的文章 Parallel vs serial javascript async tests 中概述了 Jasmine 并行执行测试,这是我问题的根源吗?

    更新 2
    这是失败的测试:
    /* more code */
    
    crmRestKitCreateSpy = spyOn( CrmRestKit, 'Create' )
       .andCallFake( function ( entitySchemaName, obj ) {
    
           return {
               then: function ( callback ) {
                   // fake a create response where the id attribute is populated
                   callback( _.extend( {}, obj, { AccountId: cloneId } ) );
               }
           };
       } );
    
    /* more code */
    
    it( 'links all child-clones to the new parent-clone', function () {
    
    // arrange - inject spy
    spyOn( CrmRestKit, 'ByQueryAll' ).andReturn(
            alfa.fake.promise.buildFakeResolvePromise( { d: fakeChildAcccounts, __next: false }, 750 )
    );
    
    // arrange
    includedOneToManyRel = [accountToAccountRel];
    
    // action
    var promise = alfa.util.clonemachine.deepClone( fakeId, includedOneToManyRel );
    
    waitsFor( function () {
        return promise.state() === 'resolved';
    }, 800 );
    
    
    runs( function () {
    
        expect( crmRestKitCreateSpy.callCount ).toBe( 5 );
    
        // assert - all child-clones reference the new parent-clone
        expect( crmRestKitCreateSpy.calls[1].args[1].ParentAccountId.Id ).toBe( cloneId );
        expect( crmRestKitCreateSpy.calls[2].args[1].ParentAccountId.Id ).toBe( cloneId );
        expect( crmRestKitCreateSpy.calls[3].args[1].ParentAccountId.Id ).toBe( cloneId );
        expect( crmRestKitCreateSpy.calls[4].args[1].ParentAccountId.Id ).toBe( cloneId );
    } );
    

    });

    更新 3
    我认为 a 找到了我面临竞争条件的证据。以下测试通过。所以我的测试受到其他正在运行的测试的影响。
    it( 'its a trape', function () {
    
                waitsFor( function () {
    
                    return ( crmRestKitCreateSpy.callCount > 0 );
                }, 4000 );
    
                runs( function () {
    
                    expect( crmRestKitCreateSpy.callCount ).toBeGreaterThan( 0 );
                } );
            } );
    

    最佳答案

    好的,似乎我正面临竞争条件:使用“运行”时,多个测试使用 createSpy(请参阅问题中的 Update-3)。

    事实证明, Jasmine 的时钟模拟解决了我的问题:

    beforeEach( function () {
    
                // mock the ByQuery method
                crmRestKitByQuerySpy = spyOn( CrmRestKit, 'ByQuery' )
                    .andCallFake( alfa.fake.promise.buildFakeResolvePromise( fakeChildAcccounts ) );
    
                // The Jasmine Mock Clock is available for a test suites that need the
                // ability to use setTimeout or setInterval callbacks. It makes the
                // timer callbacks synchronous, thus making them easier to test.
                jasmine.Clock.useMock();
            } );
    
    it( 'it supports async execution - jasmine-timer-mock', function () {
    
                var deferedInMillisecond = 250;
    
                // arrange - inject the ByQueryAll method of the CrmRestKit
                spyOn( CrmRestKit, 'ByQueryAll' ).andReturn( alfa.fake.promise.buildFakeResolvePromise( {
                    d: fakeChildAcccounts,
                    __next: false
                }, deferedInMillisecond ) );
    
                // arrange
                includedOneToManyRel = [accountToAccountRel];
    
                // action
                var deepClonePromise = alfa.util.clonemachine.deepClone( fakeId, includedOneToManyRel );
    
                expect( deepClonePromise.state() === 'pending' );
    
                jasmine.Clock.tick( deferedInMillisecond + 1 );
    
                // assert - defere the assertion until the waitFor is completed
                expect( deepClonePromise.state() === 'resolved' );
            } );
    

    10-08 18:27