This article brainstorms how design choices for better code testability fits into the big picture of providing a continuous stream of business value to the customer. Exploring the possible software design options even before the coding starts enables decoupling and dependency injection which makes the software all the more robust and flexible to absorb future changes in the customer requirements.
What is a unit? A unit is the smallest testable part of an application. Unit tests are written from the specification (FSD/TDD) based on how the product would be used by the end customer. It should be tested in isolation without any dependency. All the dependencies should be mocked up using stubs so that the unit tests are simple and fast. These tests are supposed to focus only on a specific action without any coding for the dependencies.
For example, the coder develops a method called validate_zip_code. When I enter zip code as 00000 it should return false. This is a unit test. As per the requirement spec, on the shipping form if the customer enters invalid zip code he should be re-directed to the help page that explains what is a zip code. Imagine that this help page is yet to be developed by a different team called consumer team. Testing both the zip code field and the display of help page is not unit testing but functional/integration testing. This dependency on the help page display needs to be separated from the validation of zip code. This separation is called dependency injection and it decouples the code making it more robust. The dependencies could be mocked using tools like mockito or easymock. Thus the coder needs to create a mockup or a simulator to substitute for the dependency.
Why dependency injection can ease unit tests? With dependency injection writing unit tests becomes easy and the execution of unit tests becomes simple and fast. This is because the coders are not required to write test code for dependencies as well. Writing code for dependencies makes unit tests complicated and it is also unnecessary since we are not going to do any functional testing with it.
Advantages of Dependency Injection:
1. Unit testing before coding adopts Test Driven Development. The coders can brainstorm the possible execution paths and corner cases that he had never thought of before. This results in a higher quality code
2. Code with lots of dependencies violates the Law of Demeter or the Hollywood principle. With dependency injection the code becomes loosely coupled
3. Determines if the code is fit for testing and increases the testability of the code
“Agile teams produce a continuous stream of value, at a sustainable pace, while adapting to the changing needs of the business.” – Elisabeth Hendrickson
In this definition, “business value” refers to shippable code. Agile teams release business value in the form of shippable code frequently at a very fast pace atleast monthly once.
“Sustainable pace” means that the agile teams continuously add capabilities to the emerging system at more or less the same velocity given no increases in team size.
Thus, Agile ensures that the code is built in a sustainable fashion to a high quality. This article explains how agile manifesto might apply to testing and the roles and responsibilities of a tester in an agile project.
“If you don’t know how to work together, whatever your defined processes, I can all but guarantee that your processes are not being followed” – James Bach
Team building should take the place of processes. The whole development team should commit to delivering a high-quality product. Testing is a key activity that should involve testers,developers and customer representatives. It is not supposed to be done by the testers in isolation. The developers need to write automated unit tests before starting to code. This approach of doing testing before coding is called Test Driven Development. In agile environment the distinction between a tester and a developer is blurred. Testers are not the solely responsible or even the primary owner of quality. Quality is a shared responsibility of the whole team. Individuals in an agile team may specialise in a particular role but will take on different roles depending on the context. Testers who are out of their depth reading code or uncomfortable influencing design decisions may require some training to fit into the agile team. The estimate for test efforts should be done as a part of the overall implementation efforts of the project.
Rigid processes sometimes tend to hinder the team’s progress towards quality goals and impose friction on people interactions. Testers need to be on their gaurd to watch out for any impact of rigid processes on quality. For a long time the focus of testing has been short-sighted with emphasis on functional correctness rather than value to people. This problem in testing is a subset of the bigger problems of rigid processes in place. For example, in company XYZ if the tester logs a defect that lacks the support of requirement specification document, the issue is considered to be a non-issue as per the process. The tester needs to support the defect with the automated test case that failed and the spec document it violates. Otherwise the DEV doesn’t fix the issue and the test managers do not treat the defect as a priority item. In these situations there needs to be a careful risk assessment of the issue through shared conversations with the developer and high support from the management if indeed the issue falls into high risk area.
Manager: Hey Team! We are agile now, therefore the requirements are not documented
Team: No problem, we can deal with it
Having no documentation is called agile…but wait…how do we come up with an elegant, high quality code without requirements documentation. It is a myth that agile eliminates all documentation. While agile testing does not support detailed specifications, the requirements should be clearly documented at a high level though not detailed.
Conversations and shared understanding will take the place of heavy-weight documentation. For testers this could mean favoring manual tests over automated tests. This gives the testers enough freedom to discover, diagnose and exploit the product while testing. This requires exploratory testing skills. Testers should be capable of looking at the big picture from multiple angles and ask good questions that the developers and customers might think of. The rigid test scripts and procedures in automation does not reveal critical issues at times. Exploratory testing can help to spot missing features or opportunities for product improvement. The testers could collaborate with the DEV and read the automated unit tests. Instead of spending energy on the specifications and requirements document, the testers can get into the code itself.
The agile testing mind-set is all about collaborating with customers, looking at the big picture, providing and obtaining feedback. Customer collaboration is at the heart of agility. Here the testers collaborate closely with the customers. Working closely with the customers is favored over becoming a customer proxy. The testers do not merely execute against a negotiated bill of work but have iterative collaboration with the customer. The testers state the user’s point of view in team meetings and bug reports. The testers even fail the release if the quality was unacceptable to protect\defend the customer.
Manager: Hey Team, the management has just decided to add twenty two new features to the product!
Team: Great! That’s good for the customer!
As an agile team we never resist changes and can accept any amount of changes anytime…What? If twenty two new features are added to the product what happens to the stability of the product?
While agile testing adapts to changing business needs, it doesn’t encourage adding many new features that can make the code unstable and break quality. Agile focuses on delivering a small set of core features with high quality one at a time and adds the other capabilites in the subsequent releases at a fast pace. The changes should be prioritised and if required deferred to the next release.
Change is at the heart of agile projects. The testing team might be expected to follow a plan when a change has happened in the product. The plan should support the change. After a change has been implemented the testers need to test the untouched areas of the changes to ensure the ongoing stability of the product.