2015-07-30 7 views
3

Я новичок в модульном тестировании и пытаюсь протестировать метод контроллера в 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'=>'[email protected]'])->assertJson(
      '{"mailchimpListAlreadySubscribed": "[email protected] is already subscribed to the list."}' 
     ); 
    } 
} 

У меня есть PHPUnit возвращение неисправного тест.

HomeRouteTest :: testMailchimpReturnsDuplicate Издевательство \ Exception \ InvalidCountException: Метод подписки() из Mockery_0_Mailchimp_Lists следует назвать ровно 1 раз, но называется 0 раз.

Кроме того, если я утверждаю код состояния 422, PHPUnit сообщает он получает код состояния 200.

Это прекрасно работает, когда я проверить это вручную, но я полагаю, я с видом что-то довольно легко.

+0

Привет. Каков ваш полный путь к классу «Mailchimp»? Не псевдоним, а полный путь с пространством имен. –

+0

Я использую пакет skovmand/mailchimp-laravel здесь: https://github.com/skovmand/mailchimp-laravel. Класс Mailchimp находится в глобальном пространстве имен, а require_once - множество других классов в файле. – Lea

+0

Вы издеваетесь над Mailchimp_Lists, но я не вижу, чтобы этот класс загружался в любом месте вашего контроллера. – PickYourPoison

ответ

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); 
} 
0

Насмешка будет ожидать класс, передаваемый к контроллеру быть фиктивным объектом, как вы можете видеть здесь, в их docs:

class Temperature 
{ 
    public function __construct($service) 
    { 
     $this->_service = $service; 
    } 
} 

Unit Test

$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

Я вижу, что вы пытаетесь переписать переменную элемента списка с помощью нового макета перед вызовом контроллера. Вы уверены, что объект списка заменяется на вас, насмехаясь над ним? Попытайтесь увидеть, какие классы находятся в контроллере, когда он загружается, и посмотрите, действительно ли он переопределен.

+0

Я смог подтвердить, что контроллер не получает издеваемую версию класса Mailchimp. Большая разница между моим тестом и вашим примером заключается в том, что я использую функции тестирования интеграции для тестирования контроллера, и вы создаете экземпляр класса. – Lea

Смежные вопросы