Jasmine.js: Race Conditions when using “runs”

血红的双手。 提交于 2019-12-10 22:30:29

问题


I have noticed a strange behaviour when testing my code with jasmine. One test fails when executed together with the other tests in my spec. When called alone the test passes.

The test asserts the script A.js that depends on script B.js which offers the method "Create". I create inside the test a spy for the "Create" and invoke script A.js (A.init) that will load some data (loadData with returns again a promise) and then call the "Create" method 5 times (once the loadData-promise is resolved). The A.init() returns another promise!

When I use the "runs" method of Jasmine and wait until the promise-init is resolved, I like to assert that B.Create was called 5 times.

When executing the test some other test in the same spec will set up its own spy for the B.Create method. So I assume this will create some-how a race-condition.

Annotation: Each test creates its own spy for the create-method (var createSpy = spyOn(B, "Create");

So all boils down to the following question:

  • Do I face a race-condition?
  • How do I prevent this problem? The time mock (jasmine.Clock.useMock) is not a real solution because I mock the "loadData" method with an promise-fake.

Update-1: Richard Dingwall outlines in his article Parallel vs serial javascript async tests that Jasmine executes the tests in parallel, so is this the root of my problem?

Update-2 This is the test that fails:

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

} );

Update-3 I think a found the proof that I am facing a race-condition. The following test passes. So my test is affected by other running tests.

it( 'its a trape', function () {

            waitsFor( function () {

                return ( crmRestKitCreateSpy.callCount > 0 );
            }, 4000 );

            runs( function () {

                expect( crmRestKitCreateSpy.callCount ).toBeGreaterThan( 0 );
            } );
        } );

回答1:


Ok, seems like I am facing a race-condition: The createSpy is used by multiple tests when using "runs" (see Update-3 in the question).

In turns out that the Clock-Mock of Jasmine solved my problem:

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


来源:https://stackoverflow.com/questions/17154478/jasmine-js-race-conditions-when-using-runs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!