我的Lumen应用程序中有以下控制器类来实现路由控制器:
<?php
class MyController {
public function route_method(Request $request) {
// some code
$success = $this->private_method($request->get('get_variable'));
// some code
return \response()->json(['results' => $success]);
}
private function private_method($data) {
// some code, calling 3rd party service
return $some_value;
}
}
Lumen应用程序web.php
中的以下相应路由:
<?php
$app->get('/endpoint', ['uses' => 'MyController@route_method']);
现在我想编写单元测试,确认调用返回的响应/endpoint
返回包含'results': true
的键/值对的预期JSON响应,但不允许route_method()
通过嘲笑后者来调用private_method()
,因为 - 在评论中 - private_method()
调用第三方服务而我想避免这种情况,所以我想我需要这样的事情:
<?php
class RouteTest extends TestCase {
public function testRouteReturnsExpectedJsonResponse() {
// need to mock the private_method here somehow first, then...
$this->json('GET', '/endpoint', ['get_variable' => 'get_value'])->seeJson(['results' => true]);
}
}
但是我如何为此目的使用Mockery
,还是有另一种隔离第三方通话的方式?
答案 0 :(得分:1)
您无法模拟此代码这一事实表明代码设计不当。 我在这里展示的例子只是一个想法,重点是创建一个新的类,代表与第三方系统的通信。
<?php
namespace App\Http\Controllers;
use App\Services\MyService;
class MyController
{
public function __construct(MyService $service)
{
$this->service = $service;
}
public function route_method(Request $request)
{
// some code
$success = $this->service->some_method($request->get('get_variable'));
// some code
return \response()->json(['results' => $success]);
}
}
然后按照单一责任原则
创建另一个应该做的事情的课程<?
namespace App\Services;
class MyService
{
public function some_method($variable)
{
//do something
}
}
然后你可以正确模拟:
<?php
class RouteTest extends TestCase {
public function testRouteReturnsExpectedJsonResponse() {
$service = $this->getMockBuilder('App\Services\MyService')
->disableOriginalConstructor()
->getMock();
$somedata = 'some_data' //the data that mock should return
$service->expects($this->any())
->method('some_method')
->willReturn($somedata);
//mock the service instance
$this->app->instance('App\Services\MyService', $service);
// need to mock the private_method here somehow first, then...
$this->json('GET', '/endpoint', ['get_variable' => 'get_value'])->seeJson(['results' => true]);
}
}
答案 1 :(得分:1)
基本上,你没有。
您应该测试行为,而不是实施。私有方法是一个实现细节。
尽管如此,你可以自由地做任何你想做的事情,Laravel / Lumen有很多选择:
正确方法:
看看@Felippe Duarte的回答。使用Mockery而不是PHPUnit添加测试代码进行模拟:
<?php
class RouteTest extends TestCase
{
public function testRouteReturnsExpectedJsonResponse()
{
$someData = 'some_data'; //the data that mock should return
$service = Mockery::mock('App\Services\MyService');
$service->shouldReceive('some_method')->once()->andReturn($someData);
//mock the service instance
$this->app->instance('App\Services\MyService', $service);
// need to mock the private_method here somehow first, then...
$this->json('GET', '/endpoint', ['get_variable' => 'get_value'])->seeJson(['results' => $someData]);
}
}
服务容器滥用方式:
控制器:
<?php
class MyController {
public function route_method(Request $request) {
// some code
$success = $this->private_method($request->get('get_variable'));
// some code
return \response()->json(['results' => $success]);
}
private function private_method($data) {
// say third party is some paypal class
$thirdParty = app(Paypal::class);
return $thirdParty->makePayment($data);
}
}
测试:
<?php
class RouteTest extends TestCase
{
public function testRouteReturnsExpectedJsonResponse()
{
$someData = 'some_data'; //the data that mock should return
$paypalMock = Mockery::mock(Paypal::class);
$paypalMock->shouldReceive('makePayment')->once()->with('get_value')->andReturn($someData);
//mock the service instance
$this->app->instance(Paypal::class, $paypalMock);
// need to mock the private_method here somehow first, then...
$this->json('GET', '/endpoint', ['get_variable' => 'get_value'])->seeJson(['results' => $someData]);
}
}
这将有效,因为Laravel的Service Container会认识到您在尝试实例化Paypal::class
时会定义,它应该返回在测试中创建的模拟。
不推荐这样做,因为它很容易被滥用,而且不是很明确。