Please let me illustrate. Let's say we have this to do app:
// TodoContainer.jsx
<TodoContainer>
<AddTodo />
<TodoList />
</TodoContainer>
My question is how should I test its add todo
functionality? Is it enough to..
Approach #1
shallow render TodoContainer
and assert that..
a. it has a working addTodoMethod
which updates the state accordingly
b. it passes the addTodoMethod
its child composite component, AddTodo
shallow render AddTodo
and assert that..
a. it has received the addTodoMethod
as prop
b. it fires the addTodoMethod
on button click
This is how approach #1 would look like:
// TodoContainer.spec.jsx
describe('<TodoContainer />', function() {
describe('addTodoMethod', function() {
beforeEach(function() {
this.todoContainer = shallow(<TodoContainer />)
})
it('adds todo to todos state when called', function() {
const todos = this.todoContainer.instance().state('todos')
this.todoContainer.instance().addToDoMethod('sample todo item')
expect(todos).length.to.be(1)
expect(todos[0]).to.be('sample to do item')
})
it('is passed do AddTodo component', function() {
const addTodo = shallow(<AddTodo />)
expect(addTodo.instance().prop('onClick')).to.eql.(this.todoContainer.addTodoMethod)
})
})
})
// TodoContainer.spec.jsx
describe('<AddTodo />', function() {
beforeEach(function() {
this.addTodo = shallow(<AddTodo />)
})
it('has onClick prop', function() {
expect(this.addTodo).to.be.a('function')
})
it('calls onClick when clicked', function() {
const onClickSpy = spy();
const addTodo = shallow(<AddTodo onClick={onClickSpy} />)
addTodo.setState({ newTodoText: 'sample new todo' })
addButton.find('button').simulate('click');
expect(onClickSpy.calledOnce()).to.be.true
expect(onClickSpy.calledWith('sample new todo')).to.be.true
})
})
or should I also do this next approach?
Approach #2
in addition to assertions above , also mount the TodoContainer
to assert that..
a. it can add a todo item thru inputting new to do text and clicking button (both in AddTodo
component), the new todo item is also asserted thru transversing to TodoList
component and check if it has the correct number of li
with their correct content.
Code for approach #2:
// TodoContainer.spec.jsx
describe('<TodoContainer />', function() {
...
it('can add a todo item', function() {
this.todoContainer.find('input').simulate('change', {target: {value: 'sample new todo 01'}})
this.todoContainer.find('button').simulate('click')
expect(this.todoContainer).to.have.exactly(1).descendants('li')
expect(this.todoContainer.find('li')[0]).to.have.text('sample new todo')
this.todoContainer.find('input').simulate('change', {target: {value: 'sample new todo 02'}})
this.todoContainer.find('button').simulate('click')
expect(this.todoContainer).to.have.exactly(2).descendants('li')
expect(this.todoContainer.find('li')[1]).to.have.text('sample new todo 02')
})
})
I'm just thinking if the second approach is kinda redundant, and if it is a better practice to focus only on testing the component at hand and letting the child components rely on their own tests.
In short, yes - you can write all of your unit tests with shallow
. And in most cases, you probably want exactly that. There are some times however when there's an exception where you'd want to use mount
instead:
As a general rule of thumb, though, I tend to reach for shallow
for unit tests, mount
for integration tests.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.