You can code it, I can help!

Why Testing Is So Hard

Choosing what to test

Testing is the process we apply in order to make sure that software applications work as expected.

Sounds easy, but it is not.

Starting from the outside in approach, the first form of validation is writing an acceptance criteria for a feature that may include many valid scenarios. This kind of test is called aceptance testing, should be end to end crossing all the layers and, a black box in the sense we don’t know the details of how it is implemented.

Acceptance can be done either automated (using tools like Specflow, Cucumber, etc), manually or a combination of both.

The second form of validation is writing a test for a class, more specific a method that should implement a piece of functionality. That is what is called unit testing and is associated to one class, one method. All the collaborators should be “faked” in order to avoid coupling and to make sure the test is for the class and not for the dependencies.

The third form of validation is writing a test that involves two or more classes to validate how they interact. This is called integration testing. Integration is usually used when having doubts how the classes will work together (though unit test should take care of that) and writing an acceptance test is very difficult.

I am going to focus today on unit testing more specifically when using Test Driven Development.

Test Driven Development adoption

Writing a test after the method is already written is playing catch up. Not very useful.

On the other hand, TDD proposes the test first approach, here are the rules by Uncle Bob Rules of TDD if you are unfamiliar with the methodology.

Using TDD changes completely the way we write software. Quality as a driver gives us the confidence of modifying our code and makes more enjoyable our task. And don’t make me start about all the design benefits!

However, if it is all ponies and rainbows with such awesome results, why most teams don’t use it? Why adoption is so low?

The problem is that writing tests is harder than writing regular code.

Harder than what?

But, isn’t that crazy? Isn’t writing tests actually writing more code? If we know how to code, shouldn’t we know for sure how to write tests? Maybe I forgot to take my meds this morning?

Test first help us discover coupling issues and identify smelly code. How? Whenthe test is getting complex that indicates we have an abstraction problem, some dependencies that should be there, or something even smellier lurking in the code.

One of the questions I usually get asked about TDD is how are you going to write a test for a method that does not exist yet?.

How are you going to write code for method that you don’t know what is supossed to do? Well, you don’t.

For sure you need some kind of idea, hint or inkling of what should happen. We need a trustworthy approach, a bit more formal perhaps, that will take us through the TDD path letting the methodology become our ally and code quality driver.

What does this method do?

Each method can be defined in terms of the input parameters and the output values based on those parameteres.

That means that each method will have a set (collection) of values that can be passed (the domain) as valid parameters and a set of values that will be the result based on the input.

In order to identify either set I will use a predicate that will help filter which values are valid. Each value that satisfies the predicate (returns true) will be part of the set.

The predicate that defines the values that are a valid input is called Precondition. The Precondition will help us indentify the domain of values that we could use as valid inputs for the test.

The predicate that describes the output of the method is called Postcondition. The Postcondition will help us identify the asssertions to write and the scenarios to test in order to ensure that the method works.

I know, is a lot to take in one post, so rest a bit and think about the following exercise:

Find the Pre and Post for the method Square that takes an int and returns the square:

int Square(int x)

In the next post I’m going to show examples and consider what happens when the method modifies the state of the class.