Redux reducer unit test - unit-testing

And I'm trying to make unit-test for redux reducer. But i'm struggling with receiving the same expected and equal results.
const initState = {};
export default function (state = initState, action) {
const newState = { ...state };
switch (action.type) {
case CREATE_TYPE:
return {
...state,
[action.customType.id]: {
...action.customType
}
};
default:
return state;
}
}
My test. Seems that problem in customType: { id: 'test-id' }
describe('reducer', () => {
it('should return the initial state', () => {
const state = reducer(undefined, { type: 'unknown' });
expect(state).toEqual({});
});
it('should handle CREATE_TYPE', () => {
expect(reducer({ test: true }, {
type: CREATE_TYPE,
customType: { id: 'test-id' },
id: 'test-id'
})).toEqual({
'test-id': 'test-type',
'test': true
});
});
});

Often it helps to spell everything out.
That way you can clearly understand what your expected outcome needs to be.
it('should handle CREATE_TYPE', () => {
const initialState = { test: true };
const customType = { id: 'test-id' };
const action = {
type: CREATE_TYPE,
customType: customType,
id: customType.id
};
const result = reducer(initialState, action);
const expectedResult = {
test: true,
[customType.id]: customType
};
expect(result).toEqual(expectedResult);
});
It then becomes easier to see exactly where the issue lies.

