How do I reset the redux-test-utils store between tests - javascript

I have a subscriber that dispatches an action based on the parameters supplied to a pub event
// subscriptions.js
import store from '../store';
import { action } from '../actions';
export const subscribeToToggle = () => {
window.$.subscribe('action/toggle', (_e, isToggleOn) => {
if (isToggleOn=== true){
store.dispatch(action());
}
});
};
In my test file I write 2 tests that test that the action is sent only when true is supplied.
// subscriptions.test.js
import { subscribeToToggle } from './subscriptions';
import jQuery from 'jquery';
import { actionTypes } from '../constants';
import store from '../store';
jest.mock('../store');
beforeEach(() => {
window.$ = jQuery;
window.$.unsubscribe('action/toggle');
});
test('Action not sent when false', () => {
subscribeToToggleOpen();
window.$.publish('action/toggle', false);
expect(store.getActions().length).toBe(0);
});
test('Action sent when true', () => {
subscribeToToggleOpen();
window.$.publish('action/toggle', true);
expect(store.getActions().length).toBe(1);
expect(store.getActions()[0].type).toBe(actionTypes.ACTION);
});
I have the following mocked store using redux-test-utils
import { createMockStore } from 'redux-test-utils';
let store = null;
store = createMockStore('');
export default store;
The issue I face is that my test only pass when the false test comes first. If they are the other way around the 'Action not sent when false' test fails as it sees the action supplied by the 'Action sent when true' test.
Is there any way for me to use the beforeEach method to reset the mocked store object?

In this case, the problem is that your store is essentially a singleton. This can create issues when you are trying to do things like this and is generally kind of an anti-pattern.
Instead of exporting a store object, it'd probably be better if you exported a getStore() function which could be called to get the store. In that case, you could then do:
getStore().dispatch(action());
Inside of that, you could then have other helper functions to be able to replace the store that is being returned by it. That file could look something like this:
import { createMockStore } from 'redux-test-utils';
let store = createMockStore('');
export default () => store;
Then, inside of there, you can add another which could be resetStore as a non-default export:
export const resetStore = () => store = createMockStore('');
It would still technically be a singleton, but it's now a singleton you can have some control over.
Then, in your beforeEach() in your tests, just call resetStore():
import { resetStore } from '../store';
beforeEach(() => {
resetStore();
});
This would also require you to update your real code to use getStore() instead of store directly, but it'll probably be a beneficial change in the long run.
Complete Updated Version:
// subscriptions.js
import getStore from '../store';
import { action } from '../actions';
export const subscribeToToggle = () => {
window.$.subscribe('action/toggle', (_e, isToggleOn) => {
if (isToggleOn=== true){
getStore().dispatch(action());
}
});
};
// subscriptions.test.js
import { subscribeToToggle } from './subscriptions';
import jQuery from 'jquery';
import { actionTypes } from '../constants';
import getStore, { resetStore } from '../store';
jest.mock('../store');
beforeEach(() => {
window.$ = jQuery;
window.$.unsubscribe('action/toggle');
resetStore();
});
test('Action not sent when false', () => {
subscribeToToggleOpen();
window.$.publish('action/toggle', false);
expect(getStore().getActions().length).toBe(0);
});
test('Action sent when true', () => {
subscribeToToggleOpen();
window.$.publish('action/toggle', true);
expect(getStore().getActions().length).toBe(1);
expect(getStore().getActions()[0].type).toBe(actionTypes.ACTION);
});
import { createMockStore } from 'redux-test-utils';
let store;
export const resetStore = () => { store = createMockStore(''); }
resetStore(); // init the store, call function to keep DRY
export default () => store;
Beyond that, the other way would be to have a global reducer which could reset the state of the store to it's default, but that would be messier and I don't really think would generally fit with writing a unit test.

Related

Jest Mocked Function not calling mocked axios instance function (returns undefined)

I started by following the answer in this StackOverflow Question
But I added a helper function I use which creates a new Axios instance with the auth token associated with the user.
Which looks a little like this:
import axios from "axios";
const mockAxios: jest.Mocked<typeof axios> = jest.createMockFromModule("axios");
// this is the key to fix the axios.create() undefined error!
mockAxios.create = jest.fn(() => {
return mockAxios;
});
export const createAuthenticatedInstance = () => {
return mockAxios.create();
};
export default mockAxios;
Why does mockAxios.create() return undefined?
While the object 'mockAxios' (and the create function) is defined. When I actually call create it returns undefined despite the function being declared.
I know that I can side-step the issue by just returning mockAxios against but I'd like to understand why it doesn't work in the first place. What I'd expect is to return a new instance, which would be identical to mockAxios but it just returns undefined.
If you're creating an auto-mock (within __mocks__) it's meant to be a mock of the module and any helper functions are not expected to be within the module, but probably somewhere else with your code
Exmaple:
src/axios.utils.ts (utility module which exports axios and the function)
import axios from "axios";
export const createAuthenticatedInstance = (
...args: Parameters<typeof axios.create>
) => {
return axios.create(...args);
};
export default axios;
src/__mocks__/axios.ts (the axios mock)
const axios: jest.Mocked<typeof import("axios").default> = jest.createMockFromModule(
"axios"
);
axios.create.mockReturnThis();
export default axios;
src/api.ts (api implementation that uses the axios.util)
import axios from "axios";
import { createAuthenticatedInstance } from "./axios.utils";
const client = createAuthenticatedInstance({
baseURL: "http://example.com:80/main",
});
export default {
makeSomeReq: () => client.get<string>("/path"),
};
src/api.spec.ts (the test)
import api from "./api";
import axios, { AxiosResponse } from "axios";
jest.mock("axios");
const { get } = axios as jest.Mocked<typeof import("axios").default>;
describe("api", () => {
it("should have created an axios instance", () => {
expect(axios.create).toHaveBeenCalledWith({
baseURL: "http://example.com:80/main",
});
});
})
working example

How to call an action (NuxtJs)

I'm trying to call an action in my vue from my store.
This is my file aliments.js in my store:
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
Vue.use(Vuex, axios);
export const state = () => ({
aliments: {},
})
export const mutations = () => ({
SET_ALIMENTS(state, aliments) {
state.aliments = aliments
}
})
export const actions = () => ({
async getListAliments(commit) {
await Vue.axios.get(`http://localhost:3080/aliments`).then((response) => {
console.log(response);
commit('SET_ALIMENTS', response);
}).catch(error => {
throw new Error(`${error}`);
})
// const data = await this.$axios.get(`http://localhost:3080/aliments`)
// commit('setUser', user)
// state.user = data;
// return state.user;
}
})
export const getters = () => ({
aliments (state) {
return state.aliments
}
})
I want to diplay a list of aliments in my vue with :
{{ this.$store.state.aliments }}
I call my action like this :
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
computed: {
...mapGetters(['loggedInUser', 'aliments']),
...mapActions(['getListAliments']),
getListAliments() {
return this.$state.aliments
}
}
}
</script>
I don't understand where is my mistake :/
NB: I also tried with a onclick method on a button with a dispatch('aliments/getListAliments')... but doesn't work...
The problem is that you're mapping your actions in the "computed" section of the component, you should map it in the "methods" section !
Hi and Welcome to StackOverflow
to quickly answer to your question, you would call an action as:
this.$store.dispatch('<NAME_OF_ACTION>', payload)
or though a mapActions as
...mapActions(['getListAliments']), // and you call `this.getListAliments(payload)`
or yet
...mapActions({
the_name_you_prefer: 'getListAliments' // and you call `this.the_name_you_prefer(payload)`
}),
for getters, it's the same process, as you already have 2 definitions ['loggedInUser', 'aliments'] you simply call the getter like if it was a computed value <pre>{{ aliments }}</pre>
or when we need to do a bit more (like filtering)
getListAliments() {
return this.$store.getters['aliments']
}
But I can see your store is as we call, one-to-rule-them-all, and because you are using Nuxt, you can actually leverage the module store very easy
as your application grows, you will start store everything in just one store file (the ~/store/index.js file), but you can easily have different stores and instead of what you wrote in index.js it can be easier if you had a file called, taken your example
~/store/food.js with
import axios from 'axios'
export const state = () => ({
aliments: {},
})
export const getters = {
aliments (state) {
return state.aliments
}
}
export const mutations = {
SET_ALIMENTS(state, aliments) {
state.aliments = aliments
}
}
export const actions = {
async getListAliments(commit) {
await axios.get('http://localhost:3080/aliments')
.then((response) => {
console.log(response);
commit('SET_ALIMENTS', response.data);
}).catch(error => {
throw new Error(`${error}`);
})
}
}
BTW, remember that, if you're using Nuxt serverMiddleware, this line
axios.get('http://localhost:3080/aliments')...
would simply be
axios.get('/aliments')...
and to call this store, all you need is to prefix with the filename, like:
...mapActions(['food/getListAliments'])
// or
...mapActions({ getListAliments: 'food/getListAliments' })
// or
this.$store.commit('food/getListAliments', payload)
another naming that could help you along the way:
on your action getListAliments you're actually fetching data from the server, I would change the name to fetchAliments
on your getter aliments you're actually returning the list, I would name it getAllAliments
have fun, Nuxt is amazing and you have a great community on Discord as well for the small things :o)
EDIT
also remember that actions are set in methods
so you can do:
...
export default {
methods: {
...mapActions(['getListAliments]),
},
created() {
this.getListAliments()
}
}
and in your Store action, please make sure you write
async getListAliments({ commit }) { ... }
with curly braces as that's a deconstruction of the property passed
async getListAliments(context) {
...
context.commit(...)
}

Dispatching a Redux Thunk action outside of a React component with TypeScript

I'm in the process of converting a React Native app into TypeScript and I'm having trouble with dispatching thunk actions outside of the store. Here is how my store is set up currently:
store/index.ts
import { createStore, applyMiddleware, combineReducers, Reducer, Store } from 'redux';
import thunk, { ThunkMiddleware } from 'redux-thunk';
export interface State { ... }
export interface ActionTypes { ... } // All of the non-thunk actions
const reducer: Reducer<State> = combineReducers({ ... });
export default (): Store<State> => {
return applyMiddleware(
thunk as ThunkMiddleware<State, ActionTypes>
)(createStore)(reducer);
}
index.tsx
import { Provider } from 'react-redux';
import createStore from './store/index';
import { registerStore } from './store/registry';
const store = createStore();
registerStore(); // Registers the store in store/registry.ts
AppRegistry.registerComponent(appName, () => () => (
<Provider store={store}>
<App />
</Provider>
));
store/registry.ts
import { Store } from 'redux';
import { State } from './index';
let store: Store<State>;
export const registerStore = (newStore: Store<State>) => {
store = newStore;
};
export const getStore = () => store;
So when the store is created, I'm storing it in the store registry so I can call getStore() from anywhere.
This works fine in components (where I'm not using the registry), for example in my App.tsx:
import { connect } from 'react-redux';
import { ThunkDispatch } from 'redux-thunk';
import { checkAuthStatus as checkAuthStatusAction } from './store/modules/auth/actions';
import { ActionTypes, State as AppState } from './store/index';
interface State = { ... }
interface StateProps { ... }
interface DispatchProps {
checkAuthStatus: () => Promise<boolean>;
}
type Props = StateProps & DispatchProps;
class App extends Component<Props, State> {
async componentDidMount() {
const promptSkipped: boolean = await checkAuthStatus(); // Thunk action, works fine!
}
...
}
const mapStateToProps = ...;
const mapDispatchToProps = (dispatch: ThunkDispatch<AppState, null, ActionTypes>): DispatchProps => ({
checkAuthStatus: () => dispatch(checkAuthStatusAction()),
});
export default connect<StateProps, DispatchProps, {}, AppState>(
mapStateToProps,
mapDispatchToProps,
)(App);
The problem comes when I want to use the registry to dispatch a thunk action:
lib/notacomponent.ts
import { getStore } from '../store/registry';
import { checkAuthStatus, setLoggedIn } from '../store/modules/auth/actions'
const someFunction = () => {
const store = getStore();
const { auth } = store.getState(); // Accessing state works fine!
store.dispatch(setLoggedIn(true)); // NON-thunk action, works fine!
store.dispatch(checkAuthStatus()); // Uh-oh, thunk action doesn't work.
}
This gives me the error:
Argument of type 'ThunkAction<Promise<boolean>, State, null, Action<any>>' is
not assignable to parameter of type 'AnyAction'.
Property 'type' is missing in type 'ThunkAction<Promise<boolean>, State, null, Action<any>>'
but required in type 'AnyAction'. ts(2345)
As far as I am aware, using thunk as ThunkMiddleware<State, ActionTypes> as a middleware allows Redux Thunk to replace the stores dispatch method with one that makes it possible to dispatch thunk actions and normal actions.
I think I need to somehow type the registry in a way that TypeScript can see that the dispatch method is not the default one that only allows normal actions. I am, however, at a loss on how to do this. I can't find any examples of anyone else doing the same thing.
Any help is appreciated.
Edit: The suggested duplicate of How to dispatch an Action or a ThunkAction (in TypeScript, with redux-thunk)?
doesn't solve my issue. I can dispatch thunk actions fine inside components. I'm only having issues outside components, using the store registry defined above.
Edit 2: So it seems I can use the following type assertion when dispatching the thunk action to get rid of the error:
(store.dispatch as ThunkDispatch<State, void, ActionTypes>)(checkAuthStatus())
That is very impractical though. I'm yet to find a way to make it so TypeScript knows that the dispatch method should always be able to dispatch a thunk action.
Your code is almost correct. You incorrectly set return type of your default export.
export default (): Store<State> => {
return applyMiddleware(
thunk as ThunkMiddleware<State, ActionTypes>
)(createStore)(reducer);
}
In case of middleware usage, function above should return not Store<State>, but Store<S & StateExt, A> & Ext where Ext will be overloaded dispatch which will be able to dispatch functions (as redux-thunk do).
To simplify, just remove exact return type and let TypeScript infer type itself
export default () => {
return applyMiddleware(
thunk as ThunkMiddleware<State, ActionTypes>
)(createStore)(reducer);
}
Thats solve your problem.
Alternatively, you may use more classical approach to store creation.
export default () => {
return createStore(reducer, applyMiddleware(thunk as ThunkMiddleware<State, ActionTypes>));
}
Essential things here:
Use createStore as recommended by Redux official doc. createStore called with middleware as second argument will itself call it. But TypeScript declaration files for redux and redux-thunk are preconfigured for such use of createStore. So returned store will have modified version of dispatch. (Make note of Ext and StateExt type parameters of StoreEnhancer<Ext, StateExt>. They will be intersected with resulting store and add overloaded version of dispatch which will accepts functions as arguments).
Also return type of default exported fucntion will be inferred from createStore's return type. It will NOT be Store<State>.
const boundActions = bindActionCreators( { checkAuthStatus }, store.dispatch);
boundActions.checkAuthStatus();
This works and does not look as hacky as 'Edit2'

How to mock an exported const in jest

I have a file that relies on an exported const variable. This variable is set to true but if ever needed can be set to false manually to prevent some behavior if downstream services request it.
I am not sure how to mock a const variable in Jest so that I can change it's value for testing the true and false conditions.
Example:
//constants module
export const ENABLED = true;
//allowThrough module
import { ENABLED } from './constants';
export function allowThrough(data) {
return (data && ENABLED === true)
}
// jest test
import { allowThrough } from './allowThrough';
import { ENABLED } from './constants';
describe('allowThrough', () => {
test('success', () => {
expect(ENABLED).toBE(true);
expect(allowThrough({value: 1})).toBe(true);
});
test('fail, ENABLED === false', () => {
//how do I override the value of ENABLED here?
expect(ENABLED).toBe(false) // won't work because enabled is a const
expect(allowThrough({value: 1})).toBe(true); //fails because ENABLED is still true
});
});
This example will work if you compile ES6 modules syntax into ES5, because in the end, all module exports belong to the same object, which can be modified.
import { allowThrough } from './allowThrough';
import { ENABLED } from './constants';
import * as constants from './constants';
describe('allowThrough', () => {
test('success', () => {
constants.ENABLED = true;
expect(ENABLED).toBe(true);
expect(allowThrough({ value: 1 })).toBe(true);
});
test('fail, ENABLED === false', () => {
constants.ENABLED = false;
expect(ENABLED).toBe(false);
expect(allowThrough({ value: 1 })).toBe(false);
});
});
Alternatively, you can switch to raw commonjs require function, and do it like this with the help of jest.mock(...):
const mockTrue = { ENABLED: true };
const mockFalse = { ENABLED: false };
describe('allowThrough', () => {
beforeEach(() => {
jest.resetModules();
});
test('success', () => {
jest.mock('./constants', () => mockTrue)
const { ENABLED } = require('./constants');
const { allowThrough } = require('./allowThrough');
expect(ENABLED).toBe(true);
expect(allowThrough({ value: 1 })).toBe(true);
});
test('fail, ENABLED === false', () => {
jest.mock('./constants', () => mockFalse)
const { ENABLED } = require('./constants');
const { allowThrough } = require('./allowThrough');
expect(ENABLED).toBe(false);
expect(allowThrough({ value: 1 })).toBe(false);
});
});
Unfortunately none of the posted solutions worked for me or to be more precise some did work but threw linting, TypeScript or compilation errors, so I will post my solution that both works for me and is compliant with current coding standards:
// constants.ts
// configuration file with defined constant(s)
export const someConstantValue = true;
// module.ts
// this module uses the defined constants
import { someConstantValue } from './constants';
export const someCheck = () => someConstantValue ? 'true' : 'false';
// module.test.ts
// this is the test file for module.ts
import { someCheck } from './module';
// Jest specifies that the variable must start with `mock`
const mockSomeConstantValueGetter = jest.fn();
jest.mock('./constants', () => ({
get someConstantValue() {
return mockSomeConstantValueGetter();
},
}));
describe('someCheck', () => {
it('returns "true" if someConstantValue is true', () => {
mockSomeConstantValueGetter.mockReturnValue(true);
expect(someCheck()).toEqual('true');
});
it('returns "false" if someConstantValue is false', () => {
mockSomeConstantValueGetter.mockReturnValue(false);
expect(someCheck()).toEqual('false');
});
});
There is another way to do it in ES6+ and jest 22.1.0+ thanks to getters and spyOn.
By default, you cannot spy on primitive types like boolean or number. You can though replace an imported file with your own mock. A getter method still acts like a primitive member but allows us to spy on it. Having a spy on our target member you can basically do with it whatever you want, just like with a jest.fn() mock.
Below an example
// foo.js
export const foo = true; // could be expression as well
// subject.js
import { foo } from './foo'
export default () => foo
// subject.spec.js
import subject from './subject'
jest.mock('./foo', () => ({
get foo () {
return true // set some default value
}
}))
describe('subject', () => {
const mySpy = jest.spyOn(subject.default, 'foo', 'get')
it('foo returns true', () => {
expect(subject.foo).toBe(true)
})
it('foo returns false', () => {
mySpy.mockReturnValueOnce(false)
expect(subject.foo).toBe(false)
})
})
Read more in the docs.
Thanks to #Luke I was able to expand on his answer for my needs. I had the requirements of:
Only mocking certain values in the file - not all
Running the mock only inside a single test.
Turns out that doMock() is like mock() but doesn't get hoisted. In addition requireActual() can be used to grab original data.
My config.js file - I need to mock only part of it
export const SOMETHING = 'blah'
export const OTHER = 'meh'
My test file
// import { someFunc } from 'some/file' // This won't work with doMock - see below
describe('My test', () => {
test('someFunc() does stuff', async () => {
// Here I mock the config file which gets imported somewhere deep in my code
jest.doMock('config.js', () => {
// Grab original
const originalModule = jest.requireActual('config')
// Return original but override some values
return {
__esModule: true, // Depends on your setup
...originalModule,
SOMETHING: 'boom!'
}
})
// Because `doMock` doesn't get hoisted we need to import the function after
const { someFunc } = await import(
'some/file'
)
// Now someFunc will use the original config values but overridden with SOMETHING=boom!
const res = await someFunc()
})
})
Depending on other tests you may also need to use resetModules() somewhere such as beforeAll or afterAll.
Docs:
doMock
requireActual
resetModules
Since we can't override/mock the value directly. we can use the below hack
// foo.js
export const foo = true; // could be expression as well
// spec file
import * as constants from './foo'
Object.defineProperty(constant, 'foo', {value: 1})
For functions:
Object.defineProperty(store, 'doOneThing', {value: jest.fn()})
For me the simplest solution was to redefine the imported object property, as decribed here:
https://flutterq.com/how-to-mock-an-exported-const-in-jest/
// foo.js
export const foo = true; // could be expression as well
// spec file
import * as constants from './foo'
Object.defineProperty(constant, 'foo', {value: 1, writable: true})
Facing the same issue, I found this blog post very useful, and much simpler than #cyberwombat use case :
https://remarkablemark.org/blog/2018/06/28/jest-mock-default-named-export/
// esModule.js
export default 'defaultExport';
export const namedExport = () => {};
// esModule.test.js
jest.mock('./esModule', () => ({
__esModule: true, // this property makes it work
default: 'mockedDefaultExport',
namedExport: jest.fn(),
}));
import defaultExport, { namedExport } from './esModule';
defaultExport; // 'mockedDefaultExport'
namedExport; // mock function
The most common scenario I needed was to mock a constant used by a class (in my case, a React component but it could be any ES6 class really).
#Luke's answer worked great for this, it just took a minute to wrap my head around it so I thought I'd rephrase it into a more explicit example.
The key is that your constants need to be in a separate file that you import, so that this import itself can be stubbed/mocked by jest.
The following worked perfectly for me.
First, define your constants:
// src/my-component/constants.js
const MY_CONSTANT = 100;
export { MY_CONSTANT };
Next, we have the class that actually uses the constants:
// src/my-component/index.jsx
import { MY_CONSTANT } from './constants';
// This could be any class (e.g. a React component)
class MyComponent {
constructor() {
// Use the constant inside this class
this.secret = MY_CONSTANT;
console.log(`Current value is ${this.secret}`);
}
}
export default MyComponent
Lastly, we have the tests. There's 2 use cases we want to handle here:
Mock the generate value of MY_CONSTANT for all tests inside this file
Allow the ability for a specific test to further override the value of MY_CONSTANT for that single test
The first part is acheived by using jest.mock at the top of your test file.
The second is acheived by using jest.spyOn to further spy on the exported list of constants. It's almost like a mock on top of a mock.
// test/components/my-component/index.js
import MyComponent from 'src/my-component';
import allConstants from 'src/my-component/constants';
jest.mock('src/my-component/constants', () => ({
get MY_CONSTANT () {
return 30;
}
}));
it('mocks the value of MY_CONSTANT', () => {
// Initialize the component, or in the case of React, render the component
new MyComponent();
// The above should cause the `console.log` line to print out the
// new mocked value of 30
});
it('mocks the value of MY_CONSTANT for this test,', () => {
// Set up the spy. You can then use any jest mocking method
// (e.g. `mockReturnValue()`) on it
const mySpy = jest.spyOn(allConstants, 'MY_CONSTANT', 'get')
mySpy.mockReturnValue(15);
new MyComponent();
// The above should cause the `console.log` line to print out the
// new mocked value of 15
});
One of the way for mock variables is the follow solution:
For example exists file ./constants.js with constants:
export const CONSTATN_1 = 'value 1';
export const CONSTATN_2 = 'value 2';
There is also a file of tests ./file-with-tests.spec.js in which you need to do mock variables.
If you need to mock several variables you need to use jest.requireActual to use the real values of the remaining variables.
jest.mock('./constants', () => ({
...jest.requireActual('./constants'),
CONSTATN_1: 'mock value 1',
}));
If you need to mock all variables using jest.requireActual is optional.
jest.mock('./constants', () => ({
CONSTATN_1: 'mock value 1',
CONSTATN_2: 'mock value 2'
}));
Instead of Jest and having trouble with hoisting etc. you can also just redefine your property using "Object.defineProperty"
It can easily be redefined for each test case.
This is a pseudo code example based on some files I have:
From localization file:
export const locale = 'en-US';
In another file we are using the locale:
import { locale } from 'src/common/localization';
import { format } from 'someDateLibrary';
// 'MMM' will be formatted based on locale
const dateFormat = 'dd-MMM-yyyy';
export const formatDate = (date: Number) => format(date, dateFormat, locale)
How to mock in a test file
import * as Localization from 'src/common/localization';
import { formatDate } from 'src/utils/dateUtils';
describe('format date', () => {
test('should be in Danish format', () => {
Object.defineProperty(Localization, 'locale', {
value: 'da-DK'
});
expect(formatDate(1589500800000)).toEqual('15-maj-2020');
});
test('should be in US format', () => {
Object.defineProperty(Localization, 'locale', {
value: 'en-US'
});
expect(formatDate(1589500800000)).toEqual('15-May-2020');
});
});
in typescript, you can not overwrite constant value but; you can overwrite the getter function for it.
const mockNEXT_PUBLIC_ENABLE_HCAPTCHAGetter = jest.fn();
jest.mock('lib/constants', () => ({
...jest.requireActual('lib/constants'),
get NEXT_PUBLIC_ENABLE_HCAPTCHA() {
return mockNEXT_PUBLIC_ENABLE_HCAPTCHAGetter();
},
}));
and in the test use as
beforeEach(() => {
mockNEXT_PUBLIC_ENABLE_HCAPTCHAGetter.mockReturnValue('true');
});
Thank you all for the answers.
In my case this was a lot simpler than all the suggestions here
// foo.ts
export const foo = { bar: "baz" };
// use-foo.ts
// this is just here for the example to have a function that consumes foo
import { foo } from "./foo";
export const getFoo = () => foo;
// foo.spec.ts
import "jest";
import { foo } from "./foo";
import { getFoo } from "./use-foo";
test("foo.bar should be 'other value'", () => {
const mockedFoo = foo as jest.Mocked<foo>;
mockedFoo.bar = "other value";
const { bar } = getFoo();
expect(bar).toBe("other value"); // success
expect(bar).toBe("baz"); // fail
};
Hope this helps someone.
../../../common/constant/file (constants file path)
export const Init = {
name: "",
basePath: "",
description: "",
thumbnail: "",
createdAt: "",
endDate: "",
earnings: 0,
isRecurring: false,
status: 0,
};
jest file
jest.mock('../../../common/constant/file',()=>({
get Init(){
return {isRecurring: true}
}
}))
it('showActionbutton testing',()=>{
const {result} = renderHook(() => useUnsubscribe())
expect(result.current.showActionButton).toBe(true)
})
index file
import {Init} from ../../../common/constant/file
const useUsubscribe(){
const showActionButton = Init.isRecurring
return showActionButton
}
I solved this by initializing constants from ContstantsFile.js in reducers. And placed it in redux store. As jest.mock was not able to mock the contstantsFile.js
constantsFile.js
-----------------
const MY_CONSTANTS = {
MY_CONSTANT1: "TEST",
MY_CONSTANT2: "BEST",
};
export defualt MY_CONSTANTS;
reducers/index.js
-----------------
import MY_CONST from "./constantsFile";
const initialState = {
...MY_CONST
}
export const AbcReducer = (state = initialState, action) => {.....}
ABC.jsx
------------
import { useSelector } from 'react-redux';
const ABC = () => {
const const1 = useSelector(state) => state. AbcReducer. MY_CONSTANT1:
const const2 = useSelector(state) => state. AbcReducer. MY_CONSTANT2:
.......
Now we can easily mock the store in test.jsx and provide the values to constant that we want.
Abc.text.jsx
-------------
import thunk from 'redux-thunk';
import configureMockStore from 'redux-mock-store';
describe('Abc mock constants in jest', () => {
const mockStore = configureMockStore([thunk]);
let store = mockStore({
AbcReducer: {
MY_CONSTANT1 ="MOCKTEST",
MY_CONSTANT2 = "MOCKBEST",
}
});
test('your test here', () => { .....
Now when the test runs it will always pick the constant value form mock store.

How to keep user logged in (Aka, state persistency) in react-native

I am trying to keep a user logged in in my application. I tried several techniques but i have no idea on how i can read data back into the state when the application launches.
Right now i have the following:
const getInitialState = () => {
var _initState = {
auth: new AuthInitialState(),
global: (new GlobalInitialState())
};
return _initState;
};
export default function configureStore() {
const store = createStoreWithMiddleware(reducer, load(APP_STORAGE) || getInitialState());
store.subscribe(() => {
if(!load('debug')) {
save(APP_STORAGE, store.getState());
}
});
return store;
};
const createStoreWithMiddleware = applyMiddleware(
thunk,
localStorageMiddleware,
logger
)(createStore)
In which the load and save methods are responsible for saving data to an AsyncStorage (using react-native-simple-store)
export const load = (key) => {
return store.get(key);
}
export const save = async (key, data) => {
store.save(key, JSON.stringify(data));
}
The render of my root is the current:
render() {
const store = configureStore();
return (
<Provider store={store}>
<MyApp/>
</Provider>
);
}
The data is being correctly saved (through the save subscriber) but it is not correctly reloaded on a hot reload or app relaunch. Thus my user ends up being logged out every time.
In the end i would also like to apply this technique to navigate to the correct page upon app startup.
Any recommendations on how i can approach this?
You can use redux-persist to achieve this:
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, autoRehydrate } from 'redux-persist';
import { AsyncStorage } from 'react-native';
export default function configureStore() {
const store = createStore(reducers, getInitialState(), compose(
applyMiddleware([
thunk,
localStorageMiddleware,
logger
]),
autoRehydrate()
)
);
persistStore(store, { storage: AsyncStorage });
return store;
};
With this, each time your application load, the store is hydrated from the local storage. You don't have to deal with the AsyncStorage, everything is done automatically for you. You can read the docs of redux-persist to customize it per your needs (add a whitelist, a blacklist, a callback when the store is rehydrated..)
Your basic approach looks good to me.
However, react-native-simple-store stringifies the state for you. As you run JSON.stringify() in your save function as well, it will not properly get decoded when it is loaded during the next start of your app.
See react-native-simple-store's codebase for more details.
To resolve this, remove JSON.stringify() from your save function:
export const save = async (key, data) => {
store.save(key, data);
}

Categories