Reuben
Reuben

Reputation: 4276

How to reset a Mock Object with PHPUnit

How can I reset the expects() for a PHPUnit Mock?

I have a mock of the SoapClient that I would like to call multiple times within a test, resetting the expectations of each run.

$soapClientMock = $this->getMock('SoapClient', array('__soapCall'), array($this->config['wsdl']));
$this->Soap->client = $soapClientMock;

// call via query
$this->Soap->client->expects($this->once())
    ->method('__soapCall')
    ->with('someString', null, null)
    ->will($this->returnValue(true));

$result = $this->Soap->query('someString'); 

$this->assertFalse(!$result, 'Raw query returned false');

$source = ConnectionManager::create('test_soap', $this->config);
$model = ClassRegistry::init('ServiceModelTest');

// No parameters
$source->client = $soapClientMock;
$source->client->expects($this->once())
    ->method('__soapCall')
    ->with('someString', null, null)
    ->will($this->returnValue(true));

$result = $model->someString();

$this->assertFalse(!$result, 'someString returned false');

Upvotes: 12

Views: 10916

Answers (2)

Kuba Szymanowski
Kuba Szymanowski

Reputation: 1350

You can clear mocks like this:

// Verify first
$mock->mockery_verify();
        
// and then overwrite with empty expectation directors 
foreach(array_keys($mock->mockery_getExpectations()) as $method) {
    $mock->mockery_setExpectationsFor($method, new Mockery\ExpectationDirector($method, $mock));
}

Upvotes: 0

Reuben
Reuben

Reputation: 4276

With a bit more of an investigation, it seems you just call expect() again.

However, the issue with the example is the usage of $this->once(). For the duration of the test, the counter associated with expects() can not be reset. To combat this, you have a couple of options.

The first option is to ignore the number of times it gets called with $this->any().

The second option is to target the call with the usage of $this->at($x). Remember that $this->at($x) is the number of times the mock object gets called, not the particular method, and starts at 0.

With my specific example, because the mock test is the same both times, and is only expected to be called twice, I can also use $this->exactly(), with only one expects() statement. i.e.

$soapClientMock = $this->getMock('SoapClient', array('__soapCall'), array($this->config['wsdl']));
$this->Soap->client = $soapClientMock;

// call via query
$this->Soap->client->expects($this->exactly(2))
    ->method('__soapCall')
    ->with('someString', null, null)
    ->will($this->returnValue(true));

$result = $this->Soap->query('someString'); 

$this->assertFalse(!$result, 'Raw query returned false');

$source = ConnectionManager::create('test_soap', $this->config);
$model = ClassRegistry::init('ServiceModelTest');

// No parameters
$source->client = $soapClientMock;

$result = $model->someString();

$this->assertFalse(!$result, 'someString returned false');

Kudos for this answer that assisted with $this->at() and $this->exactly()

Upvotes: 10

Related Questions