我是单元测试的新手,并尝试在Laravel 5.1和Mockery中测试控制器方法。
我正在尝试测试我写的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 :: testMailchimpReturnsDuplicate Mockery \ Exception \ InvalidCountException:来自Mockery_0_Mailchimp_Lists的方法subscribe()应该被正确调用1次,但是被调用0次。
此外,如果我断言状态代码为422,则phpUnit报告它正在接收状态代码200.
当我手动测试时,它工作正常,但我想我忽略了一些相当容易的东西。
答案 0 :(得分:0)
Mockery希望传入控制器的类是一个模拟对象,你可以在他们的docs中看到:
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覆盖lists成员变量。你确定列表对象正在被你模拟的替换吗?尝试看一下加载时控制器中的类是什么,看看它是否实际上被覆盖了。
答案 1 :(得分:0)
我设法自己解决了。我最终将订阅转移到一个单独的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);
}