Jasmine.js: Race conditions when using "runs"

I noticed strange behavior when testing my jasmine code. One test fails when run along with other tests in my specification. When one is called, the test passes.

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

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

When you run the test, another test in the same specification will set its own spy for the B.Create method. Therefore, I assume that this will create something like a race condition.

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

So, it all comes down to the following question:

  • Am I dating a racial state?
  • How to prevent this problem? Mock time (jasmine.Clock.useMock) is not a real solution, because I am mocking the loadData method with a fake promise.

Update-1 : Richard Dingall states in his article Parallel vs serial javascript async tests that Jasmine runs tests in parallel, and is this the root of my problem?

Update-2 This is a 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 I found evidence that I was facing a racial condition. The next test passes. So my test is affected by other current tests.

 it( 'its a trape', function () { waitsFor( function () { return ( crmRestKitCreateSpy.callCount > 0 ); }, 4000 ); runs( function () { expect( crmRestKitCreateSpy.callCount ).toBeGreaterThan( 0 ); } ); } ); 
+3
source share
1 answer

Well, it looks like I ran into a race condition: CreateSpy is used by several tests when using "runs" (see Update-3 in the question).

In the end, it turns out that Clock-Mock from 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' ); } ); 
0
source

Source: https://habr.com/ru/post/974777/


All Articles