Reputation: 649
Method setValue() In class SomeClass accepts arguments and no way to get this arguments back for make assertion.
Can I create a stub for method setValue() that allows save an arguments passed to this method?
class SomeClass {
public function setValue($name, $value)
{
// do some stuff
}
public function doSomething(array $values)
{
foreach ($values as $name=>$value) {
$this->setValue($name, trim($value));
}
}
}
class TestSomeClass extends PHPUnit_Framework_TestCase {
public function testDoSomething()
{
$mock = $this->getMock('SomeClass', array('setValue'));
$mock->doSomething(array('v1'=>' string '));
// here need I need assert like this
$this->assertEquals('string', $argumentPassedToSetValue);
}
}
Upvotes: 4
Views: 2811
Reputation: 36562
In this simple case you can specify the argument you expect to be passed to setValue()
.
class TestSomeClass extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('SomeClass', array('setValue'));
$mock->expects($this->once())->method('setValue')->with('v1', 'string');
$mock->doSomething(array('v1'=>' string '));
}
}
I agree with edorian, however, that you're better off testing what comes out of the class via the public API. Testing the internal implementation directly means updating more tests every time you change or refactor it.
Upvotes: 1
Reputation: 38981
On how to test if the setting worked see @Gordons answer.
Your unit tests should make sure the public API of your class works as expected. You don't care (for the sake of testing) how your values are stored internally so you don't need to make assertions on it. Doing it that way also makes your tests only test what your class does and not how the class does it
The point is that you shouldn't have to change your tests when you change your class without affecting what it does
For the sake of the argument let's say SomeClass is something that in the end spits out HTML.
class SomeClass {
public function setValue($name, $value)
{
// do some stuff
}
public function doSomething(array $values)
{
foreach ($values as $name=>$value) {
$this->setValue($name, trim($value));
}
}
public function createHTML()
{
$return = "";
foreach($this->values as $key => $value) {
$return .= "<div is='$key'>$value</div>";
}
return $return;
}
}
class SomeClassTest extends PHPUnit_Framework_TestCase {
public function testHtmlGenerationWithTwoValuesSet() {
$o = new SomeClass();
$o->setValue("foo", "bar");
$o->setValue("x", "y");
$result = $o->createHTML();
$this->assertSame(
2,
substr_count("<div>", $result),
"There should be as many divs as there are values set"
);
$this->assertTrue(
strpos("<div>bar</div>") !== false
"String should contain a set value enclosed in divs"
);
}
}
Again: It is about testing behavior of your class, not testing every method on its own. Your test suite will be much more valuable if you got about it this way
While the example with html might not be the right one it shows how to test behavior pretty nicely (i hope)
Upvotes: 3
Reputation: 199
You can use returnCallback to execute arbitrary code like a test helper method of your testcase. For example, if setValueTester is a method in your testcase this expectation will redirect the call to setValue on the mock to it. Arguments are available with func_get_args.
$mock->expects($this->any())->method("setValue")->will($this->returnCallback(array($this,"setValueTester"));
Upvotes: 2
Reputation: 317177
No. Mocks are replacements for Dependencies and not for the actual TestSubject.
In case your setValue
method sets non-public properties, you can use assertAttributeEquals
:
class SomeClass
{
protected $foo;
public function setFoo($val)
{
$this->foo = trim($val);
}
}
class SomeClassTest extends PHPUnit_Framework_TestCase
{
public function testSetFooTrimsArgument()
{
$testSubject = new SomeClass;
$testSubject->setFoo(' bar ');
$this->assertAttributeEquals(
'bar', /* expected value */
'foo', /* attribute name */
$testSubject
);
}
}
More information on how to test privates can be found in
Upvotes: 2