Be A Happier Developer By Writing Unit Tests

If it's worth building, it's worth testing. If it's not worth testing, why are you wasting your time building it?
By Elena Ariton Posted 27 January 2016

Be A Happier Developer By Writing Unit Tests

Understanding the value of Unit Tests and adopting the rules of writing good ones will make you more confident in your work, and confidence usually is a key for making you happier. It's a fact that writing Unit Tests is not the most loved activity of developers. When building something we are impatient to releasing it so we can start gathering feedback (and making money of course!). Unfortunately, writing Unit Tests increase the development time of the product, so a lot of developers aren't willing to write them. It's mostly agreed that only tests run by a 'Software Developer in Test' are enough to ensure the quality of the product. In the long run: not having tests will increase the development time (if we include here the effort for fixing bugs after they are found in CI/PROD environments).

Nowadays, testing is not worked on by a person meant only for testing, such as a Developer in Test, QA, Tester or whatever they're called. Also, it was proven that only manual or automated high level testing is not enough to be confident in your products features, and that new features are not breaking old ones. We all know that a product is made up of small connected pieces having a lot of dependencies, building the business logic feature-by-feature, and breaking each feature into small pieces of logic. If we are going to only test the features at the big picture level, there is a huge chance to miss the small yet important bits of business logic. Also, when changing the implementation of a specific feature or adding a new one, dependant or not with the existing ones, there is a chance to break what was working before. That's why Unit Testing is useful and can make your life as a developer easier. This being said, let's see what a Unit Test actually is, why Unit Tests will help the developers and also customers, and how to write good Unit Tests so all can benefit from it.

What is a Unit Test?

  • A Unit Test verifies that a function or set of functions 'honors its contract' or 'meets the requirements'
  • Unit Tests tests individual classes or methods in isolation

‘Unit’ should say it all, or at least most of it, although the definition of a Unit is controversial. These kind of tests should only test 'Units'. We, the developers, should think of the Unit as being a class; a single responsibility from an entire set of responsibilities for building the entire feature or product. Let's imagine that we have to build a simple product that will have to sell books with specific discounts calculated by different formulas. In this case we might have a class for product retrieval, which will have methods with different requests for getting different list of books to sell. We might have another class having only the responsibility of giving discount results based on different book's details. By writing Unit Tests we will focus first on the ProductRetrieval class, and then separately on DiscountCalculator class. Those are Units. Pieces of logic which defines at the end the entire feature. This is a basic example of what a Unit can be, but we will detail this subject in further posts.

A test is not a Unit Test if:

  • It talks to the database
  • It communicates across the network
  • It touches the file system
  • It can’t run at the same time as any of your other Unit Tests
  • You have to do special things to your environment (such as editing config files) to run it

Why should we write Unit Tests?

I bet that we've all asked ourselves this question at leasy once, trying to find the best answer for it. I also bet that the following points will satisfy your needs and will answer and clarify your concerns.

Happier development teams

  • Fewer bugs (fewer late nights and weekend work)
  • More time to add features
  • Helps prevent breaking the code

Happier users

  • Fewer defects reaching Production
  • Less application crashes
  • All requirements are met

Reduce business costs

  • Defects are found early in the development cycle
  • Higher quality

Reliability

  • Exactly the same code runs each time
  • No variance between tests runs, then no human error – we need to ensure not to break this (watch out for hardcoded DateTime.Now!)
  • Have a documentation of the feature/requirement implementation

Faster execution

  • Quicker than a human performing tests manually
  • Provides concrete evidence that your software works

Maintainability

  • When we change/refactor the implementation
  • Add new functionality

A well designed application

  • If it’s hard to write tests on the current implementation, it means that the architecture is not well done

We all know that is not enough just to build something. We need to take care of the quality of what we build. Quantity is less important than quality, that's why it doesn`t matter if we are writing a lot of Unit Tests if they are not at the highest possible quality and they are not meant to really touch the business logic that matter. In order to achieve and ensure the quality of the product, we need to follow a few rules for writing good quality Unit Tests which will really bring us value for the product.

What is a good quality test?

  • Independent & isolated from other tests
    • Running order doesn`t change the result
  • Test a single behavior or logical thing
    • You can have multiple asserts if they are testing the same logical behavior
  • Reliable
  • Repeatable
  • Should be readable and have a clear intent
  • Test code should have the same quality as the production code
  • Tests should be valuable for development team
  • Use of dependency injection
  • Use of Test Driven Development (TTD) approach
  • Use 'Arrange, Act, Assert' sections
  • Define the Expected result
  • Group together duplicated code across tests in a Test Initializer method
  • Use constants for strings used through the tests
  • Proper naming of the test.

For naming of the tests, agree on a convention and have your team following it consistently. Some examples of the structure to follow could be:

  • MethodNameWhatItDoesWhenHavingSpecificInput()
  • MethodNameWhenHavingSpecificInputsWhatItDoes()
  • MethodNameHavingSpecificInputsReturnsResult()

Real real uses of this structure would look like this:

  • CalculateRatingOneVoteReturnsRatingValue()
  • CreateProductValidProductCallsProductRepository()
  • CreateProductMissingPriceReturnsErrorMessage()
  • CreateProductNullProductThrowsException()

Talking about Unit Testing can take longer than expected. This subject has a lot of key points to discuss on. That's why this post is only covering the basic details and facts about what a Unit Test means, what value can bring, and why should we adopt more of this in our daily projects. It needs to be understood what benefit it can have by writing Unit Tests first, and learning how this can be done in the most effective way. Further posts will come on this subject. We will be covering concrete situations about when Unit Testing brings real value to the projects and when it makes a real difference to have them in place. We are also planning to cover more advanced topics like:

  • Unit Tests leads to better designed code
  • A Unit is not always a class
  • Do we still need Unit Tests if we have automated acceptance tests?
  • Does Unit Testing help or hinder refactoring?

Stay tuned!

Elena Ariton
Elena Ariton
Software Engineer