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
TodoContainerand assert that..a. it has a working
addTodoMethodwhich updates the state accordinglyb. it passes the
addTodoMethodits child composite component,AddTodoshallow render
AddTodoand assert that..a. it has received the
addTodoMethodas propb. it fires the
addTodoMethodon 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
TodoContainerto assert that..a. it can add a todo item thru inputting new to do text and clicking button (both in
AddTodocomponent), the new todo item is also asserted thru transversing toTodoListcomponent and check if it has the correct number ofliwith 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.