最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Chai-spies: expect().to.have.been.called() is failing on local methods - Stack Overflow

programmeradmin0浏览0评论

I'm trying to test some code written in Typescript and my test cases are failing. I've simplified the code a bit to break down my issue. I'm new to ts/js so most likely I'm just missing something obvious..

I don't have any classes defined in the traditional sense where you can create a new object. I just have two typescript files, which have functions defined. For some reason my test fails when the function I'm expecting to be called is local to the function I'm calling.

Below are there two differing examples and their results.

My caller function callFoo() is in my moduleUnderTest file. When callFoo() calls foo() in another file, my test case will pass.

moduleUnderTest file (where foo() es from the helper file)

import { foo } from "path/to/helper/methods";

export function callFoo() {
    foo();
}

helper method

export function foo() {
    console.log("Foo was called");
}

and here is my test code

import { callFoo } from "path/to/module/under/test";
import * as helperMethods from "path/to/helper/methods";

chai.use(spies);
const expect = chai.expect;

suite("Testing Foo", () => {

    test("foo", async () => {
        const spy = chai.spy.on(helperMethods, "foo");
        await callFoo();
        expect(spy).to.have.been.called();
    });

});

Output (success)

However if I move foo() into the same file moduleUnderTest and run the same exact test it fails.

export function callFoo() {
    foo();
}

export function foo() {
    console.log("local foo was called");
}

Test class

import { callFoo } from "path/to/module/under/test";
import *  as moduleUnderTest from "path/to/module/under/test";

chai.use(spies);
const expect = chai.expect;

suite("Testing Foo", () => {

    test("foo", async () => {
        const spy = chai.spy.on(moduleUnderTest, "foo");
        await callFoo();
        expect(spy).to.have.been.called();
    });

});

Output (failure)

So you can see by the output the method was called

local foo was called

but the expect().to.have.been.called() is failing. Why is that? What am I missing?

I'm trying to test some code written in Typescript and my test cases are failing. I've simplified the code a bit to break down my issue. I'm new to ts/js so most likely I'm just missing something obvious..

I don't have any classes defined in the traditional sense where you can create a new object. I just have two typescript files, which have functions defined. For some reason my test fails when the function I'm expecting to be called is local to the function I'm calling.

Below are there two differing examples and their results.

My caller function callFoo() is in my moduleUnderTest file. When callFoo() calls foo() in another file, my test case will pass.

moduleUnderTest file (where foo() es from the helper file)

import { foo } from "path/to/helper/methods";

export function callFoo() {
    foo();
}

helper method

export function foo() {
    console.log("Foo was called");
}

and here is my test code

import { callFoo } from "path/to/module/under/test";
import * as helperMethods from "path/to/helper/methods";

chai.use(spies);
const expect = chai.expect;

suite("Testing Foo", () => {

    test("foo", async () => {
        const spy = chai.spy.on(helperMethods, "foo");
        await callFoo();
        expect(spy).to.have.been.called();
    });

});

Output (success)

However if I move foo() into the same file moduleUnderTest and run the same exact test it fails.

export function callFoo() {
    foo();
}

export function foo() {
    console.log("local foo was called");
}

Test class

import { callFoo } from "path/to/module/under/test";
import *  as moduleUnderTest from "path/to/module/under/test";

chai.use(spies);
const expect = chai.expect;

suite("Testing Foo", () => {

    test("foo", async () => {
        const spy = chai.spy.on(moduleUnderTest, "foo");
        await callFoo();
        expect(spy).to.have.been.called();
    });

});

Output (failure)

So you can see by the output the method was called

local foo was called

but the expect().to.have.been.called() is failing. Why is that? What am I missing?

Share Improve this question asked Apr 6, 2020 at 0:25 mdo123mdo123 1,8974 gold badges19 silver badges38 bronze badges 7
  • First of all, it's perfectly fine and in fact desirable to keep your code simple and not use classes when simple functions will do. I expect the test framework doesn't realize it's the same function because it's a reference to a different object, the module namespace but that's just a guess. Regardless, I don't think you should write tests like that because it tests the implementation of the function which is pointless. You should be testing the interface of the function not how it gets its work done so you shouldn't care whether the helper was called. – Aluan Haddad Commented Apr 6, 2020 at 0:43
  • thanks, I'm new to unit testing so you're probably on to something in that I'm not testing it right. I thought the purpose of a unit test was to test it did what it's supposed to do? In this case I want to test the method callFoo() so wouldn't I want to validate it called the foo() function? Or what would I want to test instead, the output of the foo() function? – mdo123 Commented Apr 6, 2020 at 0:47
  • You're right, you want to test what the function does but that usually means testing that it Returns the correct value or produces the correct side effect, not how that happens. Looking at your test a little bit more, I think the conceptual issue here is that you're thinking of the module namespace object that you imported as the object under test and that's not correct. The function is the system under test. Consider that there are arbitrary numbers of ways that you could Alias and import and that function as well as multiple modules that could export the same function – Aluan Haddad Commented Apr 6, 2020 at 0:54
  • If you are new to unit testing start with a simple testing framework like tape. You can always start using more robust or sophisticated Frameworks when you need to. – Aluan Haddad Commented Apr 6, 2020 at 0:57
  • 1 I think this article, while a few years old, is still very much on point in terms of testing JavaScript. I'm not as dogmatic as the author perhaps , but its definitely a good place to start medium./javascript-scene/… – Aluan Haddad Commented Apr 6, 2020 at 1:07
 |  Show 2 more ments

1 Answer 1

Reset to default 2

I came across an article which described my same issue.

Here is the article https://medium./@DavideRama/mock-spy-exported-functions-within-a-single-module-in-jest-cdf2b61af642

This issue has to do with the way the code is piled. If you're not using typescript and just using javascript I highly remend reading the referenced article.

tdlr (solution for typescript);

  1. Move the helper function (i.e. foo()) into its own module file

OR

  1. Use an anonymous function:

From https://github./facebook/jest/issues/936#issuement-532538300

Not working:

export function doSomething(a, b) {}

Working:

export const doSomething = function (a, b) {}

I should also note I'm using mocha and the GitHub issue I referenced provides some other solutions if using jest that may be more suitable. In my case I chose to switch from a named function to an anonymous function.

发布评论

评论列表(0)

  1. 暂无评论