Это связано с этим вопросом , но после этого решения не удалось решить мою проблему. Я также понимаю, что в собственной документации Laravel говорится, что вы не должны издеваться над объектом Request
, но я не уверен, как еще писать этот тест.
Вот видимость кода, который я хочу проверить:
public function getThirdSegment() { return Request::segment(3); }
Вот тест, который у меня есть:
/** * @test */ public function indexReturnsOk() { $this->prepareForTests(); $this->call('GET', '/api/v1/courses'); $this->assertResponseOk(); $this->assertResponseStatus(200); }
Это не работает, потому что Request::segment(3)
возвращает null при запуске PHPUnit. Сначала я попытался высмеять объект Request следующим образом:
Request::shouldReceive('segment')->andReturn('courses');
Но он все равно возвращает null. Затем я попробовал это:
$request = m::mock('Illuminate\Http\Request'); $request->shouldReceive('segment')->andReturn('courses'); Input::swap($request);
И метод segment
по-прежнему возвращает значение null. Есть ли способ высмеять возвращаемое значение этого метода?
Обновить
Этот код находится в методе регистрации поставщика услуг, но я не думаю, что это является причиной проблемы. Нахождение сайта в браузере делает то, что я ожидаю от него, но запуск PHPUnit, похоже, не соответствует ни маршруту, ни URL-адресу, ни тому, что связано с самим запросом.
Лучший ответ здесь пока что я делал это неправильно. Поставщики услуг работают до того, как контроллер еще загружен, а при модульном тестировании Larvel's Illuminate\Foundation\Testing\TestCase
загружает приложение (вызывая все службы и службы boot
и методы register
) во время выполнения метода setUp
, перед тем, как вызовы могут быть оформлены во время выполнения любого отдельного теста.
Я попытался найти решение, переместив логику и получив кое-что, что-то вроде:
class MyTestClass extends TestCase { public function setUp() { // No call to parent::setUp() // From: Illuminate\Foundation\Testing\TestCase $this->app = $this->createApplication(); $this->client = $this->createClient(); // Not this one! //$this->app->setRequestForConsoleEnvironment(); $this->app->boot(); // ... } public function testWhatever() { // Calls to this will now actually have a request object $this->call('GET', '/api/v1/courses'); } }
Но это просто не может быть правильным, по крайней мере, это не так.
Вместо этого я полагаю, что лучше всего не полагаться на что-либо в объекте Request
изнутри Поставщиков услуг. Вместо этого, почему бы просто не вставить объект, который может делать то, что мне нужно, чтобы сделать это в контроллере, в котором я хочу? Никакой поставщик услуг не нужен, и я могу легко высмеять любой объект, кроме Request
в модульных тестах. Я должен был верить документам.
Обновить
Принимая этот ответ на мой собственный вопрос немного дальше, я считаю, что моя первоначальная ошибка заключалась в том, что я использовал объект Request
в Провайдере. Я думаю, по ретроспекции, что вы, вероятно, никогда не должны использовать объект Request
вообще у поставщика услуг, потому что поставщики загружаются для всего, что связано с laravel (включая команды мастеров, которые, конечно, не должны иметь никакого запроса). Мой исходный код работал в браузере, но я, вероятно, заметил бы проблемы, если бы попытался запустить любые команды artisan.
Я наткнулся на это, пытаясь найти хороший способ обработать композитор представления, который использует этот запрос, и в конечном итоге закончил работу с инсталляцией конструктора – поскольку Laravel использует класс Symfony Request
, было очень просто «высмеять» запрос с запросом Request::create('http://myurl.com')
и передать его в экземпляр моего класса для тестирования. Гораздо лучше, чем пытаться использовать методы stubbing в классе запросов и не полагается на создание всего приложения Laravel.
Для ясности, композитор представления выглядит в целом следующим образом:
class SiteLayoutComposer extends BaseComposer { function __construct(Request $request) { $this->request = $request; } public function compose($view) { $templateName = preg_match('/siteurlexample/', $this->request->getHost()) ? 'site1' : 'site2'; $view->with('siteLayout', "layouts.sites.{$templateName}"); } }
И тест:
class SiteLayoutComposerTest extends TestCase { public function testWillSetTheSiteLayoutToSite1() { $request = Request::create('http://www.siteurlexample.com'); $view = View::make('home'); $composer = new SiteLayoutComposer($request); $composer->compose($view); $this->assertEquals('layouts.sites.site1', $view->siteLayout); } }