测试时如何模拟整个应用程序的 class 方法?
How to mock class method for whole application when testing?
我有 ServiceApi.php - 在构造函数中它有默认的 guzzle 客户端:
$this->client = new Client($options);
否则它有方法:
public function fetch()
{
return $this->client->get('http://......')->getBody()->getContents();
}
另一个 class ServiceUser.php - 有使用 ServiceApi 的方法:
public function fetchFromApi()
{
return (new ServiceApi())->fetch();
}
当我 运行 测试时,我想要 (new ServiceUser())->fetchFromApi()
- 不要调用我在测试中硬编码的真实 api 和 return 预定义答案。
试图在测试中模拟 ServiceApi,但它只在测试方法中工作,当通过 ServiceUser 调用时它会变成真实的 api。
这样做是真的吗?
或者我试图做一些不可能的事情或者这个代码结构不符合测试目的?
您需要了解 Dependency Injection and Service Container 个概念。满足您的需求:
class ServiceApi {
public function __construct(Client $client)
{
$this->client = $client;
}
}
class ServiceUser {
public function __construct(ServiceApi $api)
{
$this->api = $api;
}
}
并在AppServiceProvider中配置Client:
public function register()
{
$this->app->bind(ServiceApi::class, function($app){
//I don't know where from you get options
$options = [];
$client = new Client($options);
return new ServiceApi($client);
});
}
现在,在测试中你可以这样做:
public function testFetch()
{
$mock = \Mockery::mock(ServiceApi::class);
$mock->shouldReceive('fetch')->once();
$this->instance(ServiceApi::class, $mock);
//now test
}
像马克西姆说的那样实现了。
应用服务供应商:
$this->app->bind(ApiInterface::class, function($app, $params){
switch ($params['account']->type) {
case 'first':
$class = 'App\Classes\FirstApi';
break;
case 'second':
$class = 'App\Classes\SecondApi';
break;
default:
throw new \Exception('unknown account type');
}
return new $class($params['account']);
});
UseApi 特征:
public function api()
{
return \App::makeWith(ApiInterface::class, ['account' => $this->account]);
}
但是在测试中进行模拟时,我遇到了一些问题,导致服务提供商中的参数绑定。
测试:
// does we need mock FirstApi instead ApiInterface?
// But working only with FirstApi
$mock = \Mockery::mock(FirstApi::class)->makePartial();
$mock->shouldReceive('methodApi') // mock methodApi
->once()
->andReturn('foo');
// $this->instance(......) does't work - I think it's bindings issue,
// replaced it with bind()
$this->app->bind(ApiInterface::class, function() use ($mock){
return $mock;
});
$result = $model->methodApi();
$this->assertEquals('foo',$result);
现在它过去了!
我有 ServiceApi.php - 在构造函数中它有默认的 guzzle 客户端:
$this->client = new Client($options);
否则它有方法:
public function fetch()
{
return $this->client->get('http://......')->getBody()->getContents();
}
另一个 class ServiceUser.php - 有使用 ServiceApi 的方法:
public function fetchFromApi()
{
return (new ServiceApi())->fetch();
}
当我 运行 测试时,我想要 (new ServiceUser())->fetchFromApi()
- 不要调用我在测试中硬编码的真实 api 和 return 预定义答案。
试图在测试中模拟 ServiceApi,但它只在测试方法中工作,当通过 ServiceUser 调用时它会变成真实的 api。
这样做是真的吗? 或者我试图做一些不可能的事情或者这个代码结构不符合测试目的?
您需要了解 Dependency Injection and Service Container 个概念。满足您的需求:
class ServiceApi {
public function __construct(Client $client)
{
$this->client = $client;
}
}
class ServiceUser {
public function __construct(ServiceApi $api)
{
$this->api = $api;
}
}
并在AppServiceProvider中配置Client:
public function register()
{
$this->app->bind(ServiceApi::class, function($app){
//I don't know where from you get options
$options = [];
$client = new Client($options);
return new ServiceApi($client);
});
}
现在,在测试中你可以这样做:
public function testFetch()
{
$mock = \Mockery::mock(ServiceApi::class);
$mock->shouldReceive('fetch')->once();
$this->instance(ServiceApi::class, $mock);
//now test
}
像马克西姆说的那样实现了。
应用服务供应商:
$this->app->bind(ApiInterface::class, function($app, $params){
switch ($params['account']->type) {
case 'first':
$class = 'App\Classes\FirstApi';
break;
case 'second':
$class = 'App\Classes\SecondApi';
break;
default:
throw new \Exception('unknown account type');
}
return new $class($params['account']);
});
UseApi 特征:
public function api()
{
return \App::makeWith(ApiInterface::class, ['account' => $this->account]);
}
但是在测试中进行模拟时,我遇到了一些问题,导致服务提供商中的参数绑定。
测试:
// does we need mock FirstApi instead ApiInterface?
// But working only with FirstApi
$mock = \Mockery::mock(FirstApi::class)->makePartial();
$mock->shouldReceive('methodApi') // mock methodApi
->once()
->andReturn('foo');
// $this->instance(......) does't work - I think it's bindings issue,
// replaced it with bind()
$this->app->bind(ApiInterface::class, function() use ($mock){
return $mock;
});
$result = $model->methodApi();
$this->assertEquals('foo',$result);
现在它过去了!