SergiuB
SergiuB

Reputation: 126

Can I call the original method inside the supplied function to "andCallFake" of a Jasmine spy?

I could save the original method in a variable in beforeEach, and then restore it in afterEach, but maybe I can use a spy which will be reset automatically between test suites.

spyOn(Ext, "create").andCallFake(function(className){
    if (className === 'Waf.view.Viewport')
        // call the original Ext.create method
});

Is this possible? I am using Jasmine 1.3

Upvotes: 7

Views: 6605

Answers (5)

SquarePeg
SquarePeg

Reputation: 1781

Here is how I achieved it using Jasmine with an Angular Service. The service I am spying on is being called in the constructor of my test service:

// create the TestBed:
TestBed.configureTestingModule({
    providers: [MyInjectedService, ServiceConstructorInjectedService]
});
myInjectedService = TestBed.get(MyInjectedService);
serviceConstructorInjectedService = TestBed.get(ServiceConstructorInjectedService);

it('should...', () => {
    let returnValue = 'return this';
    spyOn(serviceConstructorInjectedService , 'myFunction').and.callFake((param) => {
        if (param === 'testValue') {
            return returnValue;
        } else {
            return ServiceConstructorInjectedService.prototype.myFunction(param);
        }
    });
});

// instantiate the service again so spy is called
myInjectedService = new MyInjectedService(
    TestBed.get(ServiceConstructorInjectedService)
);

Upvotes: 0

Altair7852
Altair7852

Reputation: 1418

This is a hack for Jasmine 2.3. Ideally the fake callback should have access to the reference of the original function to call as needed instead of dancing around like this.

Given that stubbing strategy can be modified on the fly in Jasmine 2.3, the following approach seems to work as well:

var createSpy = spyOn(Ext, "create");
createSpy.and.callFake(function(className){
    if (className === 'Waf.view.Viewport'){
        createSpy.and.callThrough();
        Ext.create(className);        
    }
});

Upvotes: 6

alecmce
alecmce

Reputation: 1456

You can bind the original method into the fake:

var obj = {
  method: function(name) { return name + '!'; }
}

var methodFake = function(original, name) {
  return 'faked ' + original(name);
}.bind(obj, obj.method)
spyOn(obj, 'method').andCallFake(methodFake);

obj.method('hello') // outputs 'faked hello!'

For what it's worth, I don't think it's great practice to do this, but the need came up for me recently when I was testing some d3 code. Hope it helps.

Upvotes: 8

user2943490
user2943490

Reputation: 6940

Different scenarios should be tested independently of each other. Try structuring your tests to something like this.

beforeEach(function () {
    spyOn(Ext, 'create');
});

describe('scenario 1', function () {
    beforeEach(function () {
        Ext.create.andCallThrough();
    });

    it('should do something', function () {
        // do your assertions
    });
});

describe('scenario 2', function () {
    beforeEach(function () {
        Ext.create.andCallFake(function () {
            // faked function
        });
        // or if you're always returning a constant value, use andReturn
        // Ext.create.andReturn({});
    });

    it('should do something', function () {
        // do your assertions
    });
});

Upvotes: -1

SergiuB
SergiuB

Reputation: 126

I ended up doing something like this:

var origFunc = Ext.create;
spyOn(Ext, "create").andCallFake(function(className, classConfig){
    if (className === 'Waf.view.Viewport') {
         return {};
    } else {
         return origFunc.apply(null, arguments);
    }
});

Upvotes: -1

Related Questions