javascript - How to use a spy with toHaveBeenCalledWith and a mutable array in Jasmine.js? - Stack Overflow

admin2025-04-19  0

I have some code which calls a callback function with an array as single argument. After calling the callback, the code changes the array contents. It is similar to this code:

function myCode( callback ) {
    var someArray = [ 1, 2, 3, 4 ];
    callback( someArray );

    // change someArray in arbitrary ways
    someArray.splice( 2 );
    ...
}

Now, I want to verify that the callback is called with the correct array content. Using Jasmine.js I would write my spec like this:

describe( "My code", function() {
    var callback;

    beforeEach( function() {
        callback = jasmine.createSpy( "My callback" );
        myCode( callback );
    });

    it( "calls the callback and passes the correct array", function() {
        expect( callback ).toHaveBeenCalledWith( [ 1, 2, 3, 4 ] );
    });
});

This fails. The problem is, that Jasmine.js records the array but does not make a copy of it. Since the array is changed after the call, the expect()-Line fails, even though the actual call fulfilled the expectation. The same problem occurs with mutable objects.

How can I test such code?

I have some code which calls a callback function with an array as single argument. After calling the callback, the code changes the array contents. It is similar to this code:

function myCode( callback ) {
    var someArray = [ 1, 2, 3, 4 ];
    callback( someArray );

    // change someArray in arbitrary ways
    someArray.splice( 2 );
    ...
}

Now, I want to verify that the callback is called with the correct array content. Using Jasmine.js I would write my spec like this:

describe( "My code", function() {
    var callback;

    beforeEach( function() {
        callback = jasmine.createSpy( "My callback" );
        myCode( callback );
    });

    it( "calls the callback and passes the correct array", function() {
        expect( callback ).toHaveBeenCalledWith( [ 1, 2, 3, 4 ] );
    });
});

This fails. The problem is, that Jasmine.js records the array but does not make a copy of it. Since the array is changed after the call, the expect()-Line fails, even though the actual call fulfilled the expectation. The same problem occurs with mutable objects.

How can I test such code?

Share Improve this question asked Oct 18, 2012 at 22:50 h2steinh2stein 6164 silver badges14 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

Jasmine does shallow copy the arguments passed on to the spy, which mean the arguments object will have reference to the same objects with which it has been called (in your case arguments will have the reference to someArray object only.

Jasmine code reference for spy implementation:

  spy = function() {
    callTracker.track({
      object: this,
      args: Array.prototype.slice.apply(arguments)
    });
    return spyStrategy.exec.apply(this, arguments);
  };

Deep copy of array (arguments) is not possible as a generic solution, problems are explained at link.

A solution to your problem could be writing your own callback and matcher for your specific use case, where you know what you have to copy (clone) exactly.

Sometimes if your unit tests are hard to get right, it's telling you the code needs to be more unit-y, and there's probably some method that has too many responsibilities. Looks like myCode is doing a couple different things: applying callback to some array, and then modifying that array. Perhaps the code that modifies the array can be extracted into its own method which myCode calls. Then you can simply stub out that extra method in the unit test which ensures that the callback is being called with the right arguments.

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745078171a283692.html

最新回复(0)