Тест Larvel phpunit терпит неудачу. Переход на непредвиденную страницу. Только в тесте

Итак, у меня есть страница с кнопкой на ней со значением "Создать" . Когда я нажимаю кнопку "Создать" , не заполняя ни одного из полей, она проверяет форму и отображает сообщения об ошибках на одной странице. Когда я делаю это в браузере, он работает нормально, но когда я делаю это с phpunit, он имеет неожиданные результаты, и я не знаю почему.

Вот мой интеграционный тест:

public function testCreateValidation()                                     
{ 
    $this->visit(route('patients.indexes.create', $this->patient->id));    
    $this->press('Create');                                                
    $this->seePageIs(route('patients.indexes.create', $this->patient->id));              
}   

И это результат:

There was 1 failure:
1) Tests\Integration\IndexControllerTest::testCreateValidation
Did not land on expected page [http://localhost/patients/69/indexes/create].

Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'http://localhost/patients/69/indexes/create'
+'http://localhost/patients'

/vagrant/vendor/laravel/framework/src/Illuminate/Foundation/Testing/InteractsWithPages.php:141
/vagrant/tests/Integration/IndexControllerTest.php:51

Я не понимаю, почему он перенаправляется на страницу patients.

Вот проверенный метод Laravel create:

public function create($id)                                                         
{                                                                                   
    $index = $this->indexes->newInstance();                                         
    $patient = $this->patients->findOrFail($id);                                    

    return view('patient.index.create', ['index' => $index, 'patient' => $patient]);
} 

И вот соответствующий раздел представления create:

<?= Form::open(['route' => array('patients.indexes.store', $patient->id), 'class' => 'form-horizontal']) ?>
    @include('patient.index._form')
    <?= Form::submit('Create', ['class' => 'btn btn-primary']) ?>
<?= Form::close() ?> 

И, наконец, метод store, который он отправляет:

public function store(IndexRequest $request, $id)       
{                                                       
    $index = $this->indexes->newInstance();             
    $index->fill($request->all());                      
    $index->patient_id = $id;                           
    $index->save();                                     

    $patient = $index->patient;                         

    return redirect()->route('patients.edit', $patient);
} 

Я также использую FormRequest для проверки ввода:

public function rules()                   
{                                         
    return [                              
        'index_title' => 'required',      
        'index_description' => 'required',
    ];                                    
}  

Таким образом, по существу, поскольку он не выполняет валидацию в IndexRequest, IndexRequest должен отбросить его обратно в представление patients.indexes.create и отобразить ошибки. Но по какой-то причине его удаляют на страницу пациентов (это ТОЛЬКО случается в тесте, если я попробую его вручную щелкнуть по кнопке "Создать" в браузере, он работает как ожидалось)

У меня была эта проблема раньше, но я не смог ее решить. Любые идеи?

Ответ 1

Похоже, что у вас проблема CSRF. Когда вы переходите к форме в своем браузере, Laravel хранит специальный токен в ваших куки файлах браузера и в заголовках запросов. Затем, когда вы отправляете форму, он ищет этот токен, чтобы убедиться, что вы тот, кто отправляет форму.

Когда вы тестируете с помощью PHPUnit, этот токен не отправляется, поэтому вам нужно либо отправить его самостоятельно, либо вы должны исключить свои тесты из промежуточного программного обеспечения, которое проверяет токен. Исключением ваших тестов является более простой метод.

В Laravel 5.0 я сделал это, переопределив метод handle() в промежуточном программном обеспечении VerifyCsrfToken.

class VerifyCsrfToken extends BaseVerifier {
    // Override the handle() method in the BaseVerifier class
    public function handle($request, Closure $next) {
        // When the App environment is 'testing', skip CSRF validation.
        if (app()->environment() === 'testing') {
            return $next($request);
        }

        return parent::handle($request, $next);
    }
}

По умолчанию PHPUnit вытаскивает переменные окружения из файла phpunit.xml в ваш корень сайта Laravel. Переменная APP_ENV - это та, которая управляет именем среды приложения, и по умолчанию используется "тестирование". Если у вас другое, вам нужно изменить код, который я предоставил, чтобы соответствовать ему.

Ответ 2

Если промежуточное программное обеспечение на самом деле является проблемой, вы можете исключить его из тестов с использованием свойства без ключа:

class IndexControllerTest extends TestCase
{
    use Illuminate\Foundation\Testing\WithoutMiddleware;
    ...