Even though the question is already answered fully (how to assert attribute of object passed as argument to mock) I think it's worth noting that PHPUnit supports a callback constraint to be passed to with().
I kept bumping with this thread while trying to find out how to run further assertions on the mocked object arguments. For instance, I needed to check the return value of some method. Obviously, and for sanity sake, there is no methodReturnValueEqualTo() equivalent to the attribute assertion used in the answer above.
Fortunately, PHPUnit does support (as of 3.7 at least) a callback constraint, which makes a lot of sense and is something you can find in specialised Mock libraries like Mockery.
Current PHPUnit version docs state the following:
The callback() constraint can be used for more complex argument verification. This constraint takes a PHP callback as its only argument. The PHP callback will receive the argument to be verified as its only argument and should return TRUE if the argument passes verification and FALSE otherwise.
Therefore, using the callback constraint, the OP example can now be expressed as:
class SuTTest extends PHPUnit_Framework_TestCase {
public function testSuT () {
$stub = $this->getMock('Some');
$stub->expects($this->once())
->method('callMe')
->with($this->callback(function($arg) {
return ($arg instanceof Some) && ($arg->att === 'hi');
})
);
/*
* Will $stub->callMe be called with a Foo object whose $att is 'hi'?
*/
$sut = new SuT();
$sut->testMe($stub);
}
}
And a test fail would look something like:
1) SuTTest::testSuT
Expectation failed for method name is equal to <string:callMe> when invoked 1 time(s)
Parameter 0 for invocation Some::callMe(Foo Object (...)) does not match expected value.
Failed asserting that Foo Object () is accepted by specified callback.
You can now run any logic on that argument.
Even better, despite not being a documented feature in PHPUnit docs, you can actually use assertions and get the benefits of assertion error messages:
class SuTTest extends PHPUnit_Framework_TestCase {
public function testSuT () {
// alias to circumvent php closure lexical variable restriction
$test = $this;
// proceed as normal
$stub = $this->getMock('Some');
$stub->expects($this->once())
->method('callMe')
// inject the test case in the closure
->with($this->callback(function($arg) use ($test) {
// use test assertions
$test->assertInstanceOf('Some', $arg);
$test->assertAttributeEquals('hi', 'att', $arg);
// return true to satisfy constraint if all assertions passed
return true;
})
);
/*
* Will $stub->callMe be called with a Foo object whose $att is 'hi'?
*/
$sut = new SuT();
$sut->testMe( $stub );
}
}
Don't really know how future proof is this strategy of using assertions and returning true. It is not documented and there is a tradeoff in the error message. You no longer get the parameter constraint message, so if you set assertions on multiple arguments you will have to infer, if possible, which one failed. But you do get a better description of the failed assertion inside the closure.
1) SuTTest::testSuT
Expectation failed for method name is equal to <string:callMe> when invoked 1 time(s)
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'hi'
+'no'
Hope this helps anyone facing a similar problem.