No way to pass mocked SoapClient in tests
Trying to write tests for next case. My app is calling soap service and correctly stores response to db. I have many response xml examples from service documentation app, so I use Mockery::mock(\SoapClient::class) to make partial mock and override __doRequest method to return expected xml. But after that i have no way to pass this class to AbstractSoapClientBase descendant. What i see:
- Default soap client is defined by constant in SoapClientInterface, so it is not possible to use class, created by mockery, or any anonimous class.
- AbstractSoapClientBase::initSoapClient, called by constructor, allways creates default SoapClient, because getSoapClientClassName() is called without params. There is no way to pass $soapClientClassName with options.
- If use setSoapClient() to set other implementation of soap client, SoapClient is created two times, first in initSoapClient, second is Mock.
- It is possible to override AbstractSoapClientBase::getSoapClientClassName class in tests, but then i need some sort of injection to pass overrided class to application instead of original only in tests. It would be much simplier to do so with configs.
Can you paste your testing code? Or create a Gist containing the files
Currently i use Laravel container to Mock Serivce
In application
$options = array(
AbstractSoapClientBase::WSDL_URL => 'https://xxxx?wsdl',
AbstractSoapClientBase::WSDL_CLASSMAP => ClassMap::get(),
);
$service = app()->make(new Service($options));
$service->setSoapHeaderHeader($header);
if ($send->sendToESP(new ServiceRequest()) !== false) {
print_r($send->getResult());
} else {
print_r($send->getLastError());
}
in test
protected function mockApi($xml): void
{
$this->app->bind(Service::class, function ($app, $params) use ($xml) {
[$options] = $params;
$soapClientMock = Mockery::mock(SoapClient::class, [
$options[AbstractSoapClientBase::WSDL_URL],
[
'classmap' => $options[AbstractSoapClientBase::WSDL_CLASSMAP],
],
]);
$soapClientMock->makePartial();
$soapClientMock->shouldReceive('__doRequest')->andReturn($xml);
$soapClientMock->shouldReceive('__getLastResponse')->andReturn($xml);
$serviceMock = new Service();
$serviceMock->setSoapClient($soapClientMock);
return $serviceMock;
});
}
But as you can see, it's very complicated solution.
I currently don't see a solution to solve your issue. It's working this way and modifying it would myabe break the usage long ago designed as it is. Feel free to propose a way to ease your issue.
One of improvement that i currently can see, is to add config option for class name, and pass this option to getSoapClientClassName in initSoapClient, because currently there is no other way to pass any value to getSoapClientClassName
Maybe I'm wrong but imagine you:
- create your own SoapClient class
- create your own SoapClientBase class from which the ServiceType class would inherit
- use SoapClientBase as the main class using the --soapclient option when generating the package (if you're actually using the https://github.com/WsdlToPhp/PackageGenerator project)
- mock the SoapClient class in your test or change its behaviour depending on the current "env" context (prod, dev, test)