Unit testing is probably the most ignored and misunderstood software engineering process in the Agile software development process.
For developers, unit testing is boring, tedious and time-consuming. Generally there is no time in sprints to do this extra activity, and developers feel testing is not their job. For product owner’s, they believe unit testing might delay the release dates, may increase the number of sprints required to complete all the story points in the product backlog, and thus increases cost to the project. It is believed that performing the task doesn’t deliver anything to customers, so they don’t want to pay for this extra effort or, for the extra code written during unit testing. Some would argue that there is no reason to maintain this unit test code, others say why duplicate the efforts of testing before delivering it to the test team, since the test team needs to run their test cases anyway? Another camp believes code reviews can be a substitute for this task, and the reviews will catch the code bugs. Many people tend to confuse unit testing with the functional testing done in the development environment using tools such as Selenium, which is done to check whether the planned functionality has been implemented and is working correctly.
Today many agile teams/organizations in an effort to make their sprints successful, deliver the work on time, have tended to adopt shortcuts to unit testing or ignore it completely. The consequences of these actions regarding unit testing may not always be immediately visible. But by doing this, you’re losing a key chance to benefit from the value it can offer to the project.
In the Agile methodology, requirement, design, and code changes are part of the process, and accommodating such changes are the basis of Agile. You will never have complete and unchanging requirements and design documents from the start. These artifacts are designed to evolve as the sprints progress. This can make writing good and good unit test cases, covering all requirements based on these documents a major challenge. As the requirements and design change, you often have to change all your written unit test cases. The scrum process is designed to handle changes in requirements coming from the product owner during the development process, so you may need to change the previously written unit code. This often requires, refactoring of code as you develop the product in successive sprints. In the case of legacy products, the code base continues to increase over time, and changes to it can result in broken code. To confirm that the new development hasn’t broken the legacy code, you will need to do regression testing using your unit tests.
Why you should do unit testing?
• The best documentation of your code is your unit tests, especially when you’re working on code written by others and you’re not familiar with the methods and classes or what they actually do. If you have unit tests in place, you can use them as a reference to get all the needed details. They give you the information on how the code has been designed to work.
• Unit tests should be used as the proof to prove that the code that has been written is working correctly. Unit tests are not designed to replace acceptance testing but instead supplement it.
• Bugs that are caught earlier in the development process are easier to fix and less costly to project. Unit tests help reduce the passing of bugs on to final builds or even into the field. Bugs that are caught after release are, always more difficult and expensive to locate and fix. Unit testing saves companies technical debt, time, effort, and money needed to find and fix these problems.
• Unit testing code is code that can bind easily to the rest of the system. Unit-testable code is designed to be cohesive and have loose couplings, so that it is easy to maintain over time.
When should unit testing be performed?
• Every time the code base is released into your code repository, the corresponding unit tests should also be released into the code repository. This allows code coverage tools to keep check of the unit test coverage. As a standard best practice, there should be a matching number of unit tests for each public method exposed in the project.
• Using the TDD (Test Driven Development) method for unit tests should be used as you write your code. Write your unit test for each public method you plan to add to the class and once this done then write your method and add it to the class, finally run your test and refactor as needed. This is much easier than developing an entire module and then attempting to test all the public methods and writing unit tests for them.
• You should always have a process to keep the unit tests up to date. During a sprint where you are moving, modifying, or deleting any code in the project, you should have tasks to make the required changes in the corresponding unit tests.
• Anytime you fix a bug in a sprint, it should be part of the process to write a unit test for it so that you can run these unit tests as part of the regression to avoid the reappearance of the bug in the future.
How is unit testing performed?
• A unit testing best practice is to create a separate class of files for the unit tests. This means that the unit tests should be separated from your class implementation files. This prevents your test cases from testing private methods and properties of the implemented classes. By separating the classes and unit tests in different files, you now have the option of changing the internal implementation of your classes whenever the need arises.
• Every unit test should be independent from all other unit tests. Unit tests should never need not be run in a particular order, nor should any unit test perform the setup or initialization of work for any other unit test.
• Since we intend to use the unit tests as the main source of documentation for our code, every unit test should cover only one aspect of the code. This means that you will need to write multiple unit tests in order to fully cover all the features of each method.
• By incorporating BDD (Behavior Driven Development) you can have your unit tests probe the core behavior of each component added to the project. The unit tests should be written to cover normal conditions, test the behavior of the method with correct input values and unanticipated conditions (negative tests). Unit tests should always test whether proper error handling is in place to tackle such unforeseen or incorrect conditions.
• Proper unit test creation relies on creating simulations, mock objects, or fake data to feed unit tests in order to test the workings of the implemented components. This will help to test complex systems having lots of components as well as components that interface with third-party components.
• Properly written unit test cases need to execute the code is working correctly. This means, if you’re testing a method, you need to call that method directly in your unit test.
• A unit test case should always be written to verify that code is working as it’s expected to work (meets acceptance criteria). So your test case should verify its results with an assertion. All unit tests should contain one assertion.
Traditional xunit testing is carried out on frameworks such as JUnit, TestNG, NUnit and cppUnit.