Vue-test-utils: using $nextTick multiple times in a single test - javascript

I'm writing unit tests for vuelidate validation in my component. I figured out that the $touch() method is called asynchronously, so I need to use $nextTick() for the expect(). The problem appears when I need two nextTick()s for two expect()s.
describe('Validations', () => {
let data
let myComponent
beforeEach(() => {
data = () => {
propertyABC = 'not allowed value'
}
myComponent = localVue.component('dummy', {template: '<div></div>', validations, data})
it('Properly validates propertyABC', (done) => {
Vue.config.errorHandler = done
let wrapper = mount(myComponent, {localVue})
wrapper.vm.$v.$touch()
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.$v.propertyABC.$error).to.be.true
# fails, because propertyABC === 'allowed value', adn thus $error is false
done()
}
wrapper.vm.propertyABC = 'allowed value'
wrapper.vm.$v.propertyABC.$touch()
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.$v.proprtyABC.$error).to.be.false
done()
}
})
})
How can I run this test without splitting it into two separate tests? I think nesting the $nextTick() might work, but it would be not flexible for higher amount of tests.

If you're able to use async functions, then you could await the $nextTick calls. This would avoid having to nest them and would keep everything in the same test.
Like so:
describe('Validations', () => {
let data;
let myComponent;
beforeEach(() => {
data = () => ({ propertyABC = 'not allowed value' });
myComponent = localVue.component('dummy', {template: '<div></div>', validations, data});
});
it('Properly validates propertyABC', async () => {
let wrapper = mount(myComponent, {localVue});
wrapper.vm.$v.$touch();
await wrapper.vm.$nextTick();
expect(wrapper.vm.$v.propertyABC.$error).to.be.true;
wrapper.vm.propertyABC = 'allowed value';
wrapper.vm.$v.propertyABC.$touch();
await wrapper.vm.$nextTick();
expect(wrapper.vm.$v.proprtyABC.$error).to.be.false;
})
})

Another approach is to use flushPromises.
import flushPromises from 'flush-promises';
...
test('some async test', async () => {
const wrapper = mount(MyComponent, { localVue });
wrapper.vm.$v.$touch();
await flushPromises();
});
flushPromises() returns a promise itself, so its useful when you need/want to chain things using .then().then() etc...

Related

Async await test cases failed using jest javascript testing library

I am using Jest testing Library for some simple async/await functions. But it's failing again and again as I am very new to jest. and can you please answer what expect.assertions(1) do here
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id: 1,
name: "test",
age: 20,
});
}, 1000);
});
}
test("test async await", async () => {
const data = await fetchData();
expect(data.id).toBe(1);
});
test("async await error", async () => {
expect.assertions(1);
try {
await fetchData();
} catch (e) {
expect(e).toMatch("error");
}
});
As I pointed out in the comments, the test for the "failure" case doesn't really make sense if this fetchData is the real function because it never 'rejects'. To test the failure case in Jest, you'd need to somehow trigger the Promise.reject case.
If we assume this fetchData is a wrapper on an api call or something else, we could imagine something like this.
You might have a library or module that is making api calls like:
// api.js
const api = {
actualFetchData: () => {
// this is the function that actually connects
// to a data source and returns data
},
};
module.exports = api;
And your fetchData function which you're trying to test looks like:
// fetchData.js
const api = require("./api");
function fetchData() {
return api.actualFetchData();
}
module.exports = fetchData;
Then, assuming this structure matches what you're working on, you can mock the internals of fetchData and test both success and failure cases by mocking actualFetchData and using mockResolvedValue and mockRejectedValue.
// fetchData.test.js
const fetchData = require("./fetchData");
const api = require("./api");
jest.mock("./api");
const mockApiFetch = jest.fn();
api.actualFetchData = mockApiFetch;
describe("when the underlying fetch resolves", () => {
beforeEach(() => {
mockApiFetch.mockResolvedValue({
id: 1,
name: "test",
age: 20,
});
});
test("test async await", async () => {
const data = await fetchData();
expect(data.id).toBe(1);
});
});
describe("when the underlying fetch fails", () => {
beforeEach(() => {
mockApiFetch.mockRejectedValue(new Error("failed to get data"));
});
test("async await error", async () => {
expect(() => fetchData()).rejects.toThrow("failed to get data");
});
});
You'll notice I didn't use the expect.assertions because it didn't seem like it added anything to the test. Instead, just used toThrow with text that matches the error.
I realize this is making some assumptions about a system that you haven't fully described in the initial question so this may not be exactly what you're trying to get at. Hopefully it's close.

Jest: shared async code between test blocks

I have some test code like this:
test('Test', async () => {
const someData = await setup()
const actual = myFunc(someData.x)
expect(actual.a).toEqual(someData.y)
expect(actual.b).toEqual(someData.y)
... many more like this
}
I would like to break apart the code into multiple test blocks (because I can't even add a description message to each expect statement).
If Jest supported async describe, I could do this:
describe('Some group of tests', async () => {
const someData = await setup()
test('Test1', async () => {
const actual = myFunc(someData.x)
expect(actual.a).toEqual(someData.y)
}
test('Test2', async () => {
const actual = myFunc(someData.x)
expect(actual.b).toEqual(someData.y)
}
})
I could duplicate the setup call for each test of course, but that would slow down the test considerable (I'm populating MongoDB there).
So, any way I can improve the structure of my test with Jest?
It's correct that describe callback function isn't supposed to be asynchronous. It synchronously defines tests for a suite, any asynchronous operations in its scope will be discarded.
Previously Jasmine and Jest allowed to access common test context with regular functions and this. This feature was deprecated in Jest; common variables need to be user-defined.
Shared code can be moved into helper function that internally uses beforeAll, beforeEach, etc:
const setupWithTestContext = (testContext = {}) => {
beforeAll(async () => {
const setupData = await setup();
Object.assign(testContext, setupData);
});
return testContext; // sets up a new one or returns an existing
});
const anotherSetupWithTestContext = (testContext = {}) => {
beforeEach(() => {
testContext.foo = 0;
});
return testContext;
});
...
describe('Some group of tests', async () => {
const sharedTestData = setupTestContext();
// or
// const sharedTestData = {}; setupTestContext(sharedTestData);
anotherSetupWithTestContext(sharedTestData);
test('Test1', async () => {
// context is filled with data at this point
const actual = myFunc(sharedTestData.x)
...
}
...
})

Testing async componentDidMount() with react-test-renderer

I have a component which does some SQLite loads in it's componentDidMount() function
async componentDidMount() {
try {
const user = await this.userDao.getUserData();
const setupNeeded = user === null;
if( setupNeeded ) {
this.setState({
status : 'setup'
});
}
else {
this.setState({
status : 'ready',
seed: user.seed
})
}
} catch (e) {
logger.error("Error during db query", e);
this.setState({
status: 'corrupted'
});
}
}
And I would like to test the rendered result after the call to getUserData() has been resolved, and the state has been set accordingly.
Now in my test, I have the actual call to the database mocked away, so the Promise should be resolved immediately, yet testing like this does not work as expected:
Initially, I tried it like this:
test('Should render SetNewPasswordScreen', async () => {
const tree = await renderer.create(<IndexScreen/>);
const json = tree.toJSON();
// expect stuff
});
However the json in this case contains the data of the initial render() call.`
Doing it like this will work:
test('Should render SetNewPasswordScreen', (done) => {
const tree = renderer.create(<IndexScreen/>);
setTimeout(() => {
const json = tree.toJSON();
// expect stuff
}, 5000);
});
But this is not ideal, because I am just guessing that after 5 seconds everything will be done, but I don't know. Also, it's less then suitable if a test takes 5 seconds to finish. (I used 5 seconds arbitrarily, probably it will also work with much less since the async call is mocked anyway, but I can never really know)
My question is if anybody has a better idea of how to solve this issue?
it('should render setNewPassWordScreen', async () => {
const tree = await renderer.create(<IndexScreen/>);
const instance = tree.getInstance();
await instance.componentDidMount();
// expect other stuff.
});
Little late for the party but faced the issues as well. Here is a handy shortcut function I use with the adviced usage of act:
import renderer from "react-test-renderer"
import { ReactElement } from "react"
import { act } from "#testing-library/react-native"
export const expectToMatchSnapshot = async (component: ReactElement) => {
let tree
await act(async () => {
tree = renderer.create(component)
})
expect(tree.toJSON()).toMatchSnapshot()
}
In your case you should only then add:
test("should render ***", async () => {
await expectToMatchSnapshot(<IndexScreen/>)
}
you could use the wait for expect package:
https://www.npmjs.com/package/wait-for-expect
test('Should render SetNewPasswordScreen', async (done) => {
const tree = renderer.create(<IndexScreen/>);
await waitForExpect(() => {
const json = tree.toJSON();
// expect stuff
});
});
mind the function passed into the test('...', fn) is now an async function.

Jest: restore original module implementation on a manual mock

I have a pretty common testing use case and I am not sure what's the best approach there.
Context
I would like to test a module that depends on a userland dependency. The userland dependency (neat-csv) exports a single function that returns a Promise.
Goal
I want to mock neat-csv's behavior so that it rejects with an error for one single test. Then I want to restore the original module implementation.
AFAIK, I can't use jest.spyOn here as the module exports a single function.
So I thought using manual mocks was appropriated and it works. However I can't figure it out how to restore the original implementation over a manual mock.
Simplified example
For simplicity here's a stripped down version of the module I am trying to test:
'use strict';
const neatCsv = require('neat-csv');
async function convertCsvToJson(apiResponse) {
try {
const result = await neatCsv(apiResponse.body, {
separator: ';'
});
return result;
} catch (parseError) {
throw parseError;
}
}
module.exports = {
convertCsvToJson
};
And here's an attempt of testing that fails on the second test (non mocked version):
'use strict';
let neatCsv = require('neat-csv');
let { convertCsvToJson } = require('./module-under-test.js');
jest.mock('neat-csv', () =>
jest.fn().mockRejectedValueOnce(new Error('Error while parsing'))
);
const csv = 'type;part\nunicorn;horn\nrainbow;pink';
const apiResponse = {
body: csv
};
const rejectionOf = (promise) =>
promise.then(
(value) => {
throw value;
},
(reason) => reason
);
test('mocked version', async () => {
const e = await rejectionOf(convertCsvToJson(apiResponse));
expect(neatCsv).toHaveBeenCalledTimes(1);
expect(e.message).toEqual('Error while parsing');
});
test('non mocked version', async () => {
jest.resetModules();
neatCsv = require('neat-csv');
({ convertCsvToJson } = require('./module-under-test.js'));
const result = await convertCsvToJson(apiResponse);
expect(JSON.stringify(result)).toEqual(
'[{"type":"unicorn","part":"horn"},{"type":"rainbow","part":"pink"}]'
);
});
I am wondering if jest is designed to do such things or if I am going the wrong way and should inject neat-csv instead ?
What would be the idiomatic way of handling this ?
Yes, Jest is designed to do such things.
The API method you are looking for is jest.doMock. It provides a way of mocking modules without the implicit hoisting that happens with jest.mock, allowing you to mock in the scope of tests.
Here is a working example of your test code that shows this:
const csv = 'type;part\nunicorn;horn\nrainbow;pink';
const apiResponse = {
body: csv
};
const rejectionOf = promise =>
promise.then(value => {
throw value;
}, reason => reason);
test('mocked version', async () => {
jest.doMock('neat-csv', () => jest.fn().mockRejectedValueOnce(new Error('Error while parsing')));
const neatCsv = require('neat-csv');
const { convertCsvToJson } = require('./module-under-test.js');
const e = await rejectionOf(convertCsvToJson(apiResponse));
expect(neatCsv).toHaveBeenCalledTimes(1);
expect(e.message).toEqual('Error while parsing');
jest.restoreAllMocks();
});
test('non mocked version', async () => {
const { convertCsvToJson } = require('./module-under-test.js');
const result = await convertCsvToJson(apiResponse);
expect(JSON.stringify(result)).toEqual('[{"type":"unicorn","part":"horn"},{"type":"rainbow","part":"pink"}]');
});

How to unit test/stub async call in redux

How is the proper way to unit test the following redux async action?
const client = contentful.createClient(clientConfig);
export const fetchNavigation = () => {
return dispatch => {
return client.getEntries({content_type: 'navigation'})
.then((entries) => {
console.log('All entries for content_type = navigation')
dispatch(receiveNavigation(entries))
})
.catch(error => {
console.log('Something went wrong');
dispatch(fetchNavigationFailure(error));
});
}
}
I don't know how to customise the web request response body performed by client.getEntries. I think that replacing the getEntries function with my own one would do the trick. However, I don't know where to start to do that.
Here is the unit test I wrote:
const middlewares = [ thunk ]
const mockStore = configureMockStore(middlewares)
describe('fetchNavigation', () => {
it('creates RECEIVE_NAVIGATION when fetching navigation is done', () => {
// Here I should prepare the client.getEntries() returned promise
const expectedBodyResponse = { includes: ['do something', 'yay!'] }
const expectedActions = [
{ type: actions.RECEIVE_NAVIGATION, navigation: expectedBodyResponse }
]
const store = mockStore({ todos: [] })
return store.dispatch(actions.fetchNavigation())
.then(() => {
expect(store.getActions()).toEqual(expectedActions)
})
})
})
IMO mocking getEntries (and probably createClient) seems to be the right way to do. :)
It depends how you load the contentful sdk. As I see you're using ES Modules and Jasmine, right?
To mock the getEntries function you have to mock the createClient as the client is not accessible from within your test.
I think this this answer might be what you're looking for.
I just wrote down an example.
import contentful from 'contentful';
export const fetchNavigation = () => {
return (dispatch) => {
return contentful.createClient({ accessToken: 'fooo', space: 'bar' })
.getEntries({ content_type: 'navigation' })
.then(() => {
dispatch('yeah');
})
.catch(error => console.error('Something went wrong', error));
};
};
import { fetchNavigation } from '../Action';
import * as contentful from 'contentful';
describe('Contentful mocking', () => {
it('should be possible to mock Contentful', (done) => {
const client = { getEntries: () => { return Promise.resolve(); } };
const spy = {
fn: (value) => {
expect(value).toBe('yeah');
done();
},
};
spyOn(contentful.default, 'createClient').and.returnValue(client);
fetchNavigation()(spy.fn);
});
});
I had to move the createClient call into the action itself, because otherwise I don't think it's possible to reach and mock it when it's hidden in the module scope. I then used the import * as contentful from 'contentful' to mock and overwrite the needed functionality and to have the flexibility to adjust everything to my needs.
The usage of the createClient feels a bit unfortunate for me. I'd probably restructure everything a bit and would pass the client as dependency of all the actions? This way the mocking would become way easier and when you also have several action modules, there is most probably no need to initialize the client several times?
I solved in this way.
First I moved the creation of the client to its own file with functions initClient() and getClient(). The module is called contentfulClient.
Then, I found out that it is possible to mock the function of an instantiated object in sinon:
import * as contentful from './services/contentfulClient';
const client = contentful.initClient(clientConfig);
const navigation = {
items: ['page1', 'page2']
};
// Returns a promise with navigation as content
sinon.stub(client, 'getEntries').resolves(navigation);
// Assert
return store.dispatch(actions.fetchNavigation())
.then( () => { expect(store.getActions()).toEqual(expectedActions)
})

Categories

Resources