
Automated tests are good. The project, which is 100% covered with tests, presents coverage as an advantage. But…
I think that in this process there is no awareness. And it makes life much easier. It is possible that half of the tests in your project are not only useless, moreover, it is harmful. Below I will talk about what tests do not need to write.
Consider a component on ReactJS:
')
class FilterForm extends React.Component { constructor(props) { super(props); this.state = { value: '', frameworks: ['react', 'angular', 'ember', 'backbone'] }; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } render() { const filteredElements = this.state.frameworks .filter(e => e.includes(this.state.value)) .map(e => <li>{ e }</li>) return ( <div> <input type="text" value={this.state.value} onChange={this.handleChange} /> <ul>{ filteredElements }</ul> </div> ); } }
Working exampleTests for this may look something like this:
test('should work', () => { const wrapper = shallow(<FilterForm />); expect(wrapper.find('li').length).to.equal(4); wrapper.find('input').simulate('change', {target: {value: 'react'}}); expect(wrapper.find('li').length).to.equal(1); });
Let's think what this code is testing. It is important. And he tests by and large this line:
.filter(e => e.includes(this.state.value))
Of course, we also test how ReactJS renders changes and how events are hung on the DOM, how they are processed and so on. But this line is clearly the main one.
Do we really need to test the entire component? With this approach to writing code, we can not otherwise. We have no choice.
In a specific example, this causes no problems, but almost always the components become much more complex over time. Logic and DOM components are moved from one component to another.
For example, the next step makes sense to break one component into two: an input form and a sheet. Each element of the sheet also makes sense to make a separate component. After such changes will have to change the tests. Rather, do not change, and throw out and write again. They will become useless because they are tied to the DOM. The question arises: “Why are such tests needed !?”
Tests that need to be changed after each change in the code are useless and even harmful because they require maintenance costs and do not bring any benefit.
Of course, you can use some kind of abstraction, such as
PageObject (eng), but the problem will not be completely solved.
The fact is that the component itself is written poorly. Filtering code cannot be reused. If we need filtering in another component, then with this approach we will have to write and test everything anew. The principle of
uniqueness of responsibility has been violated.
In addition to the logic for output in the component, there is also logic for filtering. The solution is, and it is
very simple . You can transfer the code that is responsible for filtering to another place and test there.
export function filter(list, value){ return list.filter(e => e.includes(value)) } expect(filter(list, 'react').length).to.equal(1);
Now we do not need special tools and component rendering. Change components as you like, tests will remain relevant and will help you with each commit.
If you really want, you can test the component itself. But what does it give? The question is open.
The conclusion suggests itself: The difficulty of writing tests is a sign of poor design, a reason to think again about the architecture of the code. Instead of dodging and writing unsupported tests with rendering in a pseudo-browser, it is easier to put things in order and write simple clear unit tests that further document your application.