“Quality is free, but only to those who are willing to pay heavily for it.”

– T. DeMarco and T. Lister

Nowadays writing good tests for your application is the crucial part of modern software development. Applications with good test coverage have less bugs and are much easier to change and understand.

Ideally, the well-tested application should have 3 kinds of tests – unit, integration, and E2E tests in proportion described as  “TestPyramid” .

 

new-piktochart_17149863_ed1d7420e45e9101f143ec1580f38eea085e7247.png

Testing pyramid

On another side, to be effective tests should deliver 3 major qualities:

  • Fidelity
    If code under test is broken, that test should fail.
  • Resilience
    If code under test is working, test verifying it should pass.
  • Precision
    Failed tests should precisely tell what is the problem and where it is located.

You can read more about these qualities in awesome art Testing on the Toilet: Effective Testing.

Let’s take a deeper look how different kinds of test help to deliver these qualities.

End 2 End tests

Here we some to the very top of out pyramid – E2E tests. This kind of application testing verifies if some feature of the application is working or not. but they have very high fidelity so they are very fragile and easy to break.

E2E tests are often implemented as UI tests simulating user interaction with the application (Selenium Web Driver for example) or as API tests to do E2E API testing (NightfwatchjsRest Assured).

It might seem to be very appealing to write a lot of E2E tests because they seem to be cheap – after running one test you will know if app works or not. Wow! Actually not so fast!

The trick is that E2E tests are extremely hard to debug and absolutely awful on narrowing to the actual problem. In the best case, you will get something like “Selector was not found, Screw you!”.

Some smart people advice in course testing on the toilet  to follow these rules writing good E2E tests:

  • Have one E2E test for each major use-case
    Rest should be covered by integration and unit tests.
  • Use debug logging while executing E2E tests.
    It provides additional feedback and helps to narrow down the problem.
  • Focus on checking system behavior, but not CSS and implementation details
  • Allocate time to fix E2E tests :troll

Unit tests

Checking if specific class works as expected, Unit tests usually operate on the code level. Well-written applications treat unit tests as necessary explanatory part of the code base.

Another big advantage of unit testing is the high speed of execution. Such tests can be executed very often checking code for possible errors. It’s extremely necessary for languages with no strict types, like Javascript or Python. Since we don’t have a compiler that checks types for us, we should rely on good test coverage.

They also help to achieve high precision and narrow the problem in the code. It’s far easier to read the message about unexpected return type in specific function or class than figuring out roots of “UserNotSavedException“.

Tools are more language and framework specific, so it’s not a big deal to choose the right tool for unit testing that fits for you.

Integration tests

Unit tests have a lot of advantages, but they still don’t solve one big problem – making sure that all parts of the application work well together.

It’s a quite common scenario when all the tests are green but the application still doesn’t work because of a mistake in gluing different parts together.

Integration tests might be more time consuming since they involve fake systems to interact, like in-memory databases, message queues, external services.

This kind of testing can be used to validate if some par to the system is working as expected after a change, for example, refactoring of the module, or changing a library.

Summary

I hope that you found article helpful and learned something new about writing good tests. If you have something to add, please don’t hecitate to comment, if you liked the post, please subscribe and share the article.