简体   繁体   中英

How to make sure that a method is called only once given specific parameters

i have a class called Feature and it contains the following methods setUser(boolean),execute(), doExecute() And according to the below stated parameters, when i call execute() method, doExecute() method should be called only once.

I tried to test that doExecute() method is called only once in the below code using sinon, but I receive an error message says: doExecute() method is called zero times.

please let me know how to check correctly if doExecute() is called exactly once

code :

t.context.clock = sinon.useFakeTimers();
const domain = 'testDomain';
const delayInMillis = 0;
const delayInSecs = delayInMillis / 1000;
const feature = new Feature(domain, delayInMillis);
feature.setUser(false);

const p = feature.execute()
 .then(() => sinon.spy(feature.doExecute()))
 .then(() => t.pass());

sinon.assert.callCount(sinon.spy(feature.doExecute()),1);
t.context.clock.restore();

return p;
});

First of all, sinon.spy() accepts a function as a parameter, and you're passing the result of your function.

// wrong
sinon.spy(feature.doExecute());

// correct
sinon.spy(feature.doExecute);

Next, you need to store your spy into a variable for later reference. In your code you're just creating new spies every time.

const featureDoExecuteSpy = sinon.spy(feature.doExecute);

// some code

sinon.assert.callCount(featureDoExecuteSpy, 1);

There are quite a few problems with this.

As sameeh pointed out, you're simply creating a new spy as part of your assertion. Obviously a new spy will have a call count of zero. You need to make sure the assertion is referring to the same spy instance as will be called by the code under test.

A crucial thing that sameeh missed, however, is that the spy has to be passed through to your code under test. This is why the usual form of `sinon.spy()' accepts an object followed by a method name. It replaces the method on that object with a spy that wraps the original:

// wrong
sinon.spy(feature.doExecute());

// still wrong
sinon.spy(feature.doExecute);

// correct
sinon.spy(feature, 'doExecute');

You can then access the spy in place on that object for your assertion. You don't need to store it in a local variable:

sinon.assert.callCount(feature.doExecute, 1);

Another problem: Your assertion is not waiting for the promise from feature.execute to resolve. This means that if doExecute is being invoked after some asynchronous operation within execute , your assertion is happening too early. It thus needs to be in a then after the others like so (note that this code still won't work due to other problems I'll address in a bit):

const p = feature.execute()
 .then(() => sinon.spy(feature, 'doExecute'))
 .then(() => t.pass())
 .then(() => sinon.assert.callCount(feature.doExecute,1));

More problems... Your use of fake timers is very strange, and makes it difficult for me to tell whether or not the promise returned by feature.execute() will ever resolve.

If it needs the timer to tick in order to resolve? It won't. Because you aren't ever ticking the timer. I don't know what t.pass() does, but since it is chained onto the promise returned by feature.execute() , it will never get called if you don't tick the timer somewhere else. Nor will your code that creates your spy, for that matter-- for the same reason.

You need to create your spy before invoking feature.execute() , and probably invoke t.pass() after , if it is indeed the method that ticks your fake timer:

sinon.spy(feature, 'doExecute')

const p = feature.execute()
 .then(() => sinon.assert.callCount(feature.doExecute,1));

t.pass();

Finally, I don't know what test framework you're using, but you generally want to restore fake timers and other global state in blocks that will always execute, whether or not your test is successful. This ensures that failed tests don't leave crap hanging around that will carry over into other tests. Mocha has methods beforeEach and afterEach for this purpose:

beforeEach(function() {
    t.context.clock = sinon.useFakeTimers();
});

afterEach(function() {
    t.context.clock.restore()
});

it('whatever your test name is', function() {
    const domain = 'testDomain';
    const delayInMillis = 0;
    const delayInSecs = delayInMillis / 1000;
    const feature = new Feature(domain, delayInMillis);

    sinon.spy(feature, 'doExecute')

    const p = feature.execute()
      .then(() => sinon.assert.callCount(feature.doExecute,1));

    t.pass();

    return p;
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM