How to test specific validation errors in php unit thrown in validation error ? with below code we could check session has errors, but not the exact error
$this->assertSessionHasErrors();
How to test specific validation errors in php unit thrown in validation error ? with below code we could check session has errors, but not the exact error
$this->assertSessionHasErrors();
assertSessionHasErrors
can receive an array, as documented:
$this->assertSessionHasErrors([
'field' => 'Field error message.'
]);
Got the answer
$errors = session('errors');
$this->assertSessionHasErrors();
$this->assertEquals($errors->get('name')[0],"Your error message for validation");
$errors is MessageBag object which stored in laravel session when validation error thrown using $errors->get('name') you could see all the validation errors as an array
You may use the combination of assertStatus
and assertJson
...
->assertStatus(422)
->assertJson([
'errors' => [
'field' => [
'Error message'
]
]
]);
You can use $response->assertSessionHasErrors('key')
https://laravel.com/docs/7.x/http-tests#assert-session-has-errors
an example for required attribute will be
$response = $this->json('POST', '/api/courses', $this->data([
'name' => '',
'api_token' => $this->user->api_token
]));
$response->assertSessionHasErrors('name');
You can add an extra assertion, to make sure that no entry was added to the database, in this case "assert no course was added"
$this->assertCount(0, Course::all());
For multiple required attributes you may use a loop something like the following:
collect(['name', 'description', 'amount'])->each(function ($field) {
$response = $this->json('POST', '/api/courses', $this->data([
$field => '',
'api_token' => $this->user->api_token
]));
$response->assertSessionHasErrors($field);
$this->assertCount(0, Course::all());
});
First I use
$this->post()
instead of
$this->jsonPost()
Dont know why, for certain reason, the session would not come out.
Then I just use
$response->assertSessionHasErrors('field_name', 'Error Message!');
To find out what are the error message, you must dump it
$response->dumpSession();
There is also a more elegant way in my opinion:
If you throw an exception via the class GeneralException you can check in a unit test if the session has a flash_danger from throwing a exception.
Lets do a practical example: We want to test that the admin cannot activate an already activated catalogue item.
Test function
public function an_admin_cannot_activate_an_activated_catalogue()
{
$catalogue = factory(Catalogue::class)->states('active')->create();
$response = $this->get("/admin/questionnaire/catalogue/{$catalogue->id}/activate");
$response->assertSessionHas(['flash_danger' => __('The catalogue item is already activated.')]);
}
Model/Repro function
If it is activated we throw an Exception which then can be checked by the test function.
public function activate(Catalogue $catalogue) : Catalogue
{
if ($catalogue->is_active) {
throw new GeneralException(__('The catalogue item is already activated.'));
}
$catalogue->is_active = 1;
$activated = $catalogue->save();
if($activated) {
return $catalogue;
}
}
actually you can easily throw errors from validation using dd()
and session('errors')
since errors bag is stored in session you could add dd(session('errors'))
in your unit tests to see which fields you are missing.
and finally you can write more proper test by adding $response->assertSessionHasErrors('field_name');
Laravel 7; In my case, I needed to ensure there was no error.
But below did ignore form-validation errors (at least mine).
$response->assertSessionHasNoErrors();
Hence I created a custom assert function in base TestCase
class, like:
use PHPUnit\Framework\Constraint\RegularExpression;
// ...
public static function assertNoErrorReport(TestResponse $response)
{
$error = static::getViewError($response);
if ( ! empty($error)) {
$this->fail('View contains error:' . PHP_EOL . $error);
}
$response->assertSessionHasNoErrors();
}
public function assertHasErrorRegExp(string $pattern, TestResponse $response, string $message = '')
{
$error = static::getViewError($response);
static::assertThat($error, new RegularExpression($pattern),
empty($message) ? $error : $message);
}
public static function getViewError(TestResponse $response)
{
$content = $response->getOriginalContent();
if ( ! $content) {
static::fail('View content missing.');
}
if ($content instanceof View) {
$data = $content->gatherData();
$error = $data['error'] ?? $data['errors'] ?? null;
// Casts array to string.
if (is_array($error)) {
$error = '[' . join(', ', $error) . ']';
}
// Casts Error-bag to string.
$error = '' . $error;
if ($error === '[]') {
return null;
}
} else {
static::fail('Response is not a View.');
}
return $data;
}
However, my assertHasErrorRegExp(...)
could be used for OP's case.