模拟返回模拟的类参数

时间:2015-07-30 09:02:04

标签: php unit-testing mockery laravel-5.1

我是单元测试的新手,并尝试在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.

当我手动测试时,它工作正常,但我想我忽略了一些相当容易的东西。

2 个答案:

答案 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);
}