princeoo7's avatar

Whats wrong with my test case in TDD ? Please help me understand !

I am new to TDD and was trying out test in practice and came across this failure. can some one assist me on understanding the error ?

//Terminal

PHPUnit 7.5.6 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 848 ms, Memory: 18.00 MB

There was 1 failure:

1) Tests\Feature\ArticleTest::a_user_can_create_a_article
Failed asserting that a row in the table [articles] matches the attributes {
    "title": "Demo Article Title 1",
    "slug": "demo-article-title-1",
    "banner": "https:\/\/placehold.it\/600x600",
    "description": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure atque repellat perspiciatis ducimus, ea architecto dolor cupiditate! Autem aperiam similique libero rerum minima quis. Sunt sit possimus soluta modi esse.",
    "category_id": 1,
    "status": 2,
    "author_id": 1,
    "moderator_id": 1,
    "created_at": "2019-03-05 18:42:27",
    "updated_at": "2019-03-05 18:42:27"
}.

Found: [
    {
        "id": 1,
        "title": "Demo Article Title 1",
        "slug": "demo-article-title-1",
        "banner": "https:\/\/placehold.it\/600x600",
        "description": null,
        "category_id": "1",
        "body": null,
        "views": 1,
        "status": 2,
        "author_id": 1,
        "moderator_id": 1,
        "created_at": "2019-03-05 18:42:27",
        "updated_at": "2019-03-05 18:42:27"
    }
].

C:\xampp\htdocs\pdpv2\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase.php:24
C:\xampp\htdocs\pdpv2\tests\Feature\ArticleTest.php:41

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
The terminal process terminated with exit code: 1

and

//ArticleTest.php

/** @test */
public function a_user_can_create_a_article()
{
    $this->withoutExceptionHandling();

    $this->actingAs(factory('App\User')->create());

    $attributes = [
        'title' => 'Demo Article Title 1',
        'slug' => 'demo-article-title-1',
        'banner' => 'https://placehold.it/600x600',
        'description' => 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure atque repellat perspiciatis ducimus, ea architecto dolor cupiditate! Autem aperiam similique libero rerum minima quis. Sunt sit possimus soluta modi esse.',
        'category_id' => 1,
        'status' => 2,
        'author_id' => auth()->id(),
        'moderator_id' => auth()->id(),
        'created_at' => date('Y-m-d H:i:s'),
        'updated_at' => date('Y-m-d H:i:s')
    ];

    $this->post('/articles', $attributes);

    $this->assertDatabaseHas('articles', $attributes);
}

please help me out with this error.

0 likes
14 replies
tykus's avatar

The description doesn't match. Might be a mass-assignment issue on the model.

Realistically, you will never be submitting a form with the created_at or updated_at properties, so why are you including that in your test. This might also be true for the slug (if you calculate it on the server), or uploaded images, or the authenticated user id etc.

This $this->post('/articles', $attributes); should be representative of the actual POST request that your application will be handling; so, if a field is not in the form, do not include them in the test payload.

1 like
princeoo7's avatar

@TYKUS - I kept the description as nullable so i thought it will be ignored. we your pointing was right. it was description.

about the created_at and updated_at, i just added it later on as i was not able to find what was going wrong.

not the next problem i am facing is how to validate description as null ...

$attributes = request()->validate([
    'title' => 'required',
    'slug' => 'required',
    'banner' => 'required',
    'description' => 'nullable|string',
'body' => 'nullable|text',
    'author_id' => 'required',
    'moderator_id' => 'required'
]);

the above made the test pass be here as the description is required, and a null / empty string is passed, i get

PHPUnit 7.5.6 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 1.06 seconds, Memory: 18.00 MB

There was 1 failure:

1) Tests\Feature\ArticleTest::a_user_can_create_a_article
Failed asserting that a row in the table [articles] matches the attributes {
    "title": "Demo Article Title 1",
    "slug": "demo-article-title-1",
    "banner": "https:\/\/placehold.it\/600x600",
    "description": "",
"body": "",
    "category_id": 1,
    "status": 2,
    "author_id": 1,
    "moderator_id": 1
}.

