DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

  1. DZone
  2. Refcards
  3. PHPUnit: PHP Test-Driven Development Automated Tools to Improve Your PHP Code Quality
refcard cover
Refcard #124

PHPUnit: PHP Test-Driven Development Automated Tools to Improve Your PHP Code Quality

Clean Up Your Code Quality with PHPUnit

Shows you how to set up and automate your testing with ease and help you improve code quality.

Download Refcard
Free PDF for Easy Reference
refcard cover

Written By

author avatar Giorgio Sironi
Freelancer, Freelance
Table of Contents
► How To Write A Test ► Assertions ► Fixtures ► Annotations ► Command Line Usage ► Configuration
Section 1

How To Write A Test

By Giorgio Sironi

Extensively testing a web application is the only way to make sure that the code you have written really works. Formal proofs of correctness are impossible or impractical to put together for the majority of computer programs. Therefore, actually running code in a sandbox under controlled conditions is the way to uncover bugs and drive the development of new features.

Of course, manual testing cannot be totally substituted by automated approaches, but automating a large part of an application’s tests leads to greater testing frequency, and quicker discovery of issues and regression.

Automated testing is based on the concept of test cases (classes in PHPUnit’s case) that compose a complete test suite. This suite, or a subset of it, can be run at will to check the production code correctness.

In this approach, the simplest and most effective way to write tests is to write code—simple code, but with the mandatory expressiveness of imperative languages that should give you the needed freedom.

Hot Tip

Besides the quality assurance side of testing, there is also the advantage in feedback that a good test suite provides. The more fine-grained the tests are, the more internal quality is put under “the lens”. Clean code is easy to test, while you can’t get away with technical debt if you have to write automated tests at the same time.

Tests are the first users of your code. They will tell you much about its simplicity of use, the isolation from component dependencies and the side effects that may arise.

Installation

In the first part of this DZone Refcard, we’ll test array_sum(), a simple, native PHP function that computes the sum of values in an array. The purpose of this first test is to introduce the mechanics of PHPUnit usage.

Before writing a test at all, we need PHPUnit. You can simply grab it via its PEAR channel:

​x
1
​
2
sudo pear channel-discover pear.phpunit.de
3
sudo pear channel-discover ... # other channels
4
sudo pear install phpunit/PHPUnit
5
sudo pecl install xdebug # for code coverage
6
​
7
​

