The points I'm going to discuss here helped me a lot to become more aware of my tests and what I can do to improve them. If you have experience in writing tests, you probably know most of what I'm going to talk about, but it's still good to refresh your memory and add more references on the subject. If you have just started writing tests and are looking for ways to become even better, then you came to the right place!
Before starting, I would like to warn you that this is not an extensive list with all you need to know to become an expert on writing tests, but I tried my best to put in useful references so that you can keep researching even further on each topic. Bookmark this article and come back from time to time and try to invest some of your time in learning more about one specific area.
#1 Treat Test Code as Production Code
We often hear people talking about how important it is to have a clean code. The same should apply to test code. The poor feedback you get out of a dirty test suite doesn’t let you know when you actually broke something or if you just need to re-run them.
This article from Robert Martin (Uncle Bob) has a very good discussion about treating test code as production code. He has also dedicated an entire chapter on Unit Tests in his book “Clean Code”. Tests are one of the most powerful things that enable flexibility, and thus have to be robust and clean. Changes will happen every day and we need to be prepared.
The best feature that makes tests clean is readability. If you can read and understand a test case you know how the code works, how the business rule is applied and so you can figure out what is broken. I would rather have test code that is clear than a super DRY code (though current tools allow us to accomplish both). Having readable code is top priority.
#2 Use Test Patterns to achieve great readability
Patterns improve test code in the same way that they improve production code. One pattern that I really like is the Arrange Act Assert, or just 3-As. It basically tells you to:
- Arrange: Setup your data and any necessary input that your test will use;
- Act: Do the actual work that the test will be testing;
- Assert: Check that what you expected really happened - or not;
Another pattern about how to organize your tests is the classic BDD - Given, When, Then. Martin Fowler has a good description of this technique. There are also patterns that go beyond just organizing and structuring your code. The book, XUnit Test Patterns by Gerard Meszaros has whole lot of nice Patterns, Techniques and Code Smells for tests and can be found entirely online.
#3 Avoid Unreliable Tests
Is the test really broken or do you just need to re-run it? If you ever get into the situation where you hear or even say it to someone, then you probably have a problem. Neil Craven has a good post about this, with some tips on what to do to get rid of non-deterministic tests, like rewriting the tests in a lower level, and so on.
Martin Fowler also has a very good post about non-deterministic tests explaining in further detail the damage they can cause and what to do to get better at it, like quarantining tests and other nice ideas.
The XUnit Test Patterns also includes a nice and deeper discussion regarding fragile tests and the possible causes, like the lack of isolation, or high sensitivity of interface.
#4 Test at The Appropriate Level
Martin Fowler has a simple division of types of test in 3 simple groups, Unit, Service and UI, and organizes them in a Test Pyramid. The Pyramid suggests that you should have a large amount of Unit tests that will give decent coverage and fast feedback, less number of Service tests and just a small amount of UI tests. Fabio Pereira wrote a good case study of the test pyramid.
Some anti-patterns are very easy to spot on you team, like the ice-cream cone by Alister Scott, which looks like an inverted pyramid where you have a lot of UI tests or manual tests, and the testing cupcake that looks like a square by having maximum coverage at all levels.
A very good metaphor made by Fabio Pereira describing the importance of focusing on what is important for the test is described in this post.
#5 Do Use Test Doubles
Test Doubles, or Mocks as they are usually known, help you reduce the cost of your tests by not using things that you don't need to. So I would say that you should use Test Doubles to help you test at the appropriate level. The problem happens when Doubles are overused and you start not using the things you should!
Uncle Bob published an awesome article on the various types of Test Doubles and what they do. It definitely helps to know what to use in each situation to avoid shooting yourself in the foot.
Other articles on isolating your tests and the pros and cons of Test Doubles are presented and discussed in deeper detail by Gary Bernhardt and Fabio Pereira. They should give you some insight on using just the right amount of Test Doubles.
Beside the points discussed here, there is a whole lot more about TDD (there was a recent series of Google Hangouts by Martin, DHH and Kent on the advantages and disadvantages of the TDD approach. The series is called "Is TDD Dead" and is very enlightening), BDD, ADD and ways to approach automated test and software design. I really like the flow of developing tests, thinking about design and implementation that TDD and BDD gives, as well as taking care to keep my test code clean. But it is up to you to find out which one you will work the best for you.
As I said earlier, while this list may not be exhaustive, it should provide you with enough information to start reading and learning more about tests. And, no matter which way you choose, it will be a helpful learning experience
If you have any feedback on the steps discussed here or want to add more, please feel free to leave comments below or contact me at @marcosbrizeno [https://www.twitter.com/marcosbrizeno]!
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.