Found: [
    {
        "id": 1,
        "title": "Demo Article Title 1",
        "slug": "demo-article-title-1",
        "banner": "https:\/\/placehold.it\/600x600",
        "description": null,
        "category_id": "1",
        "body": null,
        "views": 1,
        "status": 2,
        "author_id": 1,
        "moderator_id": 1,
        "created_at": "2019-03-05 19:19:41",
        "updated_at": "2019-03-05 19:19:41"
    }
].

C:\xampp\htdocs\pdpv2\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase.php:24
C:\xampp\htdocs\pdpv2\tests\Feature\ArticleTest.php:39

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
The terminal process terminated with exit code: 1

GertjanRoke's avatar

@princeoo7 phpunit could'nt find the category_id inside your database so thats why it returns with a error.

phpunit is looking for everything you said by using the $attributes and if there is missing one of the keys or the value's do not match it will fail, like in your case with the category_id.

And also with the description.

1 like
princeoo7's avatar

@GERTJANROKE - as you suggest i added category_id and description was added prior. still facing the same issue.

PHPUnit 7.5.6 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 1.39 seconds, Memory: 18.00 MB

There was 1 failure:

1) Tests\Feature\ArticleTest::a_user_can_create_a_article
Failed asserting that a row in the table [articles] matches the attributes {
    "title": "Demo Article Title 1",
    "slug": "demo-article-title-1",
    "banner": "https:\/\/placehold.it\/600x600",
    "description": "",
    "body": "",
    "category_id": 1,
    "status": 2,
    "author_id": 1,
    "moderator_id": 1
}.

Found: [
    {
        "id": 1,
        "title": "Demo Article Title 1",
        "slug": "demo-article-title-1",
        "banner": "https:\/\/placehold.it\/600x600",
        "description": null,
        "category_id": 1,
        "body": null,
        "views": 1,
        "status": 2,
        "author_id": 1,
        "moderator_id": 1,
        "created_at": "2019-03-05 19:59:25",
        "updated_at": "2019-03-05 19:59:25"
    }
].

C:\xampp\htdocs\pdpv2\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase.php:24
C:\xampp\htdocs\pdpv2\tests\Feature\ArticleTest.php:42

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
The terminal process terminated with exit code: 1

and the validation goes like this,

$attributes = request()->validate([
    'title' => 'required',
    'slug' => 'required',
    'banner' => 'required',
    'description' => 'nullable|string',
    'body' => 'nullable|text',
    'category_id' => 'nullable|integer',
    'author_id' => 'required',
    'moderator_id' => 'required'
]);

manelgavalda's avatar

@PRINCEOO7 - You are comparing empty string ("") against null, so this is failing because they are not the same:

Failed asserting that a row in the table [articles] matches the attributes {
    "title": "Demo Article Title 1",
    "slug": "demo-article-title-1",
    "banner": "https:\/\/placehold.it\/600x600",
    "description": "", // empty string
    "body": "", // empty string
    "category_id": 1,
    "status": 2,
    "author_id": 1,
    "moderator_id": 1
}.

Found: [
    {
        "id": 1,
        "title": "Demo Article Title 1",
        "slug": "demo-article-title-1",
        "banner": "https:\/\/placehold.it\/600x600",
        "description": null, // null
        "category_id": 1,
        "body": null, // null
        "views": 1,
        "status": 2,
        "author_id": 1,
        "moderator_id": 1,
        "created_at": "2019-03-05 19:59:25",
        "updated_at": "2019-03-05 19:59:25"
    }
2 likes
GertjanRoke's avatar

@princeoo7 your almost there, I think the problem is that in the data you have inside $attributes the value of description and body are "" and not null.

But I don't know how picky phpunit is, but with the mindset that phpunit is for strict testing I think this could be your problem.

The rest looks oke.

princeoo7's avatar

@MANELGAVALDA - i when through this and found below

https://laravel.com/docs/5.8/validation#form-request-validation

required

The field under validation must be present in the input data and not empty. A field is considered "empty" if 
one of the following conditions are true:

The value is null.
The value is an empty string.
The value is an empty array or empty Countable object.
The value is an uploaded file with no path.

then we have this section on the same page.

A Note On Optional Fields

By default, Laravel includes the TrimStrings and ConvertEmptyStringsToNull middleware in your application's global middleware stack. These middleware are listed in the stack by the App\Http\Kernel class. Because of this, you will often need to mark your "optional" request fields as nullable if you do not want the validator to consider null values as invalid. For example:

$request->validate([
    'title' => 'required|unique:posts|max:255',
    'body' => 'required',
    'publish_at' => 'nullable|date',
]);

In this example, we are specifying that the publish_at field may be either null or a valid date representation. If the nullable modifier is not added to the rule definition, the validator would consider null an invalid date.

so how to test a empty pass ? by default Laravel includes the TrimStrings and ConvertEmptyStringsToNull middleware.

GertjanRoke's avatar

@princeoo7 it depends on what you want, can the body be empty in some cases or those it always needs to be filled? That is your destitution and if it needs to be required you could test it in a test, if it can be null you can also test if the value can be null.

But the problem with your test is that the values do not match with the data inside your database.

manelgavalda's avatar

@PRINCEOO7 - Can you paste again you test code again? Im not sure but I think something like this should work:

public function a_user_can_create_a_article()
{
    $this->withoutExceptionHandling();

    $this->actingAs(factory('App\User')->create());

    $attributes = [
        'title' => 'Demo Article Title 1',
        'slug' => 'demo-article-title-1',
        'banner' => 'https://placehold.it/600x600',
        'description' => null,
        'body' => null,
        'category_id' => 1,
        'status' => 2,
        'author_id' => auth()->id(),
        'moderator_id' => auth()->id(),
        'created_at' => date('Y-m-d H:i:s'),
        'updated_at' => date('Y-m-d H:i:s')
    ];

    $this->post('/articles', $attributes);

    $this->assertDatabaseHas('articles', $attributes);
}

Or I will even delete the body and the description from the attributes, because if they are null, I don't see the value on passing them through the request:


    $this->withoutExceptionHandling();

    $this->actingAs(factory('App\User')->create());

    $attributes = [
        'title' => 'Demo Article Title 1',
        'slug' => 'demo-article-title-1',
        'banner' => 'https://placehold.it/600x600',
        'category_id' => 1,
        'status' => 2,
        'author_id' => auth()->id(),
        'moderator_id' => auth()->id(),
        'created_at' => date('Y-m-d H:i:s'),
        'updated_at' => date('Y-m-d H:i:s')
    ];

    $this->post('/articles', $attributes);

    $this->assertDatabaseHas('articles', $attributes);
princeoo7's avatar

@GERTJANROKE - it was the empty string as you said. i change it to null in the test and i got green.

now only question i have in mind it what will happen if in real time the form having all field sends a column blank. for example description field which is nullable. will it be considered as empty or null ?

GertjanRoke's avatar

@princeoo7 if the form input or textarea is left empty it will store null, this is because of the middleware you found called ConvertEmptyStringsToNull this middleware will indeed trim and convert a empty string like "" to null.

So for example your description form field can be nullable because your validation says so, so when you do not fill in the description field inside your form Laravel will convert the empty string to null and will store that inside your database, if you give it a string of course it will save the string you gave it.

GertjanRoke's avatar

@princeoo7 did you watch the video's of jeffery way where he is building stuff using TDD like "Build A Laravel App With TDD" or "Let's Build A Forum with Laravel and TDD"?

It are really good video's on how you should think about TDD and if you understand it maybe find some video's of Adam Wathan on YouTube of Vimeo, he has some great TDD video's but it's better to view them after you know some more about TDD.

1 like
princeoo7's avatar

@GERTJANROKE - currently i am in process of watching the Let's Build A Forum with Laravel and TDD. need to balance it with every thing else :)

this is because of the middleware you found called ConvertEmptyStringsToNull this middleware will 
indeed trim and convert a empty string like "" to null.

this is what i was saying that the code should be converted from empty to null when i ran the test. or is it that the test is not able to do it like that ? and in real form it will happen ?

GertjanRoke's avatar

@princeoo7 aaah now I understand. the short answer is that phpunit will not convert it.

The layer that Laravel placed around phpunit will never change "" to null thats something you need to be aware about when you are testing. So it is good that you know the middleware of Laravel those stuff like that and for your test you need to keep that in mind that sometimes Laravel change the value you submit or get back out of the database, because of a middleware or if you have a casts on you eloquent model that will change a value.

For example if you have a boolean inside your database stored as a 0 and 1 you can casts it to false and true within your model, but when your are testing if the value is true in the database you need to look if the value for that key is 1 instead of true.

Please or to participate in this conversation.