January 13, 2017

#2 Effective Unit Testing [Series]

So, now your writing a little better code. If you haven’t read the first part of my tutorial, I suggest you read it now.

This tutorial will focus on why unit testing is important, how to make your code testable, and how to write useful tests for your code. Now, if you think unit tests are a waste of time, then I’m assuming you have little to no experience in long term application development.

Unit testing is a method of validating your code functionality automatically and meticulously without having to spend your precious time retesting old code because of changes to make sure nothing is broken.

Unit testing is a very important part of continuous integration, or ongoing development of an application (which most application life cycles require). Without unit testing, there is no way to assure that the code you are pushing to your code repository (please tell me your using one) still works the way it is intended to.

The first thing your going to have to know is some basic OOP principles. Dependency Injection, Method and Property Visibility, and scope of methods/classes are the most important for this subject.

Dependency Injection
———————
Dependency Injection is the act of basically inverting the control of where an object is created for a class.

Some classes need to depend on other classes in order to carry out their functionality. However, the classes that are needed should not be initialized inside the class that requires them unless it makes logical sense. Now, a huge challenge is trying to model your code as close to concrete objects as you can so you know how the class should logically handle all its functionality it incases (ie an HttpRequest class should only logically handle data that has to do with HttpRequests, and should never really care about HttpResponses. you know that the requests have a certain set of properties. Requests should be obtaining information from a different source by sending information passed to it. The request should not create the data in itself, because it is only middleware for transferring the information).

It’s kind of like having a deck builder hand a bucket of nails and a bunch of 2×4’s to their client and telling them to create the deck themselves (not the best metaphor, but I’m trying). The client is expecting the deck builder to provide a functioning and finished deck for their house. This can be modeled in PHP code.

Builder

House

Now logically, the house shouldn’t be responsible for creating the builder object as it doesn’t make sense. The builder is found by the owner, which then brings the builder to the house to complete the job. Think of dependency injection as introducing requirements for a class formally.

This can also be achieved informally (kind of like introducing your girlfriend to your wife while in a compromising position)

Now, it’s essentially the same thing, but remember how I said compromising position? If you ever want to switch out a generic builder and hire someone better to build your deck, such as a DeckSpecialist you have to go into the house class (which has no logical reason to be the creators of a builder) and change the new Builder() line to a new DeckSpecialist() (which implements a Builder interface). Now your wasting time on changing out a piece of code that should have been easy to swap out if you used dependency injection.

Hopefully your starting to understand why dependency injection is important, but the above reason is sort of minute compared to the next. Without dependency injection, unit testing is not logically possible. Unit testing, is by definition the testing of a single functional unit of code in the smallest nature possible and in the smallest scope possible.

If your unit testing a class that has multiple dependencies that are created inside the class, then you are no longer just testing that class, you are also testing those dependencies. If those dependencies have to communicate with an outside resource such as a file system, database, or web server then your unit tests can no longer be trusted because it’s possible other classes are changing the results of your test without knowing it.

Your probably thinking, well if a class depends on something then how can I get it to stop depending on something? Testing Doubles, and Mock Objects, more on that later though.

Method and Property Visibility
——————————

the visibility of all the items in your class should be important to you, if your treating a class like a concrete entity (which helps you structure out the scope and functionality of a class) then some things should be visible to the public and some things shouldn’t.

I feel like I shouldn’t really have to explain this further, but the rule of thumb I use is if a class will have a method call thats required to be called from outside the class then make it public. If the method is only used internally to help abstract logic out of similar public methods then either put it in a parent class, or a protected method in the current class. If the method should not be accessed by any child or external classes (ie something secret that you don’t want any other class using) then set it as private.

There are obviously exceptions to these rules, but I use them as a general rule of thumb. There is no point making a method public if it isn’t meant to be accessible by the public and only used internally.

Scope of Classes & Methods
—————————–

Your classes (like previously stated) should be treated as atomic pieces of logic, or concrete entities. A class named House should have nothing to do with a Shed, so any shed logic should be put into a Shed class instead, now both of these classes are both structures, so those two classes could have a common interface of a Structure, which can define the sort of things structures can and can’t do.

You should never be in a position where a class deviates from its original intention, and if it does you should strongly reconsider your design. If you have two very similar classes you might want to merge them into a single “Super Class” but it would be smarter to abstract the similar logic out into a parent class (that is appropriately named to be a parent of the two similar childs) and then let the children extend that parent class and use the shared logic between them, while still keeping everything logically seperate.

Now, your probably wondering how all of this ties into unit testing. Well, dependency injection is required for complex classes because the object that is injected into the class to be tested can be mocked, and it’s results can be manipulated by the person writing the tests so the external classes return the expected results. Remember, we only want to test the functionality of the current class we are testing, nothing else.

Class visibility is important because you should only be unit testing methods that are public. protected and private methods will be tested as well because they are being used by the public methods indirectly and the functionality will still be tested.

Finally, scope is important because no one wants to write a 600 line test file (I have, many times, and it is not fun wasting hours of your day testing the same class), so your code should be seperated into the proper classes to a reasonable level of scope, and your classes should never be at a crazy amount of lines.

Starting With Unit Testing
————————–

Well, your first going to need some code to test (actually, this is not technically true. There is a methodoligy of Test Driven Development which urges people to write tests before writing code, and writing code to tests instead of writing tests to code).

to start unit testing, you need to get PHPUnit. If you are not already using composer (for the love of god just use it and make your life 10x easier) you need to add composer as a development requirement for your current composer.json file.

This command adds phpunit as a development requirement for your project.

Code:
composer require phpunit/phpunit --dev

Great, now try to run the program.

Code:
./vendor/bin/phpunit --version

If you get a message output, your good to go!

Your next step is to create the phpunit.xml file, the main configuration file that PHPunit uses to find the files that runs tests. Here is a simple example from laravel. If you want to use PHPUnit for your custom code base look up tutorials online or ask for help in this thread, it really isn’t that difficult.

This file, basically says that all our tests are in the root ./test folder (remember, all the file locations are relative to this xml file). It is also saying that the tests are for the files in the ./app directory, so that when the tests are ran, it knows which files the test is for and it can accurately cover how much testing we have done on the code base.

Important Note: Ensure both your ./app and ./test directory share the same directory structure, it’s easier to keep track of where your tests are and where to modify them in the future.

Another Note: Be sure you add your directory to your composer.json autoload map just the same as a app directory so you can be sure that all those files will be loaded properly.

Alright, cool we have a fully working unit testing environment and a code base to run unit tests against.

Lets assume you have a class App\Testable that you want to test some functionality of, so lets create a new test class for it.

\App\Testable

Make sure that the the test class name is structured as follows <ClassName>Test extends TestCase, this lets PHPUnit know that the class it is reading in this directory is a test class.

The setUp() and tearDown() methods are ran before and after (respectively) every test in that class, it allows you to set important variables you want set, and do cleanups and things like that to make sure test data from one test isn’t carried over to another.

Finally, all test methods should begin with test followed by a camel cased small description of what the test does. The more descriptive, the better because once you have a large test codebase you will want to know exactly what is failing, and non-descriptive test method names will just take you a longer to find the actual issue at hand.

If you run a unit test on it, it’s not gunna work.

Code:
./vendor/bin/phpunit

The HTTP request relies on curl (most likely) and that curl call will not run if not connected to the internet, or not enabled for the CLI. Lets fix the testable class now that we know about dependency injection.

Testable

Now, if we run the test again we won’t solve the problem, we need to change the injected dependency into a object we can manipulate at will. This is where the PHPUnit mock object comes in handy. We use a couple methods here and we changed up the structure a bit.
$this->getMockBuilder() will allow us to essentially create a shell of the actual config class, which will by default just return null values for every property and method. This might not seem useful at first, but it is. The disableOriginalConstructor() allows us to bypass any constructor requirements, and getMock() finally returns the mocked class as a usable object. The object has all the same methods and properties, it’s just that those properties and methods won’t return anything useful, and that’s exactly what we want.

notice in the testCallbackReturnsDefinedMessage method that I used the ::method() method on the mocked object. The method() call allows us to over ride the default null value into something that we expect from the external library, in this case we expect the response() method to return an object, and from there extract the message property. The ->will($this->returnValue()) does exactly that.

Finally, we use an $this->assertEquals to test and ensure the the expected response we get from the getCallback is the same as the defined string. If we run the test now, we will get a green bar! yay.

This means you’ve done the following:
– created a testable class
– created mock objects for external dependencies
– mocked external dependency methods
– tested a method call and ensured it returned the proper string

Now, if someone were to change the getCallback to return a different property, such as the HttpResponseCode this unit test would throw an error, because the test was written to expect the message to be returned. This allows us to basically keep a constant warning machine that will notify us of possible hurtful changes in code, which is powerful in reducing the number of bugs you need to fix in the future if large changes are made.

Now, with this knowledge I’ll leave you with one more thing.

run that unit test again, but add the –coverage-html coverage/ option behind like so:

Code:
./vendor/bin/phpunit --coverage-html coverage/

once the test is complete navigate to that folder and open the index.html file. Looks like you have some more testing to do, hey? I generally aim for 80% code coverage, and a CRAP score (which basically measures the complexity of code) for under 4 for any given method. If it is higher, you should take some of the functionality out of that method and put it in a seperate method.

One last small tip, if there is a class (like stripe) that uses static calls only (which means they can’t be mocked) all you have to do is write a small object oriented wrapper around the class and then inject that in your class, which makes it fully unit testable.

Important Links:

Complete PHPUnit Manual
How I learned Unit Testing in PHP
All Currently Available Assert* methods

With this tutorial, and those supplementary resources you should be well on your way to writing meaningful unit tests to help continuous development of your application.

Note: This tutorial strayed away from laravel a bit and I disincluded some valuable information on unit testing in laravel. Laravel lets you unit test, but also integration test (which means testing on a bigger scope). Laravel lets you do things like testing a request to a route and making sure the response has certain text in it. This is helpful if you want to automate the tests of user experience and routing as well. There is plenty of information about this on the internet, I urge you to google about it if it interests you.

That’s all for this tutorial, in the next tutorial I’ll be explaining how to deploy your application properly with consistency, version control, automatic configuration, and automatic updates (on your own linux servers). Sounds pretty cool eh?

Category : Uncategorized

Leave a Reply

Your email address will not be published. Required fields are marked *

Proudly powered by Bolkya Resha and Software Testing Theme