Stack Overflow Asked by Ninita on January 16, 2021
I’m writing some tests for my component but I’m facing some troubles here…
Here is my Game component:
import React from 'react';
import User from './User';
import Board from './Board';
import Deck from './Deck';
class Game extends React.Component {
constructor(props) {
super(props);
this.board = new Board();
this.deck = new Deck();
//some more code
}
componentDidMount() {
this.initializeUser("xpto");
//some more code
}
//some more code
initializeUser(name) {
const user = new User(name, this.deck);
//some more code
user.pickCards();
//some more code
}
//some more code
render() {
return (
<div className="game-container">
something to show
<div id="deck"></div>
</div>
);
}
}
Game.propTypes = {
};
Game.defaultProps = {
};
export default Game;
My Board class:
export default class Board {
//some code
}
My Deck class:
export default class Deck {
constructor(props) {
//some more code
this.cardsLeft = 52;
this.lastPick = 0;
//some more code
}
pickCards() {
this.lastPick = 4;
this.cardsLeft -= this.lastPick;
const deckElem = document.getElementById("deck");
deckElem.innerHTML = this.cardsLeft;
return this.lastPick;
}
//some more code
}
My User class:
class User {
constructor(name, deck) {
this.name = name;
this.tableDeck = deck;
this.cards = 0;
//some more code
}
//some more code
pickCards() {
const newCards = this.tableDeck.pickCards();
this.cards += newCards;
//some code
}
//some more code
}
export default User;
Now, at my tests I’m trying to test if the Board
and User
are called and if the pickCards()
is called too.
Here are my tests:
import React from 'react';
import { mount } from 'enzyme';
import Game from './Game';
import User from './User';
import Board from './Board';
describe('Game start', () => {
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it("test where I'm having problems", () => {
const boardSpy = jest.spyOn(Board, 'constructor'),
userSpy = jest.spyOn(User, 'constructor'),
pickCardMock = jest.fn();
User.pickCard = pickCardMock;
const wrapper = mount(<Game />, { attachTo: container });
expect(boardSpy).toHaveBeenCalledTimes(1);
expect(userSpy).toHaveBeenCalledTimes(1);
expect(pickCardMock).toHaveBeenCalledTimes(1);
//some more code
});
it("example test where I need to test everything without mocks", () => {
const wrapper = mount(<Game />, { attachTo: container });
expect(wrapper.find("#deck").text()).toEqual('48');
//some code
});
//some more tests
});
I don’t want to mock Board
and User
because I need everything work normally on it. But I want to spy them to check if they were really called. And I want to mock pickCard()
from User
.
I already tried to use jest.mock('./board');
and require('board')
(for example) only inside my it()
test but it didn’t work. And now I’m trying to spy the components constructors.
But the expect(boardSpy).toHaveBeenCalledTimes(1)
fails saying that was called 0 times and not 1 time.
And the pickCardMock
seems not being linked to the User
module because when debugging pickCard
is a normal function and not a mock function and expect(pickCardMock).toHaveBeenCalledTimes(1)
receives 0 too instead of 1.
Anyone knows how to solve these two problems (spy a module and mock a function in another module)?
Again:
I don’t want to mock things for the whole test suite, I just want to
mock for a single test (and I want to be able to spy a module call).
You can find all this code here.
In case you haven't tried this yet:
You can spy on User.pickCard
with jest.spyOn(User.prototype, 'pickCard')
.
Another option for spying on Board
and User
while keeping the original implementation in place is with jest.requireActual
:
import Board from './Board';
jest.mock('./Board', () => {
const BoardSpy = jest.requireActual('./Board').default;
return {
default: jest.fn((...args) => new BoardSpy(...args)),
__esModule: true,
};
});
it('instantiates a Board', () => {
const board = new Board();
expect(Board).toBeCalled();
});
Correct answer by helloitsjoe on January 16, 2021
It looks like you are trying to do both unit testing and integration testing within the same test file - best practice recommends that you write separate files for them. That alone would solve your problem.
Also, in your initializeUser
method, you are creating a User
, calling pickCards
, and then... It doesn't look like you are storing the variable anywhere. Do you not need access to that user
variable again?
EDIT: removed mistake in answer.
Answered by Jake Stokes on January 16, 2021
You can use jest.mock(moduleName, factory, options) to mock User
and Board
components.
E.g.
Game.jsx
:
import React, { Component } from 'react';
import User from './User';
import Board from './Board';
class Game extends Component {
constructor(props) {
super(props);
this.board = new Board({});
}
initializeUser(name) {
const user = new User(name);
user.pickCards();
}
render() {
return <div className="game-container"></div>;
}
}
export default Game;
Board.jsx
:
import React, { Component } from 'react';
class Board extends Component {
constructor(props) {
super(props);
}
render() {
return 'Board';
}
}
export default Board;
User.jsx
:
import React, { Component } from 'react';
class User extends Component {
constructor(props) {
super(props);
this.name = this.props.name;
}
pickCards() {
console.log('pick cards real implementation');
}
render() {
return 'User';
}
}
export default User;
Game.test.jsx
:
import React from 'react';
import { mount } from 'enzyme';
import Game from './Game';
import BoardMock from './Board';
import UserMock from './User';
jest.mock('./User', () => {
const mUser = { pickCards: jest.fn() };
return jest.fn(() => mUser);
});
jest.mock('./Board', () => jest.fn());
describe('62199135', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should pass', () => {
const userMock = new UserMock();
const wrapper = mount(<Game />);
const gameInstance = wrapper.instance();
gameInstance.initializeUser('some name');
expect(BoardMock).toHaveBeenCalledTimes(1);
expect(UserMock).toHaveBeenCalledWith('some name');
expect(userMock.pickCards).toHaveBeenCalledTimes(1);
});
});
You can reset all mocks after running all test cases.
Unit test result:
PASS stackoverflow/62199135/Game.test.jsx (10.16s)
62199135
✓ should pass (33ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
Game.jsx | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.474s, estimated 12s
Answered by slideshowp2 on January 16, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP