Wednesday 8 July 2015

Automatically Doing It Right


Thankfully in the vast majority of development teams the need for automated testing of the software that's produced is an accepted part of the development cycle.
But their are different types of automated testing and the difference is more than cosmetic it should inform your approach.
The main ways in which these types differ is in what can cause them to fail and how many of them you should be writing.
Testing the Bricks
Firstly we have unit testing, testing of a single building block of code.
Unit tests should have a single reason to fail, that is that there is a problem with the class under test.
The way to achieve this is by the proper application of Inversion of Control, once all a classes dependencies are being injected behind interfaces its a trivial matter to inject mock implementations during unit testing.
If this isn't possible its a smell with your design, it shouldn't lead you to the conclusion that making a class testable is difficult.
When a unit test fails you should be in no doubt as to which class is at fault, if your testing a piece of business logic and a unit test fails you shouldn't be asking yourself the question "I wonder if that's a problem with the database".
You should be writing as many unit tests as it takes to cover all a classes public functionality, a well designed class is a testable class, although at times it may be laborious it shouldn't be difficult.
Testing the Cement
Next comes integration testing, testing that our building blocks can fit together to form a well defined part of our structure.
There are various ways to approach this type of testing, either a Big Bang approach where we just test the whole structure, or a top-down or bottom-up approach where we test certain sub-systems before building the whole.
To return to our previous example, now we do test that the business logic works when it interacts with the database.
Our integration tests have as many reasons to fail as we have concrete classes involved in the tests, mocks can still be used for classes whose functionality we aren't interested in testing as part of the sub-system, this might include for example the UI.
We need to write as many integration tests as we have clearly defined sub-systems, much as with unit testing if we don't have clearly defined sub-systems this is our mistake, the solution isn't to say "well this just isn't testable". 
Testing the House
Finally we come to acceptance testing, testing that our grand plan has been implemented properly and does what we said it would do.
Now we are testing our whole system, we are using automation tools to take the place of the user and we are running our creation through its paces.
We need to write as many of these tests as we have acceptance criteria for our system to meet.
These tests have many reasons to fail but they show us exactly what the user will experience when they take these actions.
We've Built A Pyramid
What all this adds up to is that we have built a pyramid of automated testing.  
We are writing a large number of tests to ensure our foundations are solid, we are writing a smaller number of tests to ensure we have a solid core and a smaller number still of tests to ensure our system is acceptable
You might be asking why do we not just have a large square? Why don't we just write a lot of all these types of tests? The answer lies in how long they take to run and how fragile they are.
As we move up the triangle the more time the tests take to write and the longer they take to run. We never want to end up in a position where testing becomes a chore, we don't want people to dread writing them and we don't want to be put off running them.
By concentrating on unit tests we ensure that we can easily test a large proportion of a system quickly. This warm feeling that good unit testing produces means we can reduce the scope required by these extra layers of testing to verifying interaction we know the individual units of code work we just need to test they fit together. This reduces the need to write more of the slower complicated integration and acceptance tests.
As we move up the triangle the more fragile the tests become, the more likely they are to break when we change the system, so for the same reason we want to concentrate on unit testing because it offers the quickest feedback on the health of our code base. 
Automated testing is about ensuring our system is self checking by taking the weight of testing the basis of the design, this frees up testing experts in your QA department to spend their valuable time exploring the extremes of the system and concentrating on the "what if" scenarios.
The mantra is "test, test and test again" just be smart about how you implement it so that you never have to give much thought to testing your system its all just part of the process. 


No comments:

Post a Comment