The other channels where PHPUnit pulls components from may change in the future, so refer to the official documentation (http://phpunit.de/manual). This DZone Refcard is updated to the 3.5 version.

The first test

Here is how a test case looks like:

13
1
​
2
<?php
3
class ArraySumTest extends PHPUnit_Framework_TestCase
4
{
5
        public function testSumTheValuesOfAnEmptyArray()
6
        {
7
                $sum = array_sum(array());
8
                $this->assertEquals(0, $sum, “The sum of an empty array is computed
9
as $sum.”);
10
        }
11
}
12
​
13
​

Essentially, this is a class that extends PHPUnit_Framework_ TestCase so that it can be run by the /usr/bin/phpunit script. Here is a sample output:

Figure1

The methods whose names start with ‘test’ are executed, one at a time, on a different instance of this Test Case. You can execute whichever code you prefer in order to produce a set of results to confront with the ones you expect.

You can use methods on the Test Case that start with ‘assert’ to execute checks on the result of your computations that will ensure the system is behaving correctly. PHPUnit provides many simple assertion methods, but you can always define your own by adding private methods.

Executing phpunit filename.php from the command line would run the test. You can also run all the tests in a certain folder (subfolders included) simply by passing a path to the directory. This was the simplest test we could possibly write: it will provide good practice to start with and you can augment the complexity as you gain confidence about the System Under Test (SUT).

Now that we are capable of running a test on your machine, let’s gain confidence and expand the test a bit:

27
1
​
2
<?php
3
class ArraySumTest extends PHPUnit_Framework_TestCase
4
{
5
        public function testSumTheValuesOfAnEmptyArray()
6
        {
7
                $sum = array_sum(array());
8
                $this->assertEquals(0, $sum);
9
        }
10
        public function testSumTheValuesOfAnArrayWithOneValue()
11
        {
12
                $sum = array_sum(array(42));
13
                $this->assertEquals(42, $sum);
14
        }
15
        public function testSumTheValuesOfAnArrayWithManyElements()
16
        {
17
                $sum = array_sum(array(1, 2, 3, 4, 5, 6));
18
                $this->assertEquals(21, $sum);
19
        }
20
        public function testSumTheValuesOfAnArrayWithFloatValues()
21
        {
22
                $sum = array_sum(array(1, 2.5));
23
                $this->assertEquals(3.5, $sum);
24
        }
25
}
26
​
27
​

A failing test

Here is a failing test instead. We expect that array_sum works recursively by summing up all the values in internal arrays:

13
1
​
2
<?php
3
class ArraySumTest extends PHPUnit_Framework_TestCase
4
{
5
        // ...
6
        public function testSumsRecursively()
7
        {
8
                $sum = array_sum(array(1, array(2, 3)));
9
                $this->assertEquals(6, $sum);
10
        }
11
}
12
​
13
​

When run, this test gives the following result:

Figure2

We were asserting that the production code performed a task that it couldn’t. The test tells us it is not living up to our expectations.

Before diving into PHPUnit’s features, I want to highlight some methods that complement assertions in the flow control of PHPUnit.

MethodEffect$this->fail($message = '')Will instantly make the test fail, like in the case an assertion fails.$this->markTestSkipped()Tells PHPUnit that this test should be interrupted and marks it with an (S) in the results.$this->markTestIncomplete()Tells PHPUnit that this test is incomplete and shouldn't be run either. It will be marked with an (i) in the results and will turn the green bar of PHPUnit into an orange one.

Section 2

Assertions

Assertion methods are used to check the result of your test’s execution with predefined expected values. All these methods are available on $this, as they are defined on PHPUnit_Framework_TestCase. I report here the ones that, by experience, are the most handy and widely called in my test suites.

Basic assertions are the ones you always see in introductory material, and can serve many needs. They are also versatile, since corner cases can be reconduced to assertTrue() and assertEquals() in nearly any scenario.

MethodEffectassertEquals()Fails if the two arguments are not equal (checks with the == operator).assertSame()Fails if the two arguments are not identical (checks with ===), that is, they are not the same object or they are not scalar equal in value and type.assertTrue()Fails when this argument is not true.assertFalse()Fails when this argument is not false.assertEmpty()Fails when empty() on the parameter is false.assertNull(), assertNotNull()Checks if the variable is the special value NULL or not.assertInstanceOf()Checks that an object is an instance of (using this operator) the class or interface passed.assertInternalType()Checks if a scalar is of a particular PHP type-like integer or boolean.

Here is a first sample of these methods in action:

22
1
​
2
<?php
3
class BasicAssertionsTest extends PHPUnit_Framework_TestCase
4
{
5
        public function testBasicAssertionsWorkAsExpected()
6
        {
7
                $this->assertEquals(1, 1);
8
                $this->assertEquals(1, “1”);
9
                $this->assertEquals(3.0001, 3.000, ‘Floats should be compared with
10
a tolerance’, 0.001);
11
                $this->assertSame(42, (int) “42”);
12
                $this->assertTrue(true);
13
                $this->assertFalse(false);
14
                $this->assertEmpty(‘0’);
15
                $this->assertNull(null);
16
                $this->assertNotNull(42);
17
                $this->assertInstanceOf(‘stdClass’, new stdClass);
18
                $this->assertInternalType(‘string’, ‘hello, world’);
19
        }
20
}
21
​
22
​

There are more elaborate assertions which are indeed frequently called:

MethodEffectassertArrayHasKey(), assertArrayNotHasKey()Checks the presence of a key in an array.assertContains($needle, $haystack)Checks the presence of a value in an array, both numerical or associative. Works also with two strings, checking that the first is contained in the other one.assertContainsOnly($type)Checks that an array is composed only of $type values, like integers or booleans.assertRegExp()Checks that a string matches a PREG-based regular expression.

Here is a sample test case:

21
1
​
2
<?php
3
class ArrayAndStringsAssertionsTest extends PHPUnit_Framework_TestCase
4
{
5
        public function testArrayAndStringsAssertionsWorkAsExpected()
6
        {
7
                $this->assertArrayHasKey(‘key’, array(‘key’ => ‘value’,
8
‘anotherKey’ => ‘anotherValue’));
9
                $this->assertArrayNotHasKey(‘key’, array(‘anotherKey’,
10
‘yetAnotherKey’));
11
                $this->assertContains(‘value’, array(‘value’));
12
                // works for strings too
13
                $this->assertContains(‘world’, ‘Hello, world!’);
14
                // this would work with any primitive type
15
                $this->assertContainsOnly(‘string’, array(‘value’, ‘otherValue’));
16
                // PCRE regular expression
17
                $this->assertRegExp(‘/[A-Za-z ,!]+/’, ‘Hello, world!’);
18
        }
19
}
20
​
21
​

There are also special cases:

MethodEffectassertEquals()Fails if the two arguments are not equal (checks with the == operator).assertSame()Fails if the two arguments are not identical (checks with ===), that is, they are not the same object or they are not scalar equal in value and type.assertTrue()Fails when this argument is not true.assertFalse()Fails when this argument is not false.assertEmpty()Fails when empty() on the parameter is false.assertNull(), assertNotNull()Checks if the variable is the special value NULL or not.assertInstanceOf()Checks that an object is an instance of (using this operator) the class or interface passed.assertInternalType()Checks if a scalar is of a particular PHP type-like integer or boolean.

Here is a first sample of these methods in action:

22
1
​
2
<?php
3
class BasicAssertionsTest extends PHPUnit_Framework_TestCase
4
{
5
        public function testBasicAssertionsWorkAsExpected()
6
        {
7
                $this->assertEquals(1, 1);
8
                $this->assertEquals(1, “1”);
9
                $this->assertEquals(3.0001, 3.000, ‘Floats should be compared with
10
a tolerance’, 0.001);
11
                $this->assertSame(42, (int) “42”);
12
                $this->assertTrue(true);
13
                $this->assertFalse(false);
14
                $this->assertEmpty(‘0’);
15
                $this->assertNull(null);
16
                $this->assertNotNull(42);
17
                $this->assertInstanceOf(‘stdClass’, new stdClass);
18
                $this->assertInternalType(‘string’, ‘hello, world’);
19
        }
20
}
21
​
22
​

There are more elaborate assertions which are indeed frequently called:

MethodEffectassertArrayHasKey(), assertArrayNotHasKey()Checks the presence of a key in an array.assertContains($needle, $haystack)Checks the presence of a value in an array, both numerical or associative. Works also with two strings, checking that the first is contained in the other one.assertContainsOnly($type)Checks that an array is composed only of $type values, like integers or booleans.assertRegExp()Checks that a string matches a PREG-based regular expression.

Here is a sample test case:

21
1
​
2
<?php
3
class ArrayAndStringsAssertionsTest extends PHPUnit_Framework_TestCase
4
{
5
        public function testArrayAndStringsAssertionsWorkAsExpected()
6
        {
7
                $this->assertArrayHasKey(‘key’, array(‘key’ => ‘value’,
8
‘anotherKey’ => ‘anotherValue’));
9
                $this->assertArrayNotHasKey(‘key’, array(‘anotherKey’,
10
‘yetAnotherKey’));
11
                $this->assertContains(‘value’, array(‘value’));
12
                // works for strings too
13
                $this->assertContains(‘world’, ‘Hello, world!’);
14
                // this would work with any primitive type
15
                $this->assertContainsOnly(‘string’, array(‘value’, ‘otherValue’));
16
                // PCRE regular expression
17
                $this->assertRegExp(‘/[A-Za-z ,!]+/’, ‘Hello, world!’);
18
        }
19
}
20
​
21
​

There are also special cases:

MethodEffectassertFileExists()Check that a file is present in the filesystem based on its path.assertFileEquals()Check that the content of two files are equal.assertGreatherThan(), assertGreatherThanOrEqual(), assertLessThan(), assertLessThanOrEqual()All assertion methods oriented to operate with >, >=, <and <= over the two values passed.

Here is an example of them in action:

16
1
​
2
<?php
3
class ExoticAssertionsTest extends PHPUnit_Framework_TestCase
4
{
5
        public function testExoticAssertionsWorkAsExpected()
6
        {
7
                $this->assertFileExists(__FILE__);
8
                $this->assertFileEquals(__FILE__, __FILE__);
9
                $this->assertGreaterThan(23, 42);
10
                $this->assertGreaterThanOrEqual(42, 42);
11
                $this->assertLessThan(16, 15);
12
                $this->assertLessThanOrEqual(16, 16);
13
        }
14
}
15
​
16
​

Custom assertions are great tools (you just have to define an assert*() method). However, taking advantage of the assertions already supported by the framework will not require that you reinvent the wheel, or extend your base Test Case class in all your tests.

All assertion methods take a last optional argument $message, which is displayed when they fail. You may enhance your tests error messages when they are not clear with this additional parameter.

Section 3

Fixtures

Fixtures are a common way to set up objects that are used in all test methods before each test run.

In PHPUnit, fixtures can be implemented with the setUp() and tearDown() hook methods, which can populate $this private fields, preparing them for usage by the single test methods.

There are also two static methods, setUpBeforeClass() and tearDownAfterClass(), that are called only one time for each test case class. Because of their static modifier, they can only access static properties and are typically used for resources that are particularly heavy to set up, like database connections.

Hot Tip

PHPUnit creates a new Test Case object for each run, so you won’t have to reset your fields between the test methods. Therefore, each test method is run in isolation and won’t interfere with the $this fields set in previous runs.

Due to this isolation property, tearDown() is usually not present unless there is some external resource to release, like a database. For in-memory objects, garbage collection will be enough.

Due to the space available, this Refcard won’t go into mechanisms for setting up global variables or static attributes, which are usually more a design problem than a testing one.

Here is a test case which uses fixtures implemented with setUp().

27
1
​
2
<?php
3
class FixtureTest extends PHPUnit_Framework_TestCase
4
{
5
        private $systemUnderTest;
6
        public function setUp()
7
        {
8
                $this->systemUnderTest = new stdClass;
9
        }
10
        public function testFixtureWorkAsExpected()
11
        {
12
                $this->assertInstanceof(‘stdClass’, $this->systemUnderTest);
13
                $this->systemUnderTest->field = true;
14
        }
15
        public function testAnotherTestIsRunInIsolation()
16
        {
17
                $this->assertFalse(isset($this->systemUnderTest->field));
18
        }
19
        public function tearDown()
20
        {
21
                // don’t need to do anything if garbage collection would take care
22
 of this
23
                // close your database connections, for example
24
        }
25
}
26
​
27
​
Section 4

Annotations

PHPUnit supports the use of some annotations on test case and single test methods with the goal of transforming into declarative options some common behaviors diffused in test suites. For example, running the same test multiple times with a different set of input data is an operation common enough to warrant its own annotation. The mechanism of these multiple runs isn’t duplicated throughout the test suite of our projects, instead it is kept in the framework.

PHP does not have native support for annotations, so they are embedded into the docblocks of classes and methods, much like the @param and @return annotations used for API documentation. These annotations will save you from writing boilerplate code you would otherwise end up repeating in a bunch of tests that all look alike, or inheriting from some base test case class that gets longer and longer.

@dataProvider

This annotation tells PHPUnit to run a single test method multiple times by passing a different data set as method parameters each time.

PHPUnit will display a different failure for each of the different runs, so that multiple tries are independent and one failing does not affect the sebsequent execution of the others. Running the same test code with a foreach() cycle would not have the same effect.

test code

@depends

Hot Tip

This annotation declares a test as dependent on a previous one, saying essentially that when a basic test on the SUT fails, it is not worthy to even try to run a more complex, related one.

In case the SUT experiences a large regression, the errors presented by a test run would be much more focused, consisting only of the tests that failed first and excluding their dependent siblings.

Defining dependencies is usually only a good idea inside the same test case class. In fact, it is the only scale supported by this feature. The dependent test will be marked as ‘skipped’ when the first one fails, otherwise it will be run normally. Moreover, the first test can pass a computation result to its dependency by returning it (the dependent will accept it as a parameter). A classic example is testing the add() and remove() methods of a collection:

The first test will exercise add(), check that the inserted element is present and return the collection.

The second test will exercise remove() on the passed collection, and check that the element is not present anymore.

test code2

@expectedException (with @expectedExceptionCode and @expectedExceptionMessage)

This annotation asserts that the code contained in the test methods throws an exception, and optionally checks its code and its message.

Note that any line of code can be throwing the exception, as long as it is called inside the test method. If you want a more accurate check on which statement should raise an exception, stick to try/catch blocks.

@group

Hot Tip

Grouping aids you in running the exact amount of tests during development, and not the entire suite, which can require much more time. You will get feedback on a more frequent basis, and only from the code you select. This leaves the rest of the code base for subsequent integration (pre-commit or pre-push).

This annotation is not applicable to test methods, but only on test cases. It defines a test case as pertaining to a particular group that can be used as a filter to run all the grouped tests independently from the rest of the suite.

A test case can be in multiple groups. They can be thought of as labels rather than folders (which are an independent filter.)

For examples, you may use annotations such as @group acceptance (for Selenium-based tests); @group functional (for tests which do not exercise a single unit and, hence, are a bit slower to run), and @group ticket-42 (for the tests regarding a particular bug fix).

The command used to filter tests of a particular group is *phpunit --group*, which will be treated later in the command line section. However, you can also filter groups in the XML configuration.

@runTestsInSeparateProcesses

This too is a test case annotation. It is not diffused and should be used only for special case tests. It runs tests in different executions of the *php* command, so that they do not affect each other. This mechanism can be useful when testing something related to autoloading or a similar necessary global state. For example, you may be testing that the autoloader loads the same class multiple times under different conditions, and this can only be done in different processes.

16
1
​
2
<?php
3
/**
4
  * @group acceptance
5
  * @group someOtherGroup
6
  */
7
class AnnotationsTest extends PHPUnit_Framework_TestCase
8
{
9
        public static function dataToFeed()
10
        {
11
          return array(
12
                array(1, 2, 3),
13
                array(4, 2, 6),
14
                array(10, 12, 22)
15
​
16
​
35
1
​
2
​
3
                );
4
        }
5
        /**
6
         * @dataProvider dataToFeed
7
         */
8
        public function testSumOperatorsWorksCorrectly($a, $b, $total)
9
        {
10
                $this->assertEquals($total, $a + $b);
11
        }
12
        public function testFails()
13
        {
14
                $this->fail(‘I fail in order to stop my dependent test from
15
 running.’);
16
        }
17
        /**
18
         * @depends testFails
19
         */
20
        public function testIAmDependent()
21
        {
22
                $this->fail(‘I would fail too, but I\’m not run.’);
23
        }
24
        /**
25
         * @expectedException Exception
26
         * @expectedExceptionCode 400
27
         * @expectedExceptionMessage This is the message
28
      */
29
        public function testRaisesException()
30
        {
31
                throw new Exception(‘This is the message’, 400);
32
        }
33
}
34
​
35
​

Test Doubles

Test Doubles are substitutes for real objects that you don’t want to involve in your tests. By injecting Test Doubles in your System Under Test, you can effectively isolate it from the rest of the system and let it call its collaborators without null checks. You can also define what the Test Doubles expect as method parameters, or what they should return.

The taxonomy of Test Doubles comprehends:

PatternMeaningDummiesObjects that exist only to satisfy type hints and runtime checks. They are passed around but no methods are called on them.StubsObjects that return canned results when their methods are called.MocksStubs that can also check what parameters are passed to them.FakesReal implementations of collaborators, but much more lightweight than the real object.

PHPUnit offers support for automatic generaton and instantiation of Dummies, Stubs, and Mocks. It subclasses the defined class, overrides methods and eval() the resulting code. In PHPUnit, Mock is used as an umbrella term covering any Test Double category.

The getMock() or getMockBuilder() methods can be called to obtain the mock, or a Builder implementation over the mock itself. They accept an interface or a concrete class as the first argument. getMockForAbstractClass is the equivalent of getMock() for this special case.

Hot Tip

I contributed getMockBuilder() to provide a cleaner way for complex mock instantiation, which usually requires calls with 7 arguments to getMock(). It provides a fluent interface with different methods to set the creation options one by one, prior to creating the mock.

Once you have a mock, you can perform the expects() call on it to create an expectation object. The expectation object then presents different methods which provide a fluent interface:

MethodEffectmethod()Defines the name of the method it refers to.with()Specifies assertions to make on the parameters passed. In the simplest cases, you call it with the value you would use to call the method, in the identical order.will()Defines the behavior of the overridden method as traits such as what to return or whether to throw an exception.

Here is a test case which covers many instances of stubs and mocks usage:

121
1
​
2
<?php
3
class TestDoublesTest extends PHPUnit_Framework_TestCase
4
{
5
        public function testInstantiatesADummy()
6
        {
7
                $dummy = $this->getMock(‘stdClass’);
8
                $this->assertInstanceOf(‘stdClass’, $dummy);
9
        }
10
        public function testInstantiatesAStub()
11
        {
12
                $stackStub = $this->getMock(‘SplStack’);
13
                $stackStub->expects($this->any())
14
                                  ->method(‘pop’)
15
                                  ->will($this->returnValue(42));
16
                $this->assertEquals(42, $stackStub->pop());
17
        }
18
        public function testSetsUpAStubMethodWithACallback()
19
        {
20
                $callback = function($argument) {
21
                        $map = array(
22
                                ‘key’ => ‘value’,
23
                                ‘otherKey’ => ‘otherValue’
24
                        );
25
                        return $map[$argument];
26
                };
27
                $stackStub = $this->getMock(‘SplStack’);
28
                // don’t ask me why a Stack has getter and setters
29
                // I use it only because of its availability
30
                $stackStub->expects($this->any())
31
                                  ->method(‘offsetGet’)
32
                                  ->will($this->returnCallback($callback));
33
                $this->assertEquals(‘value’, $stackStub->offsetGet(‘key’));
34
        }
35
        /**
36
          * @expectedException InvalidArgumentException
37
      */
38
        public function testThrowsAnException()
39
        {
40
                $stackStub = $this->getMock(‘SplStack’);
41
                $stackStub->expects($this->any())
42
                                  ->method(‘push’)
43
                                  ->will($this->throwException(new
44
InvalidArgumentException));
45
                $stackStub->push(42);
46
        }
47
        public function testInstantiatesAMockAndPutExpectationOnAParameter()
48
        {
49
                $stackMock = $this->getMock(‘SplStack’);
50
                $stackMock->expects($this->once())
51
                              ->method(‘push’)
52
                              ->with(42);
53
                $stackMock->push(42);
54
        }
55
        public function
56
testInstantiatesAMockAndPutExpectationOnMultipleParameters()
57
        {
58
                $stackMock = $this->getMock(‘SplStack’);
59
                $stackMock->expects($this->once())
60
                                  ->method(‘offsetSet’)
61
                                  ->with(2, 42);
62
                $stackMock->offsetSet(2, 42);
63
        }
64
        public function
65
testInstantiatesAMockAndPutExpectationsOnParametersDifferentFromEqualTo()
66
        {
67
                $stackMock = $this->getMock(‘SplStack’);
68
                $stackMock->expects($this->once())
69
                                  ->method(‘offsetSet’)
70
                                  ->with($this->anything(), $this->identicalTo(‘42’));
71
                $stackMock->offsetSet(2, ‘42’);
72
        }
73
        public function
74
testInstantiatesAMockAndPutOtherExpectationsOnParameters()
75
        {
76
                $stackMock = $this->getMock(‘SplStack’);
77
                $stackMock->expects($this->once())
78
                                  ->method(‘offsetSet’)
79
                                  ->with($this->isType(‘int’), $this-
80
>isInstanceOf(‘stdClass’));
81
                $stackMock->offsetSet(2, new \stdClass);
82
        }
83
        public function
84
testInstantiatesAMockAndPutYetOtherExpectationsOnParameters()
85
        {
86
                $stackMock = $this->getMock(‘SplStack’);
87
                $stackMock->expects($this->once())
88
                                  ->method(‘offsetSet’)
89
                                  ->with($this->lessThan(3), $this->isTrue());
90
                $stackMock->offsetSet(2, true);
91
        }
92
        public function testCallsAMockTwice()
93
        {
94
                $stackMock = $this->getMock(‘SplStack’);
95
                $stackMock->expects($this->exactly(2))
96
                                  ->method(‘push’);
97
                $stackMock->push(23);
98
                $stackMock->push(42);
99
        }
100
        public function testShouldNotCallAMock()
101
        {
102
                $stackMock = $this->getMock(‘SplStack’);
103
                $stackMock->expects($this->never())
104
                                  ->method(‘push’);
105
        // this would fail
106
        // $stackMock->push(42);
107
        }
108
        /**
109
        * Each call on the MockBuilder apart from getMock() is optional.
110
        */
111
public function testInstantiatesAMockUsingBuilder()
112
        {
113
                $arrayIteratorMock = $this->getMockBuilder(‘ArrayIterator’)
114
                                                                  ->setMethods(array(‘current’, ‘next’))
115
                                                                  ->disableOriginalConstructor()
116
                                                                  ->disableOriginalClone()
117
                                                                  ->getMock();
118
        }
119
}
120
​
121
​
Section 5

Command Line Usage

The phpunit script is the main means to run PHPUnit-based test suites, even if there are ways to bypass it like Phing (<"http:// phing.info/trac/">http:// phing.info/trac/>) tasks for purposes of Continuous Integration.

Knowing how to use *phpunit* effectively is therefore crucial to leverage your test suite power. These are the various options available to modify its behavior:

SwitchEffect--configuration <file>, --no-configurationIncludes a phpunit.xml file different from the one in the working directory or excludes its automatic recognition.--coverage-html <directory>Generates an HTML report on code coverage in the specified folder. The xdebug extension is required for code coverage to be collected and the test execution will be much slower.--colorsSpecifies to use colors in the output as a nice way to visualize the test success.--bootstrap <file>Defines a PHP file to include before starting the test suite execution. It is included also in the configuration from some releases.--filterLets you filter test methods that match the passed pattern. You won’t have to comment on the other tests when you want to focus on a single one.--groupFilters the tests defined by the current suite and runs only the ones in the specified group.--exclude-groupRuns all the tests except the ones in the chosen group.--list-groupsShows all the groups available for running independently.--include-pathSets a particular include_path ini directive for running the test.-d key=valueSets a php.ini value (temporarily).--verboseDisplays a list of test case names as the execution goes along, so that you can catch errors during long run just when they happen, instead of waiting for the whole test suite to finish.--versionDisplays current version of PHPUnit. Handy to know, since running it without parameters starts the whole test suite. -v is not supported.--testdoxGenerates a “kind” of Agile documentation from the test names. If you write test names well, it documents what your class or component under test does for a living.

Section 6

Configuration

PHPUnit supports configuration of the test suite via an XML file that can be stored under version control. Common options like bootstrap files, logging formats or filters can be saved in this file instead of being passed with each PHPUnit command.

PHPUnit looks for phpunit.xml and phpunit.xml.dist, which are assumed to be present in the working directory. Usually one of them is kept in the test suite root directory.

Hot Tip

The common practice is to store phpunit.xml dist in the source control system and ignore phpunit.xml, so that users can personalize their run if they do not have the tools for running all tests available (such as Selenium or staging http servers).

It can also be the case that you do not want to run slow integration tests sometimes. With a phpunit.xml.dist present however, you will be able to run tests just after a checkout.

Root element

<phpunit> is the root element. It accepts various attributes such as “bootstrap”, which specifies a boostrap file to execute prior to the tests, or “colors”, which prescribes the use of colors like red and green in the output.

Test suites

The <testsuites> and <testsuite> elements let you group tests from different folders. They can contain these two elements:

  • <file>, which defines a single PHP file.
  • <directory>, which will include all the tests inside that directory or a subdirectory. <directory> accepts a suffix attribute to filter the test case name: by default it is ‘Test’.

Filtering, logging, and more

Inside the configuration, you also have different modifiers available. You can:

  • Filter tests of a certain <group> to run (or not run) them.
  • Define code coverage inclusion (and exclusion) of files. The <filter> element refers to code coverage.
  • Log results and statistics in various formats, with the <logging> element. It may contain various <log> entries, of various types: coverage-html, coverage-xml, json, tap, junit, testdox-html, testdox-text.
  • Attach listeners with the <listeners> and <listener> elements.
  • Configure a Selenium RC server to run acceptance tests, with the <selenium> and <browser> elements.
47
1
<phpunit bootstrap=”/path/to/bootstrap.php” colors=”false”>
2
        <testsuites>
3
                <testsuite>
4
                        <directory>tests/</directory>
5
                        <file>DoctrineTest.php</file>
6
                </testsuite>
7
        </testsuites>
8
        <groups>
9
                <include>
10
                        <group>functional</group>
11
                </include>
12
                <exclude>
13
                        <group>acceptance</group>
14
                </exclude>
15
        </groups>
16
        <filter>
17
                <blacklist>
18
                        <directory suffix=”.php”>library/</directory>
19
                        <exclude>
20
                                <file>library/App</file>
21
                        </exclude>
22
                </blacklist>
23
                <whitelist>
24
                        <directory suffix=”.php”>application/</directory>
25
                        <file>bootstrap.php</file>
26
                        <exclude>
27
                                <directory suffix=”.php”>application/views</directory>
28
                        </exclude>
29
                </whitelist>
30
        </filter>
31
        <logging>
32
                <log type=”coverage-html” target=”/tmp/coverage-report-folder” />
33
                <log type=”junit” target=”/tmp/junit-log.xml” />
34
                <log type=”testdox-html” target=”/tmp/testdox.html”/>
35
        </logging>
36
        <listeners>
37
                <listener class=”App_Test_FunctionalTestListener” file=”library/App/Test/FunctionalTestsListener.php”>
38
                <arguments>
39
                        <string>staging</string>
40
                </arguments>
41
                </listener>
42
        </listeners>
43
        <selenium>
44
                <browser name=”Ubuntu-Firefox” browser=”*firefox /usr/bin/firefox” host=”localhost” port=”4444” />
45
        </selenium>
46
</phpunit>
47
​

Like This Refcard? Read More From DZone

related article thumbnail

DZone Article

Creating Complex Test Configurations with Red Deer
related article thumbnail

DZone Article

Scalable, Resilient Data Orchestration: The Power of Intelligent Systems
related article thumbnail

DZone Article

Comprehensive Guide to Property-Based Testing in Go: Principles and Implementation
related article thumbnail

DZone Article

AI-Based Threat Detection in Cloud Security
related refcard thumbnail

Free DZone Refcard

Platform Engineering Essentials
related refcard thumbnail

Free DZone Refcard

The Essentials of GitOps
related refcard thumbnail

Free DZone Refcard

Continuous Integration Patterns and Anti-Patterns
related refcard thumbnail

Free DZone Refcard

Getting Started With CI/CD Pipeline Security

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: