Why should you care about testing? Programmers, like any human, make mistakes. We may forget about that edge case that we implemented last month, or about how some method behaves when we pass it an empty string.

There is no point in taking an app after every change and trying every possible click, tap, gesture and orientation change just to be sure that everything works. And you’ll probably forget about that triple tap in the right top corner while rotating the device so everything will crash when the user does it and something throws null pointer exception. Users do silly stuff, and we need to make sure that every class does what it’s supposed to and that every part of the app handles everything we throw at it.

That’s why we write automated tests.

1. Testing clean architecture

Clean architecture is all about maintainability and testability. Every part of the architecture has exactly one purpose. We just need to specify it and check that it actually does its job every time.

Now, let’s be realistic. What can we actually test? Everything. Genuinely, if you structure your code right, you can test everything. It’s up to you what to test. Unfortunately, there is usually no time to test everything.

Testability. That’s the first step. The second step is testing the right way. Let’s remind us about the old rule of FIRST:

Fast — Tests should be really fast. Part of the second fast. There is no point in writing tests if it takes minutes or hours to execute them. No one will check tests if that’s the case!

Isolated — Test one unit of the app at the time. Arrange everything on that unit to behave exactly how you want and then poke the testing unit. Assert that it behaves correctly.

Repeatable — Test should have the same results every time it is executed. It should not depend on some nondeterministic data.

Self-validating — Framework should know if test has passed or not. There should not be any manual checking of tests. Just check if everything is green and that’s it :)

Timely — Tests should be written about the same time as the code, or even before the code!

So, we made a testable app, and we know how to test. What about tests’ names?

2. Naming tests

Is it important how we name the tests, honestly? Well, it has more indirect than direct value. It reflects your attitude towards the tests, your way of thinking what to test.

Let’s meet our victim:

public final class DeleteFeedUseCase implements CompletableUseCaseWithParameter { @Override

public Completable execute(final Integer feedId) {

//implementation

}

}

First, naive way would be to write tests like this:

@Test

public void executeWhenDatabaseReturnsTrue() throws Exception { } @Test

public void executeWithErrorInDatabase() throws Exception { }

This is called implementation-style naming. It is tightly coupled with class implementation. When we change the implementation, we need to change what we expect from the class. These are usually written after the code, and the only good thing about them is that they are written quickly.

The second way is example-style naming:

@Test

public void doSomethingWithIdsSmallerThanZero() throws Exception {



} @Test

public void ignoreWhenNullIsPassed() throws Exception { }

Example-style tests are examples of system usage. They are nice when testing edge cases, but don’t use them for everything, they are too coupled with the implementation.

Now, let’s try to abstract our view of this class and move away from the implementation. What about this:

@Test

public void shouldDeleteExistingFeed() throws Exception {



} @Test

public void shouldIgnoreDeletingNonExistingFeed() throws Exception { }

We know exactly what we expect from this class. This test class can be used as class’ specification, hence the name — specification-style naming. Name does not say anything about the implementation, and from the tests’ names — specification — we can write the actual concrete class. Specification-style names are usually the best way to go, but if you think you cannot test some implementation specific edge cases, you can always throw in few example-style tests.

Theory ends here and we are ready to get our hands dirty!

3. Testing domain

Let’s see how can we test use cases. Structure of use cases in our Reedly app looks like this: