Test static methods with Mocha - javascript

We have a javascript class with some static methods.
export default class TestUtils {
static test1() {
return 42;
}
}
I'm trying to write tests with mocha, but I stucked. My test looks like here:
const TestUtils = require("path to TestUtils");
describe("TestUtils", function () {
it("test1 returns 42", function () {
expect(TestUtils.test1()).is.equal(42);
});
});
But by running this I get an error:
TypeError: TestUtils.test1 is not a function
Do you have an idea what am I doing wrong?

Maybe it's kind obvious, but I hope it will help someone else.
The solution was to use following import
const {
default: TestUtils,
} = require("path to TestUtils");

Related

TypeError: _API.default is not a constructor with Jest tests

I have an API class that I am trying to use in a React app.
// API file
class API {
...
}
export default API;
// Other file
import API from "utils/API";
const api = new API();
And I am getting the error:
TypeError: _API.default is not a constructor
But.. it seems like my default is set?
My Jest setup is like this:
"jest": {
"setupFiles": [
"./jestSetupFile.js"
],
"testEnvironment": "jsdom",
"preset": "jest-expo",
"transformIgnorePatterns": [
"node_modules/(?!((jest-)?react-native|#react-native(-community)?)|expo(nent)?|#expo(nent)?/.*|#expo-google-fonts/.*|react-navigation|#react-navigation/.*|#unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|react-router-native/.*|#invertase/react-native-apple-authentication/.*)"
]
},
My strong guess is that this is due to a configuration of my babel, webpack or package.json.
What could be causing this?
Note, I want to be clear, this doesn't happen whatsoever in my main application, only in Jest testing
If I change it to a named export/import, I get this:
TypeError: _API.API is not a constructor
Extremely confusing behavior.
As mentioned by others, it would be helpful to see a minimum reproducible example.
However, there is one other possible cause. Are you mocking the API class in your test file at all? This problem can sometimes happen if a class is mistakenly mocked as an "object" as opposed to a function. An object cannot be instantiated with a "new" operator.
For example, say we have a class file utils/API like so:
class API {
someMethod() {
// Does stuff
}
}
export default API;
The following is an "incorrect" way to mock this class and will throw a TypeError... is not a constructor error if the class is instantiated after the mock has been created.
import API from 'utils/API';
jest.mock('utils/API', () => {
// Returns an object
return {
someMethod: () => {}
};
})
// This will throw the error
const api = new API();
The following will mock the class as a function and will accept the new operator and will not throw the error.
import API from 'utils/API';
jest.mock('utils/API', () => {
// Returns a function
return jest.fn().mockImplementation(() => ({
someMethod: () => {}
}));
})
// This will not throw an error anymore
const api = new API();
Trying adding "esModuleInterop": true, in your tsconfig.json. BY default esModuleInterop is set to false or is not set. B setting esModuleInterop to true changes the behavior of the compiler and fixes some ES6 syntax errors.
Refer the documentation here.
This was ultimately due to additional code inside the file that I was exporting the class from.
import { store } from "root/App";
if (typeof store !== "undefined") {
let storeState = store.getState();
let profile = storeState.profile;
}
At the top, outside my class for some functionality I had been working on.
This caused the class default export to fail, but only in Jest, not in my actual application.
You'll need to export it like this :
export default class API
You could try with:
utils/API.js
export default class API {
...
}
test.js
import API from "utils/API";
const api = new API();
I'm adding this because the issue I had presented the same but has a slightly different setup.
I'm not exporting the class with default, i.e.
MyClass.ts
// with default
export default MyClass {
public myMethod()
{
return 'result';
}
}
// without default, which i'm doing in some instances.
export MyClass {
public myMethod()
{
return 'result';
}
}
When you don't have the default, the import syntax changes.
In a (jest) test if you follow the convention where you do have export default MyClass(){};
then the following works.
const MOCKED_METHOD_RESULT = 'test-result'
jest.mock("MyClass.ts", () => {
// will work and let you check for constructor calls:
return jest.fn().mockImplementation(function () {
return {
myMethod: () => {
return MOCKED_METHOD_RESULT;
},
};
});
});
However, if you don't have the default and or are trying to mock other classes etc. then you need to do the following.
Note, that the {get MyClass(){}} is the critical part, i believe you can swap out the jest.fn().mockImplementation() in favour of jest.fn(()=>{})
jest.mock("MyClass.ts", () => ({
get MyClass() {
return jest.fn().mockImplementation(function () {
return {
myMethod: () => {
return MOCKED_METHOD_RESULT;
},
};
});
},
}));
So the issue is the way in which you access the contents of the class your mocking. And the get part allows you to properly define class exports.
I resolved this error by using below code.
jest.mock('YOUR_API_PATH', () => ({
__esModule: true,
default: // REPLICATE YOUR API CONSTRUCTOR BEHAVIOUR HERE BY ADDING CLASS
})
If you want to mock complete API class, please check the below snippet.
jest.mock('YOUR_API_PATH', () => ({
__esModule: true,
default: class {
constructor(args) {
this.var1 = args.var1
}
someMethod: jest.fn(() => Promise.resolve())
},
}));

How to spyOn an exported standalone function using javascript jest?

It is a very simple scenario but I've struggled to find an answer for it.
helpers.ts:
export function foo() {
bar();
}
export function bar() {
// do something
}
helpers.spec.ts:
import { foo, bar } from "./helpers";
describe("tests", () => {
it("example test", () => {
const barSpy = // how can i set this up?
foo();
expect(barSpy).toHaveBeenCalled();
});
});
I can't do const spy = jest.spyOn(baz, 'bar'); because I don't have a module/class to put in place of "baz". It is just an exported function.
Edit:
Jest mock inner function has been suggested as a duplicate but unfortunately it doesn't help with my scenario.
Solutions in that question:
Move to separate module: I cannot do this for my scenario. If I am testing every function in my application, this would result in me creating 10s of new files which is not ideal. (To clarify, I think this solution would work but I cannot use it for my scenario. I am already mocking a separate file function successfully in this test file.)
Import the module into itself:
helpers.spec.ts:
import * as helpers from "./helpers";
describe("tests", () => {
it("example test", () => {
const barSpy = jest.spyOn(helpers, 'bar');
foo();
expect(barSpy).toHaveBeenCalled();
});
});
results in:
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
This is the closed solution:
export function bar() {
// do something
}
export function foo() {
exports.bar(); // <-- have to change to exports.bar() instead of bar()
// or this.bar(); would also work.
}
import * as utils from './utils';
describe('tests', () => {
it('example test', () => {
const barSpy = jest.spyOn(utils, 'bar');
utils.foo();
expect(barSpy).toHaveBeenCalled();
});
});
Or take a look this duplicated question

Mocking a function jest but jest calling original function

I have a function that returns true or false, lets call it myFunc
myFunc (){
if(something){return true}
else{return false}
}
that's what it does for sake of arg
I then call it somewhere else
if(myFunc()){
}else{
}
when I log it out, it continually comes out as false. however, when i have mocked it in my test like so:
const myMock = (myModule.myFunc = jest.fn())
myMock.mockReturnValue(true)
so why is it still coming back as false when I log it from the index file? or is that not quite how mocking works?
I'm guessing that myModule is the object you imported, and then you set the mock function on that object. But in the myModule file you are referencing that function directly, not through a module reference, right?
The proper way would probably be to move myFunc out of myModule. But if you want to keep it there, then you are going to have to partially mock myModule:
jest.mock('./myModule', () => {
return {
...jest.requireActual('./myModule'),
myFunc: jest.fn()
}
})
But seriously consider moving myFunc out of myModule, because partial mocking is difficult and confusing.
One way I found to solve my issue was to use a class instead.
Here is a sudo example:
Implementation
export class Location {
getLocation() {
const environment = this.getEnvironmentVariable();
return environment === "1" ? "USA" : "GLOBAL";
}
getEnvironmentVariable() {
return process.env.REACT_APP_LOCATION;
}
}
Test
import { Location } from "./config";
test('location', () => {
const config = new Location();
jest.spyOn(config, "getEnvironmentVariable").mockReturnValue("1")
const location = config.getLocation();
expect(location).toBe("USA");
});

ES6 import nested function - mocha

I am using ES6, and I want to start testing using mocha & chai.
My current test file code is :
const assert = require('chai').assert;
var app = require('../../../../src/app/login/loginController').default;
describe('login Controller tests', function(){
it('no idea ', function(){
let result = app();
assert.equal(result, 'hello');
})
})
and my loginController.js is :
class LoginController {
checkout(){
return 'hello';
}
}
export default LoginController
I want to import the 'checkout' function into a variable inside my test file, but so far I am able to import only the class.
Will appreciate any help, thanks !
You cannot import methods directly from classes. If you want to import a function without a class as intermediary, then you need to define the function outside the class. Or if you really meant checkout to be an instance method, then you need to call it on an instance.
Here's an example file derived from yours:
export class LoginController {
// Satic function
static moo() {
return "I'm mooing";
}
// Instance method
checkout() {
return "hello";
}
}
// A standalone function.
export function something() {
return "This is something!";
}
And a test file that exercises all functions, adapted from the file you show in your question:
const assert = require('chai').assert;
// Short of using something to preprocess import statements during
// testing... use destructuring.
const { LoginController, something } = require('./loginController');
describe('login Controller tests', function(){
it('checkout', function(){
// It not make sense to call it without ``new``.
let result = new LoginController();
// You get an instance method from an instance.
assert.equal(result.checkout(), 'hello');
});
it('moo', function(){
// You get the static function from the class.
assert.equal(LoginController.moo(), 'I\'m mooing');
});
it('something', function(){
// Something is exported directly by the module
assert.equal(something(), 'This is something!');
});
});

Mocking named imports and constructors ES6 & Ava

I have a class constructor, with a function I want to stub:
class Service {
constructor(){}
async someFunction() {
try {
// does stuff
}
catch (e) {}
}
}
In the file I want to test, this is imported an used like so:
const { Service } = require('something')
const newService = new Service('xyz')
I'm struggling to get this to import & stub correctly in my tests.
Currently am importing like this:
t.context.service = {
Service: class Service {
constructor () {
this.someFunction = sinon.stub()
}
}
}
This import seems to work, but then I can't get a reference back to it through the constructed version. Any help on this one?
I want to be able to make an assertion like:
t.true(t.context.service.Service.someFunction.calledOnce)
AVA doesn't provide any stubbing. Have a look at https://github.com/testdouble/testdouble.js/ or http://sinonjs.org/.

Categories

Resources