Create mock for class with dangerous constructor

You want to:

Create a mock, but the mocked class has a constructor that executes sudo rm -Rf /, accesses a database, or otherwise causes trouble for your unit tests.

Solution:

Pass in an optional boolean fifth argument to $this->getMock().

$this->getMock() takes as a fifth argument a boolean that indicates whether PHPUnit should call the original constructor when constructing the mocked out class, which defaults to TRUE. If set to FALSE, the constructor won't be called. In that case, you can also pass an empty array as constructor arguments (the third argument to $this->getMock()), since they won't be used anyway.

Of course, you can also refactor the code to not do as much in its constructor. Quite often, there's a way to structure the code differently so that it is more loosely coupled and easier to test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
 
require_once "PHPUnit/Framework.php";
 
class FilesMultiplier
{
    /*
     * Read a pair of numbers from a file, mutiply, and return.
     */
    public function multiply($inputFile)
    {
        $line = $inputFile->read();
        $parts = explode(" ", $line);
        $a = (integer) $parts[0];
        $b = (integer) $parts[1];
        $result = ($a * $b);
        return $result;
    }
}
 
class FileObject
{
    public function __construct($filename)
    {
        // This exception represents our "dangerous" code.
        throw new Exception("Danger, Will Robinson");  
    }
 
    /*
     * This function reads a line from a file.
     */
    public function read()
    {
        // ... code omitted
    }
}
 
class TestFilesMultiplier extends PHPUnit_Framework_TestCase
{
    public function test_multiply()
    {
        $multiplier = new FilesMultiplier();
        $inputFile = $this->getMock('FileObject',
                                    array('read'),
                                    array(),
                                    'FileObject_test_multiply',
                                    FALSE);
 
        $inputFile->expects($this->once())
                  ->method('read')
                  ->will($this->returnValue("3 4"));
 
        $result = $multiplier->multiply($inputFile);
 
        $this->assertEquals($result, 12,
                            "Multiplying 3 by 4 did not give 12 as expected.");
    }
}
 
?>