I am trying to design a Database($db) Mock with Atoum that would return different values depending on previous method calls (and arguments).
I'm using PHP 5.6 and Atoum 3.2
Here is what I tried:
$this->calling($db)->select = function($table, array $bind, $boolOperator = "AND") use ($permissionClientMapper, $db, $permissionsClientRaw){
if($table === $permissionClientMapper->getTableName()){
$this->calling($db)->fetchAll = function() use ($bind, $permissionsClientRaw){
if(array_key_exists('type_service', $bind) && array_key_exists('id_service', $bind) && $bind['type_service'] === 'mutu' && $bind['id_service'] === 4012){
return EXPECTED_RETURN_VALUE;
}
return null;
};
}
};
I would except the code to return the EXECTED_RETURN_VALUE
when I call (with arguments):
1/ $db->select() -> This method is called as expected
2/ $db->fetchAll() -> This one is never called
I didn't find any example of this in the Atoum documentation.
Can someone confirm this is the correct way to mock successive method calls ?
I also tried to use a reference to the database in the closure
$this->calling($db)->select = function($table, array $bind, $boolOperator = "AND") use ($permissionClientMapper, &$db, $permissionsClientRaw){
if($table === $permissionClientMapper->getTableName()){
$this->calling($db)->fetchAll = function() use ($bind, $permissionsClientRaw){
if(array_key_exists('type_service', $bind) && array_key_exists('id_service', $bind) && $bind['type_service'] === 'mutu' && $bind['id_service'] === 4012){
return EXPECTED_RETURN_VALUE;
}
return null;
};
}
};
But this doesn't work either.
Edit: One workaround would probably be to use the atoum call order to return different values for each call, and then to test the mock to check it was called with the correct arguments.
I will give you some insight about your questions and hope give you some clue to find a way to solve it.
So to validate that a mock method is not called, you can use 'call' with 'never'
$this->mock($mock)->call('fetchAll')->never();
And to be called :
$this->mock($mock)->call('select')->once();
To deal with you mock answer, you can use several things, like this
$this->calling($db)->fetchAll[0] = null; // default answer
$this->calling($db)->fetchAll[1] = function () {....} // first call to method
If you want something like a chain : when use the mocked method select, and inside it we call fetchAll method then the answer is ... atoum doesn't offer yet this behavior. The best is to create an issue exposing your case.
When you use 'calling' you define the behavior of the mock. It's only when the method is called, that atoum will grab everything and resolve it.
So for me, if I understand correctly your question, I will write it like that :
$this->calling($db)->fetchAll = function() use ($bind){
if(array_key_exists('type_service', $bind) && array_key_exists('id_service', $bind) && $bind['type_service'] === 'mutu' && $bind['id_service'] === 4012){
return EXPECTED_RETURN_VALUE;
}
return null;
};
$this->calling($db)->select = function($table, array $bind, $boolOperator = "AND") use ($permissionClientMapper, $db){
if($table === $permissionClientMapper->getTableName()){
return $db->fetchAll();
}
};
// this is the same as your code. But It a bit more readable
$this->newTestedInstance;
$this->testedInstance->setDb($db);
$this->variable($this->testedInstance->doTheCallThatReturnNull())
->isEqualTo(null);
// do some change in the vars to be in the value
$this->variable($this->testedInstance->doTheCallThatReturnValue())
->isEqualTo(EXPECTED_RETURN_VALUE);
ps : to help you going further you can read http://docs.atoum.org/en/latest/asserters.html#mock and http://docs.atoum.org/en/latest/mocking_systems.html and you can also tag the question with 'atoum'.