我是单元测试的新手,并试图在Laravel 5.1和mock中测试控制器方法。
我正在尝试测试我写的registerEmail
方法,如下:
<?php
namespace App'Http'Controllers;
use Illuminate'Http'Request;
use Response;
use Mailchimp;
use Validator;
/**
* Class ApiController
* @package App'Http'Controllers
*/
class ApiController extends Controller
{
protected $mailchimpListId = null;
protected $mailchimp = null;
public function __construct(Mailchimp $mailchimp)
{
$this->mailchimp = $mailchimp;
$this->mailchimpListId = env('MAILCHIMP_LIST_ID');
}
/**
* @param Request $request
* @return 'Illuminate'Http'JsonResponse
*/
public function registerEmail(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
]);
$email = $request->get('email');
try {
$subscribed = $this->mailchimp->lists->subscribe($this->mailchimpListId, [ 'email' => $email ]);
//var_dump($subscribed);
} catch ('Mailchimp_List_AlreadySubscribed $e) {
return Response::json([ 'mailchimpListAlreadySubscribed' => $e->getMessage() ], 422);
} catch ('Mailchimp_Error $e) {
return Response::json([ 'mailchimpError' => $e->getMessage() ], 422);
}
return Response::json([ 'success' => true ]);
}
}
我试图模拟Mailchimp对象在这种情况下工作。到目前为止,我的测试如下:
<?php
use Illuminate'Foundation'Testing'WithoutMiddleware;
use Illuminate'Foundation'Testing'DatabaseMigrations;
use Illuminate'Foundation'Testing'DatabaseTransactions;
class HomeRouteTest extends TestCase
{
use WithoutMiddleware;
public function testMailchimpReturnsDuplicate() {
$listMock = Mockery::mock('Mailchimp_Lists')
->shouldReceive('subscribe')
->once()
->andThrow('Mailchimp_List_AlreadySubscribed::class);
$mailchimp = Mockery::mock('Mailchimp')->lists = $listMock;
$this->post('/api/register-email', ['email'=>'duplicate@email.com'])->assertJson(
'{"mailchimpListAlreadySubscribed": "duplicate@email.com is already subscribed to the list."}'
);
}
}
我有phpUnit返回一个失败的测试。
HomeRouteTest:: testMailchimpReturnsDuplicatemock 'Exception'InvalidCountException:方法subscribe()从Mockery_0_Mailchimp_Lists应该被调用恰好1次,但调用0次。
同样,如果我断言状态码是422,phpUnit报告它正在接收状态码200。
当我手动测试它时,它工作得很好,但我想我忽略了一些相当容易的东西。
我自己解决了。我最终将订阅移动到一个单独的Job类中,并且能够在测试文件中重新定义Mailchimp类。
class Mailchimp {
public $lists;
public function __construct($lists) {
$this->lists = $lists;
}
}
class Mailchimp_List_AlreadySubscribed extends Exception {}
和一个测试
public function testSubscribeToMailchimp() {
// create job
$subscriber = factory(App'Models'Subscriber::class)->create();
$job = new App'Jobs'SubscribeToList($subscriber);
// set up Mailchimp mock
$lists = Mockery::mock()
->shouldReceive('subscribe')
->once()
->andReturn(true)
->getMock();
$mailchimp = new Mailchimp($lists);
// handle job
$job->handle($mailchimp);
// subscriber should be marked subscribed
$this->assertTrue($subscriber->subscribed);
}
mock将期望传递给控制器的类是一个mock对象,您可以在他们的文档中看到:
class Temperature
{
public function __construct($service)
{
$this->_service = $service;
}
}
单元测试$service = m::mock('service');
$service->shouldReceive('readTemp')->times(3)->andReturn(10, 12, 14);
$temperature = new Temperature($service);
在laravel IoC中,它自动加载类并注入它们,但由于它不是自动加载Mailchimp_Lists
类,因此它不会是模拟对象。Mailchimp需要在它的主类require_once 'Mailchimp/Lists.php';
之上的类
然后Mailchimp在构造函数
中自动加载类$this->lists = new Mailchimp_Lists($this);
我不认为你能很容易地开箱模拟那门课。因为没有办法将模拟对象传递给Mailchimp类并让它替换实际Mailchimp_Lists
的实例
我看到你试图在调用控制器之前用一个新的Mock覆盖列表成员变量。您确定列表对象已被替换为您所模拟的对象吗?试着看看当它被加载时控制器中的类是什么,看看它是否实际上被覆盖了。