Tray2's avatar
Level 74

Test fails even if the required value is in the database.

I have a test that I can't get to pass due to a validation error.

I have this in my store method

public function store(Request $request)
    {
        $request->validate([
          'author_id' => 'required|exists:authors, id',
          'title' => 'required'
        ]);

        $book = new Book();
        $book->author_id = $request->author_id;
        $book->title = $request->title;
        $book->series = $request->series;
        $book->part = $request->part;
        $book->format_id = $request->format_id;
        $book->genre_id = $request->genre_id;
        $book->isbn = $request->isbn;
        $book->released = $request->released;
        $book->reprinted = $request->reprinted;
        $book->pages = $request->pages;
        $book->blurb = $request->blurb;

        $book->save();
    }

And I'm adding validations one by one. However when running this test.

    /** @test */
    public function the_title_must_start_every_word_with_an_upper_case()
    {
        $author = factory(Author::class)->create();

        $book = factory(Book::class)->make([
              'title' => 'THis iS tHe titlE'
            ]);

        $response = $this->post('/books', $book->toArray());

        $this->assertEquals(1, Book::where('title', 'This Is The Title')->count());
    }

It fails because of the an invalid author_id. A ddof the sessions errors gives me this

Illuminate\Support\ViewErrorBag {#714
  #bags: array:1 [
    "default" => Illuminate\Support\MessageBag {#715
      #messages: array:1 [
        "author_id" => array:1 [
          0 => "The selected author id is invalid."
        ]
      ]
      #format: ":message"
    }
  ]
}

Even if I assign the `$author->id in my test it fails.

Using this factory to create the book.

$factory->define(App\Book::class, function (Faker $faker) {
    return [
        'author_id' => 1,
        'format_id' => 1,
        'genre_id' => 1,
        'title' => 'Some Fake Title',
        'series' => 'Some Fake Serie',
        'part' => 9999,
        'isbn' => '0123456789',
        'released' => 1900,
        'reprinted' => 3000,
        'pages' => 100,
        'blurb' => 'Some nice fake text about the book'
    ];
});

and this in my AuthorFactory.

<?php

use Faker\Generator as Faker;

$factory->define(App\Author::class, function (Faker $faker) {
    return [
        'first_name' => $faker->firstName(),
        'last_name' => $faker->lastName()
    ];
});

My happy path test also fails after adding this validation

'author_id => 'required|exists:authors, id

Probably a simple error but I can't see it.

0 likes
8 replies
tykus's avatar

How do you know for sure that the created Author instance has id = 1?

$book = factory(Book::class)->make([
    'author_id' => $author->id,
    'title' => 'THis iS tHe titlE'
]);
Tray2's avatar
Level 74

I'm using RefreshDatabase and a SQLite in memory database and I've done a ddon the created $author

PHPUnit 7.2.7 by Sebastian Bergmann and contributors.

App\Author {#634
  #connection: "sqlite"
  #table: null
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: true
  #attributes: array:5 [
    "first_name" => "Mustafa"
    "last_name" => "Buckridge"
    "updated_at" => "2018-08-05 13:47:46"
    "created_at" => "2018-08-05 13:47:46"
    "id" => 1
  ]
  #original: array:5 [
    "first_name" => "Mustafa"
    "last_name" => "Buckridge"
    "updated_at" => "2018-08-05 13:47:46"
    "created_at" => "2018-08-05 13:47:46"
    "id" => 1
  ]
  #changes: []
  #casts: []
  #dates: []
  #dateFormat: null
  #appends: []
  #dispatchesEvents: []
  #observables: []
  #relations: []
  #touches: []
  +timestamps: true
  #hidden: []
  #visible: []
  #fillable: []
  #guarded: array:1 [
    0 => "*"
  ]
}

and I also tried doing that assignment you suggest and I do exactly that in my happy path test and it also fails after adding that validation.

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

        $author = factory(Author::class)->create();
        $genre = factory(Genre::class)->create();
        $format = factory(Format::class)->create();

        $book = factory(Book::class)->make([
          'author_id' => $author->id,
          'title' => 'The Eye Of The World',
          'series' => 'The Wheel Of Time',
          'part' => 1,
          'format_id' => $format->id,
          'genre_id' => $genre->id,
          'isbn' => '0812511816',
          'released' => 1990,
          'reprinted' => 1991,
          'pages' => 805,
          'blurb' => 'Some placeholder blurb just for our test.'
      ]);

        $response = $this->post('/books', $book->toArray());
        $response->assertStatus(200);

        $this->assertEquals(1, Book::count());
    }
tykus's avatar
tykus
Best Answer
Level 104

Can you remove the space before id in the validation rule?

Aside I don't like using a factory as a data provider for simulating a request; your model probably has more properties than the form you are testing has fields.

I prefer to have a private method in my test which simulates a valid form, but allow overrides to test specific functionality such as validation rules.

Tray2's avatar
Level 74

It was the space. Thanks. One of those hard too catch simple errors :)

I only use the factory to populate the values I don't care about and override those that I do care about.

Cronix's avatar

Yes you can't have spaces anywhere in the validation rules (except rules that allow quotes). When the validator explodes on pipes and commas to get the rules/parameters, I don't think it's trimming the values so any spaces would mess things up.

Tray2's avatar
Level 74

So I noticed, That's what you get for trying to make it more readable ;)

Cronix's avatar

I usually use arrays for readability (for longer rules anyway)

'author_id' => [
   'required',
   'exists:authors,id'
],
'title' => 'required'
Tray2's avatar
Level 74

So do I but I think this one is ok as a oneliner.

Please or to participate in this conversation.