Ask who has written tests before. Ask about experience with PHPUnit.
Single Responsibility PrincipleOpen/Closed PrincipleLiskov Substitution PrincipleInterface Segregation PrincipleDependency Inversion Principle
Emphasise that it is a framework like other frameworks. You still need to write the 20% to have your own tests!
Discuss that this only works if the pear command has been added to your path.
Mention that the require_once call will only work if the path to the pear installation dir has been added to PHP’s include path.Mention that casing is important. Also mention that defaults (use of word test, etc) can be overridden in config, command line switches, etc.Mention that you can have as many methods as you like in a test class but only test* methods will be run as tests.
Mention cases where setUpBeforeClass and tearDownAfterClass are useful.
Mention annotations similar to doc blox in PHP.
Explain that fatal errors cannot be converted to exceptions. Give an example where a warning could be caught, such as a call to fopen that fails on a non-existent file. Explain that PHPUnit registers its own error handler.
Switch to browser to show sample code coverage report.
Mention supported formats of data such as CSV and XML.
PHPUnit: from zero to hero
From Zero to Hero
WHAT IS UNIT TESTING… and why is it a Good Thing™?
WHAT IS UNIT TESTING? “In computer programming, unit testing is a method by which individual units of source code are tested to determine if they are fit for use. A unit is the smallest testable part of an application.” Wikipedia
HAVE ANY OF THESE EVER APPLIED TO YOU? You have a piece of code that everyone is too terrified to touch. You’re too scared to refactor or change a piece of code because you can’t predict the impact it will have on your application. Releases are a complete nightmare. You’ve had to work late or through a weekend to track down a bug.
WRITING TESTS IS HARD, WRITINGGOOD TESTS IS EVEN HARDER…
…BUT WORTHWHILE A study conducted by Microsoft and IBM showed writing tests can add 15%-35% to development time but reduce the number of bugs by 40%-90%! Writing code that is easily testable encourages best practices, such as SOLID principles. Having a set of unit tests can help you make sure your deployments are painless rather than painful None of us want to be stuck at work all weekend trying to work out why a code change has broken the whole application.
INTRODUCTION TO PHPUNIT PHPUnit is a unit testing framework written in PHP, created by Sebastian Bergman. Part of the xUnit family of testing frameworks. While there are other unit testing frameworks for PHP (such as SimpleTest or Atoum) PHPUnit has become the de facto standard. Major frameworks, such as Zend, Symfony and Cake, and many other PHP projects such as Doctrine have test suites written with PHPUnit.
REQUIREMENTS Minimum requirement is PHP 5.2.7 but 5.3.X is recommended. If you want code coverage analysis (you do!) you need to install the PECL xDebug extension. Best installed via the PEAR installer so you need to have run the pear installation.
INSTALLING VIA PEAR If installing via PEAR you need two commands to get up and running: Full installation instructions (including other optional PHPUnit modules) can be found at http://www.phpunit.de/manual/3.6/en/installatio n.html
TEST WRITING BEST PRACTICES The general aim is to make sure every possible path through your code is exercised at least once in a test. This means you need to write tests that exercise error conditions too. Rule of thumb: one test method per expected outcome per method tested. Have descriptive test method names. Name the test class after the class being tested.
TEST BASICS Simply add ‘require_once PHPUnit/Autoload.php’ to each test file. A PHPUnit test is a class that (usually) extends PHPUnit_Framework_TestCase. The class name should end with the word ‘Test’, e.g. ‘FooBarTest’. Each test class should be in its own file named after the class, e.g. ‘FooBarTest.php’. Each test method name must begin with the word ‘test’, e.g. ‘testSomethingWorks’ or have an @test annotation. Test methods must be public. The class must contain one or more methods that perform tests or the test case will fail. A test method must contain one or more assertions, be marked as incomplete or skipped.
TEST FIXTURES A fixture in a test is something (usually an object) that you want to test. Also known as System Under Test (SUT). PHPUnit will set up fixtures for you if you add protected methods setUp and optionally tearDown in your test class. You provide code to create/destroy fixtures in these methods. These methods are called before and after each test method so that each test runs on fresh fixtures. setUpBeforeClass and tearDownAfterClass are also available. These are run once at the beginning and end of the test class.
WRITING ASSERTIONS Assertions are used to test expected behaviour from your SUT. PHPUnit provides many different assertions for all sorts of needs, e.g. assertEquals, assertEmpty, assertTrue, assertType, et c. You can also test output using expectOutputString. More complicated assertions can be constructed using assertThat. For a test to pass all assertions must evaluate to true.
ANNOTATIONS PHPUnit supports annotations to give instructions to the test method being executed. Annotations are included in block comments before the method and always begin with an ‘@’. Examples: @depends, @expectedException, @dataProvider. Full details on supported annotations at http://www.phpunit.de/manual/3.6/en/appendix es.annotations.html
DATA PROVIDERS A data provider is a method that returns an array of values to use in a test. PHPUnit will call the related test method once for each set of data, passing the values as arguments to the method. Set using the @dataProvider annotation. Allows you to easily add extra values to test with. Also makes tests shorter and more concise by keeping values to test with out of test methods.
TESTING EXCEPTIONS You can tell PHPUnit that a test should expect an exception. This can be done in two ways: Through the method setExpectedException($exception, $message = ‘’, $code = null) Through @expectedException, @expectedExceptionMessage, @e xpectedExceptionCode annotations. PHP errors such as warnings are converted into exceptions by PHPUnit. These can also be tested for.
MOCK OBJECTS One of the most powerful features of PHPUnit.
WTF IS A MOCK OBJECT? Allows you to replace a dependency of your SUT with an object that has predefined behaviour. The mock object becomes part of the test. If the methods defined are not called as expected the test fails. Proper use of mock objects allow you to make sure you’re only testing the SUT and not other code. Helps to ensure that if a test fails it’s in the SUT, not a dependency. However, you need to be using Dependency Injection to use mock objects.
MOCK OBJECT BASICS PHPUnit creates a mock by sub-classing the original object. Once you have a mock object you can define what methods you expect to be called on it, with what arguments and what the mock should do.
OTHER MOCK OBJECT USES Mock objects allow you to test concrete methods of abstract classes with getMockForAbstractClass. You can create a mock object representing a SOAP web service using getMockFromWsdl. PHPUnit has experimental support for mocking out file system calls using the package vfsStream. More information at http://www.phpunit.de/manual/3.6/en/test- doubles.html.
COMMAND LINE RUNNER Tests are normally run from the command line with the phpunit command. Just typing phpunit will get a ‘help’ output. Passing the name of the test will run just that test. You can also pass a path to a directory to run all tests in it or the name of a test file to run tests in that file. PHPUnit prints a ‘.’ for each test passed, ‘F’ for a failure, ‘E’ for an error, ‘I’ for incomplete or ‘S’ for skipped. More information is also printed for anything other than a pass.
OTHER WAYS TO RUN TESTS Many IDE’s such as Zend Studio include PHPUnit integration. This enables you to write and run tests from within the IDE. Running tests can be integrated into continuous integration servers such as Jenkins. In this case your full test suite can be run automatically with each edit to your code. Any test failure will mean the build fails.
CODE COVERAGE ANALYSIS… or how to make sure you’re not feeling a false sense of security.
WTF IS CODE COVERAGE? Code coverage tells you how much of your code is covered by tests. PHPUnit can generate code coverage reports in a number of formats including HTML but… You must have installed xDebug. (pecl install xdebug) Helps to avoid a false sense of security. Code coverage threshold percentage can be added to continuous integration builds. Add code coverage by passing the –coverage-xxx option, eg. phpunit –coverage-html ./reports ./
PHPUNIT TEST EXTENSIONS Writing tests for different situations.
INTRO TO EXTENSIONS PHPUnit supports a number of extensions that allow you to write specialised tests. These include working with databases, Selenium, and running profiling with XHProf. Full details at http://www.phpunit.de/manual/3.6/en/installatio n.html.
TESTING WITH DATABASES To do this install the DbUnit You need to provide a extension, eg. ‘pear install schema with tables for phpunit/DbUnit’ PHPUnit to use. However, where possible getConnection must return a PDO instance for PHPUnit avoid tests using a database to use. by using mock objects. getDataSet returns data to Test case must extend populate database tables ‘PHPUnit_Extensions_Datab with. ase_TestCase’. PHPUnit truncates tables Defines two extra methods before each test run and that you must implement: inserts data. This means getConnection and each test runs with a fresh set of predictable data. getDataSet.
ADDING A CONFIG FILE Options can be passed to PHPUnit through the command line but it also supports adding an XML config file. This can include a number of options such as where to find test cases and which directories to include for code coverage analysis. To use this just create the file and name it phpunit.xml, adding it to the root of your tests directory. Especially useful when running tests as part of a CI build. Full details at http://www.phpunit.de/manual/3.6/en/appendixes.co nfiguration.html