Mock a function inside a module using Jest - javascript

I'm currently having a problem with mocking a function inside another lib.
In our team, we have a project (npm lib, let's call it component-lib) with some components and functions that are being used by our React main app. We expose the functions and components in a single JS file. (index.js) like this:
import { isFeatureEnabled } from './modules/features'
import mockComponent from './components/mockComponent'
export {
isFeatureEnabled,
mockComponent
}
I'm trying to mock the return value of one of the functions inside this component-lib that is being used by the main react app, my unit test goes like this:
PS: I'm using Jest and React Testing Library
describe('unit test', () => {
jest.mock('component-lib', () => ({
...jest.requireActual('component-lib'),
isFeatureEnabled: jest.fn().mockReturnValue(false)
}))
it('Should send data', () => {
render(<Main />)
[... i do some stuff here and trigger the isFeatureEnabled function ...]
})
})
and the code I use it:
function onSubmitPassword (data) {
console.log('is this feature enabled? ', isFeatureEnabled('feature')) // logs as true
if (isFeatureEnabled('feature') {
...do this // this is executed
} else {
do that
}
}
The thing is that when I do a console.log to see the value of isFeatureEnabled, it's returning true instead of false, ignoring the mock.
Do you guys have any idea why the mock is not working in this case?
I already tried importing the lib as:
import * as components from 'component-lib'
jest.mock('component-lib', () => ({
...jest.requireActual('component-lib'),
isFeatureEnabled: jest.fn()
}))
it('Should send data', () => {
isFeatureEnabled.mockReturnValue(false)
render(<Main />)
[... i do some stuff here and trigger the isFeatureEnabled function ...]
})
and it returns an error saying the function only has a getter

Related

In Jest, how to test a function declared inside a function?

I am trying to test a function that is declared inside another function(Parent). I tried a few things but was not able to test that. I am using react library and JEST testing framework in the project.
Here is the service file (service.ts) function which I am trying to test:
import { useThisHook } from './base-hooks';
export function someFunction(param: string) {
const [
resolveFunction,
,
{ loading, error },
] = useThisHook();
const childFun = (param : number) => {
resolveFunction(someArguments);
}
return{
someValue: childFun
}
}
Here is the spec file code:
import * as SomeHooks from './base-hooks';
import * as Service from './service';
describe('Service function', () => {
beforeEach(() => {
jest.spyOn(SomeHooks, 'useThisHook').mockReturnValue(
{
//Some Value
}
);
});
test('Some function test', () => {
const someFunctionResponse = Service.someFunction('string');
expect(someFunctionResponse).toEqual({
someValue: expect.any(Function),
});
});
});
Till here it is fine. Tests are also getting passed but the problem is that childFn is not getting the coverage and that's what my requirement is. I am new to React and Jest. I am not able to understand how can I achieve that. I tried many things but didn't succeed.
Your childFn is not being called, that's why it's not getting coverage.
You could either refactor the whole childFn to a new hook and test it individually with something like react-hooks-testing-library.
Or you could separate the childFn and declarate it outside the someFunction scope pass the resolveFunction and then test it.
You only get coverage if the code is actually called, not when it is declared.

Nested describe and behaviour of dynamically created "it"s

I have nested describes in my tests and as usual I am using some beforeEach and before in describes. And one of my describe function calls helper function which creates dynamic tests (DRY). And mocha runs description of nested describe before beforeEach method. And my dynamically created it has comp as undefined.
const checkProps = (comp, propName, expectedvalue) => {
it(`${comp} should have ${propName} equal to ${expectedvalue}`, () => {
expect(comp.prop(propName)).to.equal(expectedvalue);
});
};
describe('Component', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<MyComponent />);
});
describe('prop checking', () => {
checkProps(wrapper, 'title', 'SomeTitle');
});
});
What is the best way todo it? Thanks in advance.
What happens
The Mocha Run Cycle runs all describe callback functions first (...which is also true for other testing frameworks such as Jest and Jasmine).
Then it runs the before hooks, then beforeEach hooks, and finally the it callbacks.
So checkProps runs as part of running the initial describe callbacks, and at that point wrapper is undefined, so as you have noticed the test description says undefined should have....
The beforeEach hook runs before the it callback function runs...but it redefines wrapper so when the it callback runs comp is still undefined and the test fails:
1) Component
prop checking
undefined should have title equal to SomeTitle:
TypeError: Cannot read property 'prop' of undefined
at Context.prop (test/code.test.js:15:19)
Solution
A couple of things need to be changed:
The component name needs to be available when it runs and at that point wrapper doesn't exist yet so you'll have to pass the name yourself.
If you pass an object to checkProps then you can set a wrapper property on the object during beforeEach and access that wrapper property within your test since the object is never redefined.
Here is a working test that should get you closer to what you are trying to do:
import * as React from 'react';
import { shallow } from 'enzyme';
const MyComponent = () => (<div title="SomeTitle">some text</div>);
const checkProps = (name, obj, propName, expectedvalue) => {
it(`${name} should have ${propName} equal to ${expectedvalue}`, () => {
expect(obj.wrapper.prop(propName)).to.equal(expectedvalue); // Success!
});
};
describe('Component', () => {
const obj = {};
beforeEach(() => {
obj.wrapper = shallow(<MyComponent />);
});
describe('prop checking', () => {
checkProps('MyComponent', obj, 'title', 'SomeTitle');
});
});

Correct syntax for importing axios method in Vue js

I am trying to separate my axios calls from my main vue instance by importing them instead of calling them directly in the created hook.
I have this in a separate file called data.js
import axios from 'axios'
export default{
myData() {
return axios.get(`http://localhost:8080/data.json`)
.then(response => {
// JSON responses are automatically parsed.
return response.data;
})
.catch(e => {
return this.myErrors.push(e)
});
},
And in my vue instance I have the following:
import myDataApi from '#/api/data.js'
export default {
name: 'app',
components: {
myDataApi, // not sure if this is correct
},
data: function () {
return {
myInfo: '',
}
},
created() {
this.myInfo = myDataApi.myData();
console.log('this.myInfo= ', this.myInfo)
},
I am trying to populate myInfo with the json called by myData. This returns [object Promise] in Vue devtools and the as Promise {<pending>} in the console.
All the data I need is inside that Promise {<pending>} in an array called [[PromiseValue]]:Object so I know it is working, I just need to know the correct way implementing this.
I don't have a development environment enabled to test this at the moment, but I do notice that you are trying to assign a variable the moment that the component is initialized. This object is a promise, but you're not handling the promise after it is resolved inside the component where you have imported it.
I would recommend trying to handle the promise inside of the actual component, something like:
import myDataApi from '#/api/data.js'
export default {
name: 'app',
components: {
myDataApi, // not sure if this is correct
},
data: function () {
return {
myInfo: '',
}
},
created() {
myDataApi.myData()
.then((data) => {
this.myInfo = data
console.log('this.myInfo= ', this.myInfo);
});
.catch((e) => handleError) // however you want to handle it
},
Just to add to #LexJacobs answer. I omitted the parenthesis around data in .then() as seen below. Vue was squawking about data not being available even though it was. This solved that problem, although to be honest I don't know why.
myDataApi.myData()
.then(data => {
this.dataHasLoaded = true;
this.myInfo = data;
})
.catch(e => {
this.myErrors.push(e)
});

Manual mock not working in with Jest

Can somebody help me with manual mocking in Jest, please? :)
I try to have Jest use the mock instead of the actual module.
my test:
// __tests__/mockTest.js
import ModuleA from "../src/ModuleA"
describe("ModuleA", () => {
beforeEach(() => {
jest.mock("../src/ModuleA")
})
it("should return the mock name", () => {
const name = ModuleA.getModuleName()
expect(name).toBe("mockModuleA")
})
})
my code:
// src/ModuleA.js
export default {
getModuleName: () => "moduleA"
}
// src/__mocks__/ModuleA.js
export default {
getModuleName: () => "mockModuleA"
}
I think I followed everything the documentation says about manual mocks, but perhaps I'm overlooking something here?
This is my result:
Expected value to be:
"mockModuleA"
Received:
"moduleA"
Module mocks are hoisted when possible with babel-jest transform, so this will result in mocked module:
import ModuleA from "../src/ModuleA"
jest.mock("../src/ModuleA") // hoisted to be evaluated prior to import
This won't work if a module should be mocked per test basis, because jest.mock resides in beforeEach function.
In this case require should be used:
describe("ModuleA", () => {
beforeEach(() => {
jest.mock("../src/ModuleA")
})
it("should return the mock name", () => {
const ModuleA = require("../src/ModuleA").default;
const name = ModuleA.getModuleName()
expect(name).toBe("mockModuleA")
})
})
Since it's not an export but a method in default export that should be mocked, this can also be achieved by mocking ModuleA.getModuleName instead of entire module.
This answer is not strictly related to the OPs question but google lead me here for a similar issue where jest.mock('module', () => ({})) in the root of a file did not work. In my case it was caused by cyclic dependencies. So if jest suddenly starts to ignore calls to jest.mock() then you might want to check for cyclic dependencies in your files.

error in unit test vue.js karma (webpack): undefined is not a constructor

I'm using the webpack template generated by Vue's CLI and have been trying to add some unit tests. And example has already been provided and it works perfectly:
import Vue from 'vue'
import Hello from '#/components/Hello'
describe('Hello.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(Hello)
const vm = new Constructor().$mount()
expect(vm.$el.querySelector('.hello h1').textContent)
.to.equal('Welcome to Your Vue.js App')
})
})
Then I try to add another test which I copied straight from Vue's documentation (Documentation link) but something weird happened:
// Inspect the raw component options
it('has a created hook', () => {
console.log(typeof Hello.created)
expect(typeof Hello.created).toBe('function')
})
I got the following error:
LOG LOG: 'function'
✗ has a created hook
undefined is not a constructor (evaluating 'expect((0, _typeof3.default)(_Hello2.default.created)).toBe('function')')
webpack:///test/unit/specs/Hello.spec.js:16:38 <- index.js:75919:64
So it seems like Hello.created gives me an undefined, but as you can see, I also console.log it to double check, and it does give it the desired result: undefined
Can anyone give me some help on what happened and how to fix it? I've already tried the solution here and still couldn't make it work.
For your reference, here's how Hello.vue looks like:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'hello',
data () {
return {
msg: 'Welcome to Your Vue.js App',
message: 'hello!'
}
},
created () {
console.log('oh crap')
this.message = 'bye!'
}
}
</script>
Turns out the template is actually using chai instead of jasmine to do unit test.
In this case,
expect(typeof Hello.created).to.equal('function')
or
expect(Hello.created).to.be.a('function')
both work.
For me to.be or toBe did not work. Instead I used .equal in the following way (to get the return of a component method) :
import Vue from 'vue'
import SingleTask from '#/components/SingleTask'
describe('SingleTask.vue', () => {
it('should return true', () => {
const Constructor = Vue.extend(SingleTask)
const vm = new Constructor().$mount()
expect(vm.forUnitTest()).equal(true);
})
})
That's what worked for me

Categories

Resources