I am using the following in all my individual test case files which runs successfully.
jest.mock('next-i18next', () => {
return {
useTranslation: () => {
return {
t: (key) => {
const translations = {
title: 'May I help?',
showErrors: 'Something went wrong.',
noResultFound: `Oops`,
};
return translations[key];
},
};
},
};
});
The translations change based on different test cases and hence I had duplicated them everywhere I need.
But I want to modify this so that this mock becomes generic(maybe by creating a function) that sits in one place while I call this created function from all the test case files I need and pass my custom dictionary to it. Something like this but it doesn't work:
const mockTranslations = (mockdictionary) => ({
jest.mock('next-i18next', () => {
return {
useTranslation: () => {
return {
t: (key) => {
return mockdictionary[key];
},
};
},
};
})
})
I am not sure if it is possible to do in Jest, still trying to figure out. Any help here or other approach of doing it will be really appreciated.
I would suggest two strategies :
modify your mock, so the t function always returns the translation key (basically, t: (key) => key). In your tests, check that the correct translation key is present in the DOM (expect(getByText("myTranslationKey")).toBeTruthy()). So you can validate that the correct key is used, but not that the corresponding text is correct,
do not mock react-i18next in your test, even better :). More realistic, more confidence. If you load JSON files dynamically, you will need to modify your react-i18next configuration for the Jest tests. In a Jest configuration file, add :
// assuming that you have multiple JSON files in /public/locales/en
import * as namespaces from '/public/locales/en';
i18next.use(initReactI18next).init({
[...]
lng: 'en',
resources: { en: namespaces },
});
So now you can test you real texts in Jest suites.
Related
How to make my test wait for the result of my api?
I'm using vue and jest to test my components.
I want to test the method that writes a client to my database. In my component I have the following method:
methods: {
onSubmitClient(){
axios.post(`urlApi`, this.dados).then(res => {
return res;
})
}
}
in my test
describe('login.vue', () => {
let wrapper
beforeAll(()=>{
wrapper = mount(client, {
stubs: ['router-link'],
store,
data() {
return {
dados: {
name: 'tes name',
city: 'test city'
},
};
}
})
})
it('store client', () => {
res = wrapper.vm.onSubmitLogin()
console.log(res);
})
})
My test does not wait for the API call to complete. I need to wait for the API call to know if the test worked. How can I make my test wait for API return?
There are several issues in your code.
First, you cannot return from an async call. Instead, you should be probably setting up some data in your onSubmitClient, and returning the whole axioscall, which is a Promise. for instance:
onSubmitClient(){
return axios.post(`urlApi`, this.dados).then(res => {
this.result = res;
return res;
})
}
I assume the method here is storing a result from the server. Maybe you don't want that; it is just an example. I'll come back to it later.
Ok, so now, you could call onSubmitClient in your wrapper and see if this.result is already set. As you already know, this does not work straightforward.
In order for a jest test to wait for asynchronous code, you need either to provide a done callback function or return a promise. I'll show an example with the former:
it('store client', (done) => {
wrapper.vm.onSubmitLogin().then((res) => {
expect(wrapper.vm.dados).toEqual(res);
done();
})
});
Now this code should just work, but still there is an issue with it, as #jonsharpe says in a comment.
You usually don't want to perform real network requests in unitary tests because they are slow and unrealiable. Also, unitary tests are meant to test components in isolation, and here we are testing not only that our component sets this.result properly when the request is made. We are also testing that there is a webserver up and running that is actually working.
So, what I would do in this scenario to test that single piece of functionality, is to extract the request to another method, mock it with vue-test-utils and jest.fn, and then assert that onSubmitClient does its work:
The component:
export default {
data() {
return {
http: axios,
...
},
methods: {
onSubmitClient(){
this.http.post(`urlApi`, this.dados).then(res => {
this.result = res;
})
}
}
}
}
The test:
it('store client', (done) => {
const fakeResponse = {foo: 'bar'};
var post = jest.fn();
var http : {
post,
};
var wrapper = mount(client, {
stubs: ['router-link'],
store,
data() {
return {
dados: {
name: 'tes name',
city: 'test city'
},
http, //now, the component under test will user a mock to perform the http post request.
}
}
});
wrapper.vm.onSubmitLogin().then( () => {
expect(post).toHaveBeenCalled();
expect(wrapper.vm.result).toEqual(fakeResponse);
done();
})
});
Now, your test asserts two things:
post gets called.
this.result is set as it should be.
If you don't want to store anything in your component from the server, just drop the second assertion and the this.result = res line in the method.
So basically this covers why your test is not waiting for the async request and some issues in your code. There are still some things to consider (f.i. I think a global wrapper is bad idea, and I would always prefer shallowMount over mount when testing components behavior), but this answer should help you a lot.
PS: didn't test the code, so maybe I messed up something. If the thing just doesn't work, look for syntax errors or similar issues.
I have an arrow function in my React-Redux application that dispatches just an action without a payload, which will wipe (Reset) a part of my Redux store. There's no HTTP (or Promises) involved.
export const resetSearch = () => dispatch => {
dispatch({
type: RESET_SEARCH
});
};
I want to test that it returns the correct Action type:
import * as actions from '../actions/actionCreators';
import * as types from '../actions/actionTypes';
describe('actions', () => {
it('should create an action to reset part of the store', () => {
const expectedAction = {
type: types.RESET_SEARCH
};
expect(actions.resetSearch()).toEqual(expectedAction);
});
});
The test is not passing because I need to return an Object but, instead, I send this anonimous arrow function. Here is the Jest output
Expected value to equal: {"type": "RESET_SEARCH"}
Received: [Function anonymous]
How should the test be?
All help will be apreaciated!
Thanks
Can you plese try below code snippet, It should do:
const expectedAction = {
type: types.RESET_SEARCH
};
let retnFunc = actions.resetSearch();
retnFunc((receivedAction)=>{
expect(receivedAction).toEqual(expectedAction);
});
Here resetSearch returns a function which gets called with the action object so just imitated the same.
Let me know if you need any more help!
I'm using VueJS from Vue CLI. So all my components are in .vue format.
In one of my components, I have an array called fields in the data section.
//Component.vue
data() {
return {
fields : [{"name" : "foo", "title" : "Foosteria"}, {"name" : "bar", "title" : "Barrista"}]
}
}
I have a computed property that is a subset of fields
//Component.vue
computed : {
subsetOfFields () {
// Something else in component data determines this list
}
}
I've set up all of my unit tests in jasmine like this and they work fine.
//Component.spec.js
import Vue from 'vue'
import MyComponent from 'Component.vue'
describe("Component test", function() {
var myComponentVar = new Vue(MyComponent);
var vm = myComponentVar.$mount();
beforeEach(function() {
vm = myComponentVar.$mount();
);
afterEach(function() {
vm = myComponentVar.$destroy();
});
it("First spec tests something", function() {
...
});
});
For everything else, doing something inside the spec, then running assertions on the data objects works just fine. However, running an assertion on subsetOfFields always returns an empty array. Why so? What should I do, in order to be able to test it?
FYI, I even tried nesting the spec inside another describe block and then adding a beforeEach which initializes the fields array. It did not work.
However, initializing fields inside the generic beforeEach function worked. But I don't want to initialize the fields array with that mock data for the other specs.
I came across this link that talks about testing and the section you'll need to look at is the Vue.nextTick(...) section
https://alligator.io/vuejs/unit-testing-karma-mocha/
The block I'm talking about is below:
import Vue from 'vue';
// The path is relative to the project root.
import TestMe2 from 'src/components/TestMe2';
describe('TestMe2.vue', () => {
...
it(`should update when dataText is changed.`, done => {
const Constructor = Vue.extend(TestMe2);
const comp = new Constructor().$mount();
comp.dataProp = 'New Text';
Vue.nextTick(() => {
expect(comp.$el.textContent)
.to.equal('New Text');
// Since we're doing this asynchronously, we need to call done() to tell Mocha that we've finished the test.
done();
});
});
});
I'm trying to write tests for a react component I've built that utilizes navigator.geolocation.getCurrentPosition() within a method like so (rough example of my component):
class App extends Component {
constructor() {
...
}
method() {
navigator.geolocation.getCurrentPosition((position) => {
...code...
}
}
render() {
return(...)
}
}
I'm using create-react-app, which includes a test:
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
});
This test fails, printing out this in the console:
TypeError: Cannot read property 'getCurrentPosition' of undefined
I'm new to React, but have quite a bit of experience with angular 1.x. In angular it is common to mock out (within the tests in a beforeEach) functions, "services", and global object methods like navigator.geolocation.etc. I spent time researching this issue and this bit of code is the closest I could get to a mock:
global.navigator = {
geolocation: {
getCurrentPosition: jest.fn()
}
}
I put this in my test file for App, but it had no effect.
How can I "mock" out this navigator method and get the test to pass?
EDIT: I looked into using a library called geolocation which supposedly wraps navigator.getCurrentPosition for use in a node environment. If I understand correctly, jest runs tests in a node environment and uses JSDOM to mock out things like window. I haven't been able to find much information on JSDOM's support of navigator. The above mentioned library did not work in my react app. Using the specific method getCurrentPosition would only return undefined even though the library itself was imported correctly and available within the context of the App class.
It appears that there is already a global.navigator object and, like you, I wasn't able to reassign it.
I found that mocking the geolocation part and adding it to the existing global.navigator worked for me.
const mockGeolocation = {
getCurrentPosition: jest.fn(),
watchPosition: jest.fn()
};
global.navigator.geolocation = mockGeolocation;
I added this to a src/setupTests.js file as described here - https://create-react-app.dev/docs/running-tests#initializing-test-environment
I know this issue might have been solved, but seems that all the solutions above are all wrong, at least for me.
When you do this mock: getCurrentPosition: jest.fn()
it returns undefined, if you want to return something, this is the correct implementation:
const mockGeolocation = {
getCurrentPosition: jest.fn()
.mockImplementationOnce((success) => Promise.resolve(success({
coords: {
latitude: 51.1,
longitude: 45.3
}
})))
};
global.navigator.geolocation = mockGeolocation;
I am using create-react-app
A TypeScript version for anyone that was getting
Cannot assign to 'geolocation' because it is a read-only property.
In the mockNavigatorGeolocation.ts file (this can live in a test-utils folder or similar)
export const mockNavigatorGeolocation = () => {
const clearWatchMock = jest.fn();
const getCurrentPositionMock = jest.fn();
const watchPositionMock = jest.fn();
const geolocation = {
clearWatch: clearWatchMock,
getCurrentPosition: getCurrentPositionMock,
watchPosition: watchPositionMock,
};
Object.defineProperty(global.navigator, 'geolocation', {
value: geolocation,
});
return { clearWatchMock, getCurrentPositionMock, watchPositionMock };
};
I then import this in my test at the top of the file:
import { mockNavigatorGeolocation } from '../../test-utils';
And then use the function like so:
const { getCurrentPositionMock } = mockNavigatorGeolocation();
getCurrentPositionMock.mockImplementation((success, rejected) =>
rejected({
code: '',
message: '',
PERMISSION_DENIED: '',
POSITION_UNAVAILABLE: '',
TIMEOUT: '',
})
);
Mocking with setupFiles
// __mocks__/setup.js
jest.mock('Geolocation', () => {
return {
getCurrentPosition: jest.fn(),
watchPosition: jest.fn(),
}
});
and then in your package.json
"jest": {
"preset": "react-native",
"setupFiles": [
"./__mocks__/setup.js"
]
}
I followed #madeo's comment above to mock global.navigator.geolocation. It worked!
Additionally I did the following to mock global.navigator.permissions:
global.navigator.permissions = {
query: jest
.fn()
.mockImplementationOnce(() => Promise.resolve({ state: 'granted' })),
};
Set state to any of granted, denied, prompt as per requirement.
For whatever reason, I did not have the global.navigator object defined, so I had to specify it in my setupTests.js file
const mockGeolocation = {
getCurrentPosition: jest.fn(),
watchPosition: jest.fn(),
}
global.navigator = { geolocation: mockGeolocation }
Added to the above answers, if you want to update navigator.permissions, this will work.The key here is to mark writable as true before mocking
Object.defineProperty(global.navigator, "permissions", {
writable: true,
value: {
query : jest.fn()
.mockImplementation(() => Promise.resolve({ state: 'granted' }))
},
});
Suppose I have a reducer file reducers/group1.js like this
export default combineReducers({
A: combineReducers({ A1, A2 }),
B: reducerB,
C: reducerC
})
Is there any difference between testing each slice reducer (A1, A2, reducerB and reducerC) and testing the combined one?
import group1 from 'reducers/group1'
describe('reducers', () => {
describe('group1', () => {
it('should provide the initial state', () => {
expect(group1(undefined, {})).to.equal({ group1: { A: { ... }, B: ... } })
})
it(...)
// ...
})
})
or
import { A1, A2, reducerB, reducerC } from 'reducers/group1'
describe('reducers', () => {
describe('group1', () => {
describe('A1', () => {
it('should provide the initial state', () => {
expect(A1(undefined, {})).to.equal(0) // if A1 is just a number
})
})
describe('A2', () => { ... })
describe('reducerB', () => { ... })
describe('reducerC', () => { ... })
})
})
Your second example is usually better because it allows for simpler unit tests. I can imagine a scenario where a developer might want to write a bunch of tests for reducer C without knowing anything about reducers A and B. The second code sample allows for that developer to write a suite of C tests without being concerned about what A or B even are. It also helps when rewriting tests if a reducer's behavior is drastically changed: all those tests live in one place instead of being scattered all over the test file.
However, there might be some instances where you want to write a test for the entire reducer. For example, if you have a global reset action, you would want to test that the entire reducer properly responds to that action instead of writing an individual test for each reducer. Most of the time it's probably going to be cleaner to write tests for individual reducers though.