Skip to content

Commit 99cc5b7

Browse files
committed
[#4] add PHPMock::defineFunctionMock() as a workaround for bug#68541
1 parent ff6d7fd commit 99cc5b7

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ class BuiltinTest extends \PHPUnit_Framework_TestCase
6262
There's no need to disable the mocked function. The PHPUnit integration does
6363
that for you.
6464

65+
## Restrictions
66+
67+
This library comes with the same restrictions as the underlying
68+
[`php-mock`](https://github.com/php-mock/php-mock#requirements-and-restrictions):
69+
70+
* Only *unqualified* function calls in a namespace context can be mocked.
71+
E.g. a call for `time()` in the namespace `foo` is mockable,
72+
a call for `\time()` is not.
73+
74+
* The mock has to be defined before the first call to the unqualified function
75+
in the tested class. This is documented in [Bug #68541](https://bugs.php.net/bug.php?id=68541).
76+
In most cases you can ignore this restriction. But if you happen to run into
77+
this issue you can call [`PHPMock::defineFunctionMock()`](http://php-mock.github.io/php-mock-phpunit/api/class-phpmock.phpunit.PHPMock.html#_defineFunctionMock)
78+
before that first call (e.g. with `@beforeClass`).
79+
This would define a side effectless namespaced function.
80+
6581
# License and authors
6682

6783
This project is free and under the WTFPL.

classes/PHPMock.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,33 @@ public function registerForTearDown(Deactivatable $deactivatable)
108108
$result = $this->getTestResultObject();
109109
$result->addListener(new MockDisabler($deactivatable));
110110
}
111+
112+
/**
113+
* Defines the mocked function in the given namespace.
114+
*
115+
* In most cases you don't have to call this method. {@link getFunctionMock()}
116+
* is doing this for you. But if the mock is defined after the first call in the
117+
* tested class, the tested class doesn't resolve to the mock. This is
118+
* documented in Bug #68541. You therefore have to define the namespaced
119+
* function before the first call (e.g. with @beforeClass).
120+
*
121+
* Defining the function has no side effects. If the function was
122+
* already defined this method does nothing.
123+
*
124+
* @see getFunctionMock()
125+
* @link https://bugs.php.net/bug.php?id=68541 Bug #68541
126+
*
127+
* @param string $namespace The function namespace.
128+
* @param string $name The function name.
129+
*/
130+
public function defineFunctionMock($namespace, $name)
131+
{
132+
$functionMockBuilder = new MockBuilder();
133+
$functionMockBuilder->setNamespace($namespace)
134+
->setName($name)
135+
->setFunction(function () {
136+
})
137+
->build()
138+
->define();
139+
}
111140
}

tests/PHPMockTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,34 @@ protected function disableMocks()
2727
{
2828
}
2929

30+
/**
31+
* Tests defineFunctionMock().
32+
*
33+
* @test
34+
*/
35+
public function testDefineFunctionMock()
36+
{
37+
$this->defineFunctionMock(__NAMESPACE__, "escapeshellcmd");
38+
self::escapeshellcmd("foo");
39+
40+
$mock = $this->getFunctionMock(__NAMESPACE__, "escapeshellcmd");
41+
$mock->expects($this->once())->willReturn("bar");
42+
43+
$this->assertEquals("bar", self::escapeshellcmd("foo"));
44+
}
45+
46+
/**
47+
* Returns the built-in call to escapeshellcmd().
48+
*
49+
* @param string $command Shell command.
50+
*
51+
* @return string The built-in call.
52+
*/
53+
private static function escapeshellcmd($command)
54+
{
55+
return escapeshellcmd($command);
56+
}
57+
3058
/**
3159
* Tests building a mock with arguments.
3260
*

0 commit comments

Comments
 (0)