You are exprecting back from your reducer :
{
...state,
[action.customType.id]: {
...action.customType
}
which if you send
{
type: CREATE_TYPE,
customType: { id: 'test-id' },
id: 'test-id'
}
Will equate itself to :
{
...state,
'test-id' : { id: 'test-id' }
}
and you are evaluating it equal to
{
...state,
'test-id': 'test-type'
}
I don't know how you are expecting to format your state via your reducer - but the way your reducer is set up now will not provide the state you are expecting. I don't know what you are expecting because i don't see the node or value 'test-type' anywhere else in your provided code. It looks like you just have some syntactical errors maybe?

Related

(Apollo client v3) useLazyQuery custom hook testing

I was struggling with a test issue for my custom useLazyQuery hook. My first test is passing but the second one is failing. What am doing wrong for the second test?
Here is the useLazyFetchCoin.tsx
export const useLazyFetchCoin = () => {
const [coins, setCoins] = useState<ICoin[]>([]);
useEffect(() => {
const coins = localStorage.getItem('coinsInfo');
if (coins) {
setCoins(JSON.parse(coins));
}
}, []);
const [getData, { loading, error }] = useLazyQuery(GET_COIN_PRICE_QUERY, {
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
onCompleted: (data) => {
const hasSameCoin = coins.some((f) => f.id === data.markets[0]?.id);
if (data.markets.length && !hasSameCoin) {
const allCoins = [...coins, data.markets[0]];
setCoins(allCoins);
localStorage.setItem('coinsInfo', JSON.stringify(allCoins));
} else if (data.markets.length <= 0) {
alertNotification('Coin not found !', Notification.ERROR);
}
if (hasSameCoin) {
alertNotification('This coin already exists on your list !', Notification.WARNING);
}
}
});
return { coins, setCoins, getData, loading, error };
};
Here is the test file
describe('useLazyFetchCoin custom hook', () => {
const QueryMock = [
{
request: {
query: GET_COIN_PRICE_QUERY,
variables: { code: 'BNB' }
},
result: {
data: {
markets: [
{
id: 'binance_bnb_eur',
baseSymbol: 'BNB',
ticker: {
lastPrice: '414.90000000'
}
}
]
}
}
}
];
const QueryWrongCodeMock = [
{
request: {
query: GET_COIN_PRICE_QUERY,
variables: { code: 'asd' }
},
result: {
data: {
markets: []
}
}
}
];
function getHookWrapper(mocks: any, code: string) {
const wrapper = ({ children }: any) => (
<MockedProvider mocks={mocks} addTypename={false}>
{children}
</MockedProvider>
);
const { result, waitForNextUpdate } = renderHook(() => useLazyFetchCoin(), {
wrapper
});
expect(typeof result.current.coins).toBe('object');
expect(result.current.loading).toBeFalsy();
expect(result.current.error).toBeUndefined();
// call the lazy function
act(() => {
result.current.getData({
variables: { code }
});
});
return { result, waitForNextUpdate };
}
it('should return an array of coins', async () => {
// Working correctly
const { result, waitForNextUpdate } = getHookWrapper(QueryMock, 'BNB');
await waitForNextUpdate();
expect(result.current.loading).toBeFalsy();
expect(result.current.coins[0]).toEqual({
id: 'binance_bnb_eur',
baseSymbol: 'BNB',
ticker: {
lastPrice: '414.90000000'
}
});
});
it('should return an empty array when requesting a wrong code', async () => {
// Not working
const { result, waitForNextUpdate } = getHookWrapper(QueryWrongCodeMock, 'asd');
await waitForNextUpdate();
expect(result.current.loading).toBeFalsy();
expect(result.current.coins[0]).toEqual([]);
});
});
I got this error message for the second test.
Expected: []
Received: {"baseSymbol": "BNB", "id": "binance_bnb_eur", "ticker": {"lastPrice": "414.90000000"}}
I don't get it because I'm using different queries for each test.
Also, the second test should receive an empty array when you pass a wrong code such as 'asd'.
How can write a proper test for it?
I fixed the problem. When I was changing the order test, It worked correctly.
I added a clear mock function for it.
clear mock

Jest, expected mock function to have been called, but it was not called

Testing lifecycle methods when a VueJS component renders on the transition group.
I've been writing tests for lifecycle methods when the component renders on the transition group of the following VueJS component I've made little progress on getting it to work and would appreciate advice regarding this. I also tried switching between shallow mounting and mounting the component though that seemed to make no difference.
import { shallowMount } from '#vue/test-utils';
import StaggeredTransition from '../src/index';
const staggeredTransitionWrapper = componentData =>
shallowMount(StaggeredTransition, {
...componentData,
});
const staggeredTransition = staggeredTransitionWrapper();
describe('StaggeredTransition.vue', () => {
it('should render a staggered transition component', () => {
expect(staggeredTransition.element.tagName).toBe('SPAN');
expect(staggeredTransition.html()).toMatchSnapshot();
});
it('should mock calling the enter method', () => {
const enterMock = jest.fn();
StaggeredTransition.methods.enter = enterMock;
const staggeredTransitionWrapper2 = componentData =>
shallowMount(StaggeredTransition, { ...componentData });
const staggeredTransition2 = staggeredTransitionWrapper2({
slots: {
default: '<h1 :key="1">Staggered transition test</h1>',
},
});
expect(enterMock).toBeCalled();
});
});
Code for the StaggeredTransition component
<template>
<transition-group
:tag="tag"
:name="'staggered-' + type"
:css="false"
appear
#before-enter="beforeEnter"
#enter="enter"
#leave="leave"
>
<slot />
</transition-group>
</template>
<script>
const { log } = console;
export default {
name: 'StaggeredTransition',
props: {
type: {
type: String,
options: ['fade', 'slide'],
required: false,
default: 'fade',
},
tag: {
type: String,
required: false,
default: 'div',
},
delay: {
type: Number,
required: false,
default: 100,
},
},
methods: {
beforeEnter(el) {
console.log('beforeEnter');
el.classList.add(`staggered-${this.type}-item`);
},
enter(el, done) {
console.log('enter');
setTimeout(() => {
el.classList.add(`staggered-${this.type}-item--visible`);
done();
}, this.getCalculatedDelay(el));
},
leave(el, done) {
console.log('leave');
setTimeout(() => {
el.classList.remove(`staggered-${this.type}-item--visible`);
done();
}, this.getCalculatedDelay(el));
},
getCalculatedDelay(el) {
console.log('getCalculatedDelay');
if (typeof el.dataset.index === 'undefined') {
log(
'data-index attribute is not set. Please set it in order to
make the staggered transition working.',
);
}
return el.dataset.index * this.delay;
},
},
};
</script>

Saga unit test deepEqual returns false anyway

I am pretty much new with Redux-saga and working on unit test. API call function unit test code returns false even if it looks identical. What am I missing?
[Actions]
export const actions = {
readUsers: {
read: (data) => util.createAction('READ_USER', data),
request: () => util.createAction('READ_REQUEST'),
success: (data: any) => util.createAction('READ_SUCCESS', data),
failure: (error: any) => util.createAction('READ_FAILURE', error)
}
};
[Generator function]
export function* fetchUsers(action) {
yield put(actions.readUsers.request());
try {
let data = {id: action.payload.id, option: action.payload.option};
let response = yield call(API().getUsers, data);
if (response) {
yield put(actions.readUsers.success(response.users));
} else {
yield put(actions.readUsers.failure(response.error));
}
} catch (error) {
yield put(actions.readUsers.failure(error));
}
};
[Unit test]
const id = 'user-id';
const option = {};
const result = {users: []};
const action = {payload : {id, option}};
describe('user-saga', () => {
describe('fetchUsers', () => {
const generator = fetchUsers(action);
it('should return actions.readUsers.request()', () => {
assert.deepEqual(generator.next().value, put(actions.readUsers.request()));
}); // PASS
it('should return the Api.getUsers call', () => {
assert.equal(generator.next(result).value, call(Api().getUsers, action.payload));
}); // FAIL
it('should return actions.readUsers.success()', () => {
assert.deepEqual(generator.next(result).value, put(actions.readUsers.success(result)));
}); // FAIL
it('should be finished', () => {
assert.deepEqual(generator.next().done, true);
}); // PASS
})
});
[Test output]
First test passed.
Second test failed with this error message
AssertionError: { '##redux-saga/IO': true,
CALL: { context: null, fn: [Function: getUsers], args: [ [Object] ] } } deepEqual { '##redux-saga/IO': true,
CALL: { context: null, fn: [Function: getUsers], args: [ [Object] ] } }
Third test failed with this error message
AssertionError: undefined deepEqual { '##redux-saga/IO': true,
PUT: { channel: null,
action: { type: 'READ_SUCCESS', users: undefined } } }

Unit testing the instance of Sequelize Model

I have the following code:
async save(id: string) {
const person = await PersonModel.findOne({
where: { id: id },
});
if (!person) {
await PersonModel.create({
id: '2345',
name: 'John Doe',
age: 25
});
return;
}
await person.increment({ age: 15 });
}
Now, I wanted to test person.increment() in which the age will be added with 15. I have the following code to escape the condition that will create a new record for the model.
const findOneFake = sinon.spy(() => {
return {}; //returns empty object or true
});
const proxy = (proxyquire('./path/to/file.ts', {
'./path/to/PersonModel.ts': {
default: {
findOne: findOneFake
}
}
})).default;
beforeEach(async () => {
await save();
});
it('should increment age with 15');
How am I going to do that? What do I do to test it? I can use sinon.fake() to PersonModel.create or PersonModel.update but I am troubled testing the instance of a Sequelize Model.

Apollo: Update React Props on Subscription Update?

Looking at the Apollo docs example code for subscriptions, I am not yet seeing how to update the React props with the subscription results.
From http://dev.apollodata.com/react/subscriptions.html:
Here is a regular query:
import { CommentsPage } from './comments-page.js';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
const COMMENT_QUERY = gql`
query Comment($repoName: String!) {
entry(repoFullName: $repoName) {
comments {
id
content
}
}
}
`;
const withData = graphql(COMMENT_QUERY, {
name: 'comments',
options: ({ params }) => ({
variables: {
repoName: `${params.org}/${params.repoName}`
},
})
});
export const CommentsPageWithData = withData(CommentsPage);
Now, let’s add the subscription.
Note that this sample code appears to leave out this part of the props code for usual queries - from http://dev.apollodata.com/react/queries.html:
props: ({ ownProps, data: { loading, currentUser, refetch } }) => ({
userLoading: loading,
user: currentUser,
refetchUser: refetch,
}),
...which AFAIK is the correct way to update the data props on my React component and trigger a page refresh.
Here is the complete subscription code sample from http://dev.apollodata.com/react/subscriptions.html:
const withData = graphql(COMMENT_QUERY, {
name: 'comments',
options: ({ params }) => ({
variables: {
repoName: `${params.org}/${params.repoName}`
},
}),
props: props => {
return {
subscribeToNewComments: params => {
return props.comments.subscribeToMore({
document: COMMENTS_SUBSCRIPTION,
variables: {
repoName: params.repoFullName,
},
updateQuery: (prev, {subscriptionData}) => {
if (!subscriptionData.data) {
return prev;
}
const newFeedItem = subscriptionData.data.commentAdded;
return Object.assign({}, prev, {
entry: {
comments: [newFeedItem, ...prev.entry.comments]
}
});
}
});
}
};
},
});
How do I get the code shown here, to update the data props on my React component and trigger a page refresh, when the results come in from the non-subscription query COMMENT_QUERY?
Thanks to #neophi on the Apollo Slack for this answer!
const withDataAndSubscription = graphql(GETIMS_QUERY, {
options({toID}) {
console.log(GETIMS_QUERY);
const fromID = Meteor.userId();
return {
fetchPolicy: 'cache-and-network',
variables: {fromID: `${fromID}`, toID: `${toID}`}
};
}
,
props: props => {
return {
loading: props.data.loading,
instant_message: props.data.instant_message,
subscribeToMore: props.data.subscribeToMore,
subscribeToNewIMs: params => {
const fromID = Meteor.userId();
const toID = params.toID;
return props.data.subscribeToMore({
document: IM_SUBSCRIPTION_QUERY,
variables: {fromID: `${fromID}`, toID: `${toID}`},
updateQuery: (previousResult, {subscriptionData}) => {
if (!subscriptionData.data) {
return previousResult;
}
const newMsg = subscriptionData.data.createIM;
return update(previousResult, {
instant_message: {
$push: [newMsg],
},
});
}
});
}
};
},
})
;