Testing for no Testers

Diego A. Rojas
Blog Técnico QuintoAndar
6 min readFeb 16, 2019

Testing is one of the most important phases of the development process. Whatever type of project you are working on, testing is key if you want to deliver applications with high quality and make your users happy. Just imagine what would happen if your favorite taxi application makes your driver take you to the wrong place or the price of your trip is higher than it’s supposed to be? This embarrassing bug will undoubtedly produce a series of catastrophic events that will damage the reputation of the application and, worse still, the company reputation.

If you belong to the IT world, you must have heard about a very famous term in the last years: Test Driven Development. This approach changes the way developers test the code of their applications. Imagine the time it takes to code all the business logic of your application, then the fact that you have to write tests for all the possible scenarios. Don’t you think it requires a lot of knowledge about the system itself? Of course yes, and the risk breaking another part of your application is really high.

We can resume TDD in 3 simple steps:

Now, let’s start defining the first step. Which test should we write?

The good cop and the bad cop

When you start coding some feature, you know what is the goal. Imagine you are developing an application to store articles. You need to add the basic operations (Add, Update, Read, Delete). Let’s implement the Add operation first.

The goal here is really simple, just store a new article in the database, so we will write something like:

A little tricky to read? Don’t worry, this piece of code is perfect to start explaining some testing techniques and concepts.

Let’s start at the beginning: The test method name.

How will we name our test method is very important. Always remember that code is interpreted by machines but written and read by humans. The name has to be self-explanatory about what we are testing and in which scenario. Some naming conventions are:

  • ShouldThrowExceptionWhenAgeLessThan18
  • WhenAgeLessThan18ExpectIsAdultAsFalse
  • testIsNotAnAdultIfAgeLessThan18

You can find more info about naming conventions here: https://dzone.com/articles/7-popular-unit-test-naming

The art of mocking and stubbing

When you are writing a unit test, we only want to test a specific piece of code in an isolated way. Our application has different layers integrated between them, so if we want to test just one, we will need to mock those external dependencies.

Mock is an object simulating being another. A fake one. Suppose we have an ArticlesRepository and ArticleService class. ArticleRepository is a dependency of ArticleService because the last one sends data to be stored in the repository layer. We want just to test ArticleService, so we will need to create a mock of the repository.

For this purpose, we have a well-known library called Mockito. It makes really easy to create mocks to simulate we are providing the required dependencies.

@Mock    
private ArticleRepository repository;

Yes, we only need to add the Mock annotation. Now, we have an initialized instance of ArticleRepository… but is not a true one. If we call any method belonging to this class, we will fall in a NullPointerException because just not exists. We need to simulate that behavior and that’s were another powerful feature of Mockito will help us. Mockito.when().

Very human understandable, in line 5 of the code example we are only saying “When my save method is called with this parameter, then return this data”. Which parameter we can send? Mockito has helper methods to simulate anyString(), anyDouble(), or just any() thing.

Did you love Mockito? Me too, and you can read more about this awesome library here: https://site.mockito.org/

Stubbing is another technique to simulate objects that are not the real ones. We won’t depend on any external library for stub an object, we just will create an implementation of the existing object to stub but without the real behavior. For the case of ArticleRepository, we know that it consumes an Article object and returns the same object but with a generated id. So, we just can write something like:

As you may note, we are defining all the behavior manually. Stubbing is really useful when we want to simulate just some specific methods of our class without configuring a when-then cycle with Mockito.

Now, time for truth. Call the method. Basically, we are just calling the method so we won't explain that part.

Last but not least: The assertion.

Its time to verify if our piece of code is doing what its supposed to do. In this case, we have prepared a hardcoded article to simulate the mock response, so basically, we just need to make sure that the returned id is the same we have predefined. Here we are using some helpers like Mockito (again) and Hamcrest. The last is a library with good syntax methods to write what are we comparing with the current result. You can read the 9th line as “Assert that the returned id is my expected id”.

You can find more information about using Hamcrest here: https://www.baeldung.com/hamcrest-core-matchers

A good tip: Try to have just one assertion per test written.

Now, its time to make the test run and….se how it fails. Of course, we have not written the method solution yet! We just defined how should be our method behavior and what should return in case there were no errors. That's our good cop.

The bad cop

Nothing can be just happiness in this life and the same happens in software development. Our methods can throw errors? Of course. The goal here is to be prepared to deal with those errors. We need to know which type of exceptions or unexpected behaviors we could have.

What happens if i send an article with a content text bigger than the current length our database supports? What if we are trying to store an existing article twice? What should our application return if we are trying to update an unexisting article? There are a lot of scenarios that we need to be prepared for. Fortunately, we can test those scenarios too.

So auto explainable. We are just expecting an exception to be thrown when the method is called with a wrong parameter or a wrong result type. Now we are sure we will have this kind of exception and the error will be handled in a proper way, avoiding unexpected results in the behavior of our application.

Is it all we need to know about testing?

You know the basic steps about testing. We have used Java for these examples, but the concepts and techniques are applicable to any code language. We were just working on unit test in this post but is not the only kind of testing. We can divide application testing into 3 layers:

We have a long way yet to still talking about testing. Is one phase more important than another? No, all 3 are very important to ensure our product quality. In the next post, i will cover the second layer, Integration test. For this time, just go, open your project and start adding unit testing to your methods.

Conclusions

Testing is important. Consider it as a part of your common development routine. Don't think as a developer when you are writing tests, think as a user that has no idea what the system is doing inside. You just know that if you enter 2 + 2, then you should get 4. What if the user enters 2.01 + 1.99? You need to be prepared for that.

Thanks for reading, this article was inspired in an assessment application i did some months ago, you can check the complete code used on examples here:

And of course, some content is based on the book I wrote last year, “Fullstack development with Aurelia”. Get yours on Amazon and Packt store ;)

Cheers!

--

--

Diego A. Rojas
Blog Técnico QuintoAndar

Improving people's life through code | Co-Founder at @HalloID