Shipping code that works is crucial to retaining the support of customers and high quality in your application. While it’s impossible to ship code without any bugs at all, it is possible to control for as many as possible, and fix as many known issues as there is time. These strategies are designed to ensure that code works when it is shipped to the end user.
Developers have a tendency to test their code only with expected data. Testers, on the other hand, aren’t developers themselves; instead, they will use data that you don’t expect and find bugs that your users might otherwise experience.
Hiring testers is a tough sell in many development teams, especially small ones. It is possible to have testers that have other functions – that is, they might be in another department or moonlight as testers. But with teams larger than 5 developers, having a full time tester is a crucial component of good development practices.
Write unit tests.
Every developer makes mistakes at some point. Having unit tests in place will help find these mistakes by showing you where a class breaks. It makes refactoring easier as well, since you can refactor and know that if your unit tests pass, there’s a good chance that you did it properly.
Unit testing should be built into the process of code development from the beginning of a project. However, if you’re starting from someone else’s project and the project doesn’t have unit tests already, simply institute a process of fixing bugs after you’ve written a unit test that identifies the bug. Eventually you’ll have unit tests for most of the application.
Write functional tests.
Unit tests are great, but they’re not enough. Knowing that one function takes an array and creates an object is fine, but what happens with the next function, and the one after that? Introducing functional tests: testing against expected behavior.
There’s a subtle difference between these two concepts. Unit tests test a specific component of the code: a single method, function, or clause. Functional testing, on the other hand, tests expected behavior: does clicking that button actually result in a refreshed page? Does my controller actually invoke the action properly? More than one method might be acted upon with functional testing.
A lot of this testing is done by the testers; however with applications like Selenium you can conduct some automated functional tests. On a small team that doesn’t have testers, or on a large team where there might be a challenged set of resources, automated functional tests can help reduce the testing burden.
Work unit testing into your build process.
We talked about integrating your build process with a continuous integration server. With build engines like Phing, it’s possible to automate the unit testing process (and even the functional testing process to some degree).
Each time you make a build for release, you should know that all the unit tests pass. If they don’t, there’s a problem that should be addressed before the build is completed.
Use continuous integration to know when tests began failing.
To hit on the same theme, a solid continuous integration server will automatically run your tests and alert you as soon as the first one fails. This helps prevent regression – the introduction of bugs into code that worked in previous releases. The time to discover regression isn’t when the build is due and the team is ready to go, it’s right after a commit, and continuous integration will help with this.