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');
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()
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));
}
Related
I'm trying to update a template document via PHP API using this: https://github.com/docusign/docusign-php-client/blob/master/src/Api/TemplatesApi.php#L4946
I get one of two errors depending on if I set the apply_document_fields option.
Without it set, I get UNSPECIFIED ERROR Value cannot be null.\r\nParameter name: fileBytes. However, if I view the request body before sending, document_base_64 is set as expected.
With apply_document_fields set 'true' (actual boolean value is not supported), I get FORMAT_CONVERSION_ERROR The data could not be converted.
Either way, it seems like the document data is not getting sent correctly, but I can't figure out how I'm supposed to be sending it. Here's my code:
public static function updateTemplateWithDocument(string $documentId, string $templateId, $documentBody = null)
{
$api = My_Service_Docusign::getInstance();
$templatesApi = new DocuSign\eSign\Api\TemplatesApi($api->getAuth());
$document = new \DocuSign\eSign\Model\Document();
$document->setDocumentBase64(base64_encode($documentBody));
// Got an error reusing $documentId, so I'm incrementing it now
$document->setDocumentId((string) (((int)$documentId) + 1));
$def = new DocuSign\eSign\Model\EnvelopeDefinition();
$def->setDocuments(array($document));
$opts = new \DocuSign\eSign\Api\TemplatesApi\UpdateDocumentOptions();
// Different behavior with this set vs not
$opts->setApplyDocumentFields('true');
$res = $tmpApi->updateDocument($api->getAccountId(), $documentId, $templateId, $def, $opts);
return $res;
}
Unfortunately, DocuSign support doesn't support their API :-(
I figured out I need to use TemplatesApi::updateDocuments (plural) instead, which also allows me to reuse the documentId.
I'm looking for a way to wait my script until a variable is still different from null.Actually, I am submitting a batch job file and I need to wait until I get the id.
I took a look at sleep function, but it seems not matching to my need.
I need to do something like that:
<?php
...
$options = array('features' => SOAP_SINGLE_ELEMENT_ARRAYS);
$client = new SoapClient($URL, $options);
$result = $client->submitBatchJob(array('data'=>$data, 'process'=>$NameChecker))
->submitBatchJobResult;
while($result=='');
wait;
//execute rest of the php script...
?>
What you're looking for is:
if($variable === NULL) {...}
PHP treats NULL, false, 0, and the empty string as equal.
I have a functional test that creates several records and then makes some request calls, the tests sometimes passes and others not, it's really weird, when I use var_dump it sometimes give me the amount of records I was requiring, and other times it just give me less than that.
This is the code:
foreach (range(0, 80) as $number)
{
$citaDetalle = new CitasDetalle();
$citaDetalle->setCodigo('FF#')
->setCitaGenerator($generator)
->setUidCreate($user)
->setFechaCita( DateExtension::nextLaborDay((new \DateTime())->modify("+5 Day"), false, false) )
->setCitaTurno($turno)
->setCitaPlace($place)
;
$em->persist($citaDetalle);
}
foreach (range(0, 20) as $number)
{
$citaDetalle = new CitasDetalle();
$citaDetalle->setCodigo('FF#')
->setCitaGenerator($generator)
->setUidCreate($user)
->setFechaCita( DateExtension::nextLaborDay((new \DateTime())->modify("+5 Day"), false, false) )
->setCitaTurno($turno2)
->setCitaPlace($place)
;
$em->persist($citaDetalle);
}
$em->flush();
$crawler = $this->client->request('GET', '/c/g/citas/new');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode(),
"Unexpected HTTP status code for GET /c/g/citas/new");
$form = $crawler->selectButton('Generar Cita')->form([
'core_gestion_bundle_citas_detalle_type[citaGenerator]' =>
$crawler->filter('#core_gestion_bundle_citas_detalle_type_citaGenerator option:contains("Generator Test")')->attr('value')
]);
$this->client->submit($form);
$this->client->followRedirect();
$lastDate = $em->getRepository('CoreGestionBundle:CitasDetalle')
->obtenerUltimaCita()[0]->getFechaCita();
$compareDate = DateExtension::nextLaborDay((new \DateTime())->modify("+6 Day"));
$this->assertEquals($compareDate->format('Y-m-d'), $lastDate->format('Y-m-d'));
This is not a proper way to test things. Why would you create over and over again records in your db? It's silly as with DataFixtures you can reach the same but you can do only once (and, more important, you don't need to "littering your test code").
Remember also that your db should be cleared and restored at every test (or, if you're able to do this, test "write" onto db with transaction and, in tearDown() function, discard changes)
Answer to your question
No, doctrine will not do things in async. way, your problem must be somewhere else.
I am occasionally getting the PHP Fatal error: Call to undefined method stdClass::transition() in agent.php on line 25 (I marked line 25 in the code). This code is called often, so struggling to see why it is happening.
Here is the snippet of agent.php that calls the
function agent_exam_complete($exam){
$ce = $exam->educational();
$ce->exam_id = $exam->exam_id;
$ce->exam_grade = $exam->score;
$ce->exams_remaining -= 1;
$ce->exam_received_date = sql_now();
if($exam->status()=='passed'){
$ce->transition('passed');
}elseif($ce->exams_remaining <= 0){
$ce->transition('failed');
}
$ce->save();
if($ce->is_certification_completed($ce->certification_id, $ce->client_no)){
agent_certification_complete($ce->certification_id, $ce->client_no);
}
}
function agent_certification_complete($certification_id, $client_no){
$ce = ClientPurchase::find('first', array('conditions' => "certification_id = '$certification_id' and is_certification = 1 and client_no='$client_no'"));
$ce->certification_date = date('Y-m-d');
$ce->transition('passed'); **//Line 25**
$ce->save();
}
transition() is defined in another file and is called often. I've included a little bit of it's code just for flavor.
function transition($event_tag){
$old_status = $this->status;
$next_status = $this->next_status_for_transition($event_tag);
if($next_status==''){
return; }
$this->status = $next_status;
My question is, why am I only getting this error periodically and not all the time? What can I do to eliminate the error and subsequent blank screen for my clients? I've only noticed that it is happening to those with Firefox or Chrome.
Thanks in advance,
Jim
The object $ce that contains the function is being generated multiple times. I suppose this is so transition is customized for whatever object is called.
Why not create another object for re-useable functions? Consider expanding the function so that it is compatible with all objects that would use it.
$my = new functionClass;
class functionClass
{
function transition()
{
$old_status = $this->status;
$next_status = $this->next_status_for_transition($event_tag);
if($next_status==''){
return; }
$this->status = $next_status;
}
}
$my->transition( 'passed' );
Something like that would cut down on unpredictability and I believe may solve your problem.
Try this little snippet of code to see whats going on:
$ce = false;
$ce->certification_date = date('Y-m-d');
var_dump($ce);
In this case $ce get cast to an object of stdClass when you try to set a property (certification_date).
Now your code:
function agent_certification_complete($certification_id, $client_no){
$ce = ClientPurchase::find('first', array('conditions' => "certification_id = '$certification_id' and is_certification = 1 and client_no='$client_no'"));
//$ce is probably false or null
//it gets cast to a stdClass object
$ce->certification_date = date('Y-m-d');
//stdClass does not have a transition method; ERROR
$ce->transition('passed'); **//Line 25**
$ce->save();
}
So in your code, if find() is returning null or false, or maybe some other choice values, $ce gets cast to a stdClass object on the next line. Then that stdClass object does not have a transition() method so you get an error.
To fix this, either adjust your find method or check its return value and handle accordingly.
As to it happening only in certain browser, I think thats a false conclusion. If find() is calling a query, it probably only happens at certain times depending on the result of that query.
I am trying to call a magento api method thanks to a php SoapClient object.
The problem is the method called creates magentos products and can be relatively long (up to 2 minutes). I need to get the returned values of this method but after a while, the soap call stops and return null.
$session_id = _get_session_id();
$client = new SoapClient($api_url . '&SID=' . $session_id, array('trace' => 1));
try {
$session = $client->login($api_user, $api_password);
$result = $client->call($session, 'api_call.method', array($arg1, $arg2);
}
catch(SoapFault $soapFault) {
...
}
I really need to get the called method return value, whatever the time it takes.
Do you know why the call return null after a while?
Is there a default timeout that can be configured?
Here is a solution, thanks to Jürgen comment:
ini_set('default_socket_timeout', 120); // 2 minutes
This set the call timeout to 2 minutes long