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