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.'
]);
ValidationException like so $ex->errors()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
assertSessionHasErrors: laravel.com/docs/6.x/http-tests#assert-session-has-errorssetUp()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());
});
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.
In Laravel 8+, you can use assertInvalid to test for specific validation errors
// Assert that these fields have validation errors
$response->assertInvalid(['name', 'email']);
// Assert that these fields give particular validation fail messages
$response->assertInvalid([
'name' => 'The name field is required.',
'email' => 'valid email address',
]);
https://laravel.com/docs/10.x/http-tests#validation-assertions
Requestor rules, you can do$this->expectException$this->expectException('Illuminate\Validation\ValidationException');and$this->expectExceptionMessage = "The name field is required";- see also stackoverflow.com/questions/18647836/…The given data was invalid