In episode 6 of Testing All The Things we look at setting up PHPStorm and PHPUnit. During the video we look at feature and shortcuts to make your development process as simple as possible.
Tag: Test Driven Development
Episode 005 – Fake Test Double
In Episode 5 of Testing All The Things we look at the final type of test double.
During this video we replace the mock random number generator with a fake.
You can find all the Fake Test Double code in this GitHub repository.
Episode 004 – Mock Test Doubles
In Episode 4 of Testing All The Things we continue to create the driving licence generator we started in the previous two videos. In this video we create a mock random number generator to create random digits to the end of the driving licence number.
You can find all the Test Double code in this GitHub repository or just the code created for the spy functionality in this commit
Episode 003 – Spy Test Doubles
In this episode 3 of Testing All The Things we continue to look at different types of Test Double.
In the second video on test doubles we look at Spy Test Doubles.
You can find all the Test Double code in this GitHub repository or just the code created for the spy functionality in this commit.
Episode 002 – Stub Test Doubles
In episode 2 of Testing All The Things we start to exploring Test Doubles. Test doubles are used to isolate the code under test from its dependencies. Test doubles imitate the functionality of a dependency.
Creating test doubles do not require the use of a mocking framework. In the following videos we will not use a mocking framework so we can concentrate of the theory.
In the first video we are looking at the simplest form of Test Double, Stubs.
Episode 001 – Queue (Abstract Data Type)
In the first episode of Testing All The Things we introduce the concept of Test Driven Development (TDD). Using unit tests to guide the implementation of a Queue (Abstract Data Type)
During this episode we follow Uncle Bob’s three Rules of TDD
- You are not allowed to write any production code unless it is to make a failing unit test pass.
- You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
- You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
The GitHub repository for the code produced during this video can be found here.
Announcing: Testing All The Things Screencast
For a while now I’ve been thinking about producing content other than a blog. So with that in mind I’ve been working on a series of coding screencasts about software testing.
Testing All The Things is my coding screencast that I will use to demonstrate different techniques and tools for automated testing software.
I plan to do broadcast live coding once I get a bit more practise but for now I will record the coding sessions then upload them to YouTube fortnightly.
The code produced during my coding demonstrations will be uploaded to the Testing All The Things GitHub account.
There is also a new Twitter account to follow as well if you want more tweets about coding and testing rather than cycling and family.
If you have any suggestions for things you would like me to cover please leave a comment on this post.
Look out for the first video tomorrow.
Code Coverage: testing private functions
I was recently discussing whether or not you should directly test the functionality of private methods in a Go project. The other person reasoned for testing public and private functions to ensure 100% test coverage.
Testing private functions in Go is very simple. To do this you put your implementation and tests in the same package. The private functions are available in the test file as they are in the implementation file.
However just because you do not write tests that directly access the private functions, this does not mean you cannot achieve 100% code coverage. In fact if you follow Test Driven Development (TDD) most private functions will only be created during refactoring.
I prefer to only test public functions.
Lets go through a slightly contrived example.
We are going to implement a number package with two functions. One will add one integer to another. The second function will take one integer away from another. Both functions will return the result of the sum as the string representation of the number. i.e. result is 4, the value returned “four”.
We will only return a string for numbers between 0 and 10. If the number is out side of that range we will return “Cannot convert # to a string”.
Add Function
First we implement the functionality of the Add function. Testing and implementing for one simple addition with a result within our range and one addition with a result outside our range.
Tests
package number_test import ( "testing" "github.com/braddle/blog-testingPrivateFunctions/number" ) func TestTwoAddTwoReturnsStringFour(t *testing.T) { act := number.Add(2, 2) exp := "four" assertEquals(t, exp, act) } func TestSixAndFiveReturnsNumberNotConvertableString(t *testing.T) { act := number.Add(6, 5) exp := "Cannot convert 11 to a string" assertEquals(t, exp, act) } func assertEquals(t *testing.T, exp, act string) { if act != exp { t.Error("Actual value did not match Expected value") t.Logf("Expected: %s", exp) t.Logf("Actual: %s", act) } }
Implementation
package number import "fmt" // Add function add together the numbers given and returns the result as a // string func Add(a, b int) string { num := a + b switch num { case 4: return "four" default: return fmt.Sprintf("Cannot convert %v to a string", num) } }
Run Tests
$ go test -cover -v === RUN TestTwoAddTwoReturnsStringFour --- PASS: TestTwoAddTwoReturnsStringFour (0.00s) === RUN TestSixAndFiveReturnsNumberNotConvertableString --- PASS: TestSixAndFiveReturnsNumberNotConvertableString (0.00s) PASS coverage: 50.0% of statements ok github.com/braddle/blog-testingPrivateFunctions/number 0.001s
So far everything looks good. The tests are all passing and we have 100% test coverage.
Adding Minus
Now we implement the functionality of the Minus function. Testing and implementing for one simple subtraction with a result within our range and one subtraction with a result outside our range.
Tests
package number_test import ( "testing" "github.com/braddle/blog-testingPrivateFunctions/number" ) // Removed Add Test for Brevity. func TestSixMinusThreeReturnsStringThree(t *testing.T) { act := number.Minus(6, 3) exp := "three" assertEquals(t, exp, act) } func TestThreeMinusSixReturnsNumberNotConvertableString(t *testing.T) { act := number.Minus(3, 6) exp := "Cannot convert -3 to a string" assertEquals(t, exp, act) } func assertEquals(t *testing.T, exp, act string) { if act != exp { t.Error("Actual value did not match Expected value") t.Logf("Expected: %s", exp) t.Logf("Actual: %s", act) } }
Implementation
package number import "fmt" // Removed Add function for brevity. // Minus function take the value of b from the value of a and return the // result as a string func Minus(a, b int) string { num := a - b switch num { case 3: return "three" default: return fmt.Sprintf("Cannot convert %v to a string", num) } }
Run Tests
$ go test -cover -v === RUN TestTwoAddTwoReturnsStringFour --- PASS: TestTwoAddTwoReturnsStringFour (0.00s) === RUN TestSixAndFiveReturnsNumberNotConvertableString --- PASS: TestSixAndFiveReturnsNumberNotConvertableString (0.00s) === RUN TestSixMinusThreeReturnsStringThree --- PASS: TestSixMinusThreeReturnsStringThree (0.00s) === RUN TestThreeMinusSixReturnsNumberNotConvertableString --- PASS: TestThreeMinusSixReturnsNumberNotConvertableString (0.00s) PASS coverage: 100.0% of statements ok github.com/braddle/blog-testingPrivateFunctions/number 0.002s
We now have two functions and all the tests are passing with 100% test coverage. So far so good. However we have some duplication, this can be removed with a small refactor.
refactor
We already have all the test we need. We just need to move the duplicate code to it own function. There is currently no need for its functionality to be accessed from outside of the number package so we make it a private function.
package number import "fmt" // Add function add together the numbers given and returns the result as a // string func Add(a, b int) string { num := a + b return intToString(num) } // Minus function take the value of b from the value of a and return the // result as a string func Minus(a, b int) string { num := a - b return intToString(num) } func intToString(num int) string { switch num { case 3: return "three" case 4: return "four" default: return fmt.Sprintf("Cannot convert %v to a string", num) } }
Run Tests
$ go test -cover -v === RUN TestTwoAddTwoReturnsStringFour --- PASS: TestTwoAddTwoReturnsStringFour (0.00s) === RUN TestSixAndFiveReturnsNumberNotConvertableString --- PASS: TestSixAndFiveReturnsNumberNotConvertableString (0.00s) === RUN TestSixMinusThreeReturnsStringThree --- PASS: TestSixMinusThreeReturnsStringThree (0.00s) === RUN TestThreeMinusSixReturnsNumberNotConvertableString --- PASS: TestThreeMinusSixReturnsNumberNotConvertableString (0.00s) PASS coverage: 100.0% of statements ok github.com/braddle/blog-testingPrivateFunctions/number 0.002s
Now when we run the tests they all pass, so our refactor is good. We still have 100% test coverage without having to add new tests to cover our new private function intToString. The moved functionality is being indirectly tested by the existing tests on our public functions.
The completed implementation that ensure all some that result in a value between 0 and 10 can be be found in this Github repository.
Writing A Testable API Client Library
Recently at work I had to create a PHP client library for one of our REST microservices. The client library will be used by many of our projects. I wanted to implement the library using test driven development
I had some trouble finding any advice or examples of client libraries that had be implemented using Test Driven Development where the tests did not actually rely on making calls the service.
I wanted Guzzle to be a dependency of the client library. Doing this would allow me to pass in a mocked Guzzle instance to my PHPSpec tests .This would mean my unit tests would not rely on communicating with API to pass.
However I did not want users of the client library to have to worry about the Guzzle dependency.
After a discussion with some colleagues I decided the best approach was have the client object require Guzzle as a constructor dependency but provide a static factory method for client library users to use for construction without providing the Guzzle dependency.
class PokeClient { /** * @var \GuzzleHttp\ClientInterface */ private $httpClient; /** * @var \Braddle\PokeApi\Factory\PokemonFactory */ private $pokemonFactory; /** * PokeClient constructor. */ public function __construct( ClientInterface $httpClient, PokemonFactory $pokemonFactory ) { $this->httpClient = $httpClient; $this->pokemonFactory = $pokemonFactory; } /** * @return Client */ public static function create() { $httpClient = new Client( ['base_uri' => 'http://pokeapi.co/api/v2/'] ); return new self($httpClient, new PokemonFactory()); }
Any users of the client library now have a very simple way to instantiate the client library.
In this example the base URI is hard coded as the is only one version of the service. If you have different environments to work with you can pass URI or a constant for the environment in to the create() function.
Creating an instance of the PokeClient is now as simple at this
$pokeCLient = PokeClient::create();
This implementation allows me to mock Guzzle calls in my PHPSpec tests.
class PokeClientSpec extends ObjectBehavior { function let(Client $httpClient, PokemonFactory $pokemonFactory) { $this->beConstructedWith($httpClient, $pokemonFactory); } function it_is_initializable() { $this->shouldHaveType('Braddle\PokeApi\PokeClient'); } function it_should_be_able_to_create_an_instance_of_itself() { $this::create()->shouldReturnAnInstanceOf(PokeClient::class); } function it_should_return_a_pokemon_when_attmepting_to_find_one_by_id( Client $httpClient, PokemonFactory $pokemonFactory, Response $response, StreamInterface $stream, Pokemon $pokemon ) { $pokemonId = 975; $json = json_encode( [ 'id' => 34, 'name' => '', 'base_experience' => 4, 'height' => 15, 'is_default' => false, 'order' => 1, 'weight' => 468, ] ); $response->getBody()->willReturn($stream); $stream->getContents()->willReturn($json); $httpClient->request('GET', 'pokemon/' . $pokemonId) ->willReturn($response); $pokemonFactory->createPokemon(Argument::any()) ->willReturn($pokemon); $this->findPokemonById($pokemonId) ->shouldReturnAnInstanceOf(Pokemon::class); } }
This method also allowed my to add addition Guzzle options for my Behat integration tests
Feature: Getting Pokemon Scenario: Ensure that is it possible to find a Pokemon by its ID Given The client has been instantiated When I try to find a Pokemon by ID 1 Then I should have been returned a Pokemon
class FeatureContext implements Context, SnippetAcceptingContext { /** * @var PokeClient */ private $client; /** * @var Pokemon */ private $pokemon; /** * Initializes context. * * Every scenario gets its own context instance. * You can also pass arbitrary arguments to the * context constructor through behat.yml. */ public function __construct() { } /** * @Given The client has been instantiated */ public function theClientHasBeenInstantiated() { $this->client = PokeClient::create(); } /** * @When I try to find a Pokemon by ID :id */ public function iTryToFindAPokemonById($id) { $this->pokemon = $this->client->findPokemonById($id); } /** * @Then I should have been returned a Pokemon */ public function iShouldHaveBeenReturnedAPokemon() { if (!$this->pokemon instanceof Pokemon) { throw new \Exception('No Pokemon found'); } } }
See the full example client library for the Pokemon API in this GitHub repository.