Saturday, February 1, 2014

Using PHPUnit data provider for less code and greater coverage

Writing testable code is a harder target to achieve than just writing tests for the code written. You cannot write comprehensive tests that covers many function each of 200 or more lines and classes that measure 1000s of lines of code (LOC). Writing testable code is always important if you want to be able to test/unit test your code and be confident that nothing will break.

Tests also help a lot when you re-factor some code or write new features. If all the tests are passing you are quite sure that nothing is breaking. Seeing all tests come out green is a very good sight for us software engineer.


Testing in PHP with PHPUnit

Unit testing and testing as a whole is not a new thing in PHP,  PHPUnit the de facto unit testing framework in PHP started in 2001, The chart below shows that popularity of PHPUnit has really grown over the past years in comparison to Simple Test another PHP testing framework.

Source: Google Trends
PHPUnit is a great unit testing framework that can automate testing and help in getting optimal output. As they state in the documentation:
The difference between a good programmer and a bad programmer is that the good programmer uses tests to detect mistakes as soon as possible. The sooner you test for a mistake the greater your chance of finding it and the less it will cost to find and fix.
It is true the sooner the problem is detected and fixed the better it is, same goes when some code is re-factored, a new feature is added or a bug resolved.

Data Provider in PHPUnit

There are may features of PHPUnit, which can help in writing comprehensive unit testing and moving toward the direction of test driven development (TDD). Among them, data providers provide arbitrary arguments to a test function so that same code can be used to test multiple scenarios and possible cases. In this very simple example I will use a part of checkout. The logic is very simple:
  1. If the payment method is  "Cash", apply a cash on delivery fee of 5.0
  2. If the payment method is "Credit Card", do not apply a cash on delivery fee. 

The part of the checkout class is given below:


Source: Github repository

The test for the above checkout class is given below:


Source: Github repository

What makes using data provider in this example relevant and optimal?

  • As the test needs to be run for both "Cash" and "Credit Card" the only thing it changes here is the addition of cash on delivery fee.
  • There is just one test with the payment method being passed as a parameter, saving us from writing X no of tests for X no. of payment methods. So less code and more coverage.
  • The test is clear concise and understandable.

Things to consider in the code:

  • The test runs the as many times as the data set provided by the provider function.
  • @dataProvider annotation is necessary in the test to get the data sets from the provider function.
  • When writing the provider function, its better to comment who is using the provided data sets and the order of variables (check the comment of  paymentMethodProvider method).
Both the above files with composer.json is available as a public git repository for your reference, you can check the read me file to know how to run the tests.

Conclusion

Whenever you write tests and think, "Why am I writing a new test for the similar thing. I guess there can be a way to write less test/code and cover all my test cases." Then is time to use data providers in PHPUnit. You save on time and effort to cover the cases, a new case is just another array in the provider function, even in case of new test cases being added its a one line change given the tests pass. I hope this helps you to write less test code and achieve more coverage.

1 comment :

  1. i was unaware of data provider. i will have to refactor my tests now.

    ReplyDelete

Comments will be moderated, so they will not appear as soon as you post them.