|

How To Write Validation Test Cases

How To Write Validation Test Cases

We’ve reached the point in our tutorial series where we will add some validation to submitting new threads and replies. In this tutorial, we’ll investigate how we can create tests to support our validation calls in the controllers. We’re going to need to set up some tests to support creating new threads. We’ll also set up a test for creating new replies. We’ll run into a few opportunities to refactor along they way, so we’ll tackle that as well as we go.


A Quick Refactor

We’re going to be dealing with the tests inside of the CreateThreadsTest class. Before we start adding validation to the tests, let’s refactor a little bit. We will fist look at the test_a_logged_in_user_can_create_new_threads test. It looks like as we have the test now, we are creating a thread and storing it in the database before we even make a post request. We actually want to test if the post request was successful. So a few changes are in order. We will capture the response from the post request, and inspect the header to see where it redirected. Here is how:

Now you run that specific test like so to see what we get.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_a_logged_in_user_can_create_new_threads
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

"http://localhost/threads/aut/1"

We took this step so that we could see what the id is of the thread that gets generated. In the output ‘aut’ is the channel, and 1 is the id.

Ok since we now know the redirect path, we can make a request to that path, and then do our assertions to see if the body and title are present. Make sense? Let’s see how to do that.

Run the test_a_logged_in_user_can_create_new_threads test one more time, and we are all good in the hood.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_a_logged_in_user_can_create_new_threads
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 815 ms, Memory: 10.00MB

OK (1 test, 2 assertions)

Creating A Validation Test

With that small refactor out of the way, let’s set up our first validation test. When creating a thread, we know that at the minimum it will need a title. Ok, we can create a test to reflect that.

Let’s think of some pseudo code first.

  • Given a user is signed in
  • That user creates a faulty thread
  • And that user makes a post request to /threads
  • The session should have an error

In Laravel, if there is a validation error, an $errors variable will be placed in the session so that is what we want to test here. Sounds good, here is the code that represents this test case.

So what happens here. Well, the first part of $this->signIn(); simply signs in the user. Next with the code of $thread = make(‘App\Thread’, [‘title’ => null]); – we are creating a faulty thread. Why? Because as the second argument to the make() function we are overriding the title to have a null value. Of course we want threads to have a valid title, not a null value, so this is good for our test case. Lastly, we make the post request with that faulty data we have, and we need to assert that the session has errors. If the session has the error, then the test passes. Let’s run that test now, and see what we get.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_a_thread_requires_a_title
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 964 ms, Memory: 12.00MB

There was 1 failure:

1) Tests\Feature\CreateThreadsTest::test_a_thread_requires_a_title
Session is missing expected key [errors].
Failed asserting that false is true.

/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:635
/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:673
/home/vagrant/Code/forumio/tests/Feature/CreateThreadsTest.php:41

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

This test is in fact failing. We see the error of Session is missing expected key [errors]. What that tells us, is that validation did not work, since no error was populated in the session. Well this makes perfect sense right? We have not added any validation at all to the store() method on the ThreadsController. I’m betting if we now add the validation in the store() method of the ThreadsController that we will get our test to pass. We can update that method like so:

Yes! We are passing now.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_a_thread_requires_a_title
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 981 ms, Memory: 10.00MB

OK (1 test, 2 assertions)

This means the validation method is working properly in our controller, and our test is testing as it should as well.


Validation Testing For Each Field

What we would like to do is to actually create a separate test for each field we validate in a request made to the server. We can extract a method out of some of the test code to help with this. Here we will continue to use the test of test_a_thread_requires_a_title, but we will extract some of it’s code to a function named publishThread. Here is that code.

Running the test now with our new refactor shows it working quite well.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_a_thread_requires_a_title
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 955 ms, Memory: 10.00MB

OK (1 test, 2 assertions)

Since we have that new method extracted now, we an easily use it to make additional tests. We can now make a test for the body of a thread. Let’s add it like so:

Let’s run the test on the full class now.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

...F                                                                4 / 4 (100%)

Time: 1.58 seconds, Memory: 14.00MB

There was 1 failure:

1) Tests\Feature\CreateThreadsTest::test_a_thread_requires_a_body
Session is missing expected key [errors].
Failed asserting that false is true.

/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:635
/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:673
/home/vagrant/Code/forumio/tests/Feature/CreateThreadsTest.php:41

FAILURES!
Tests: 4, Assertions: 9, Failures: 1.

Ok we can see that our new test test_a_thread_requires_a_body is failing. And why is it failing? Oh yeah that’s right, we have not included the ‘body’ as a required attribute of the validation rules in the store() method of ThreadsController. Therefore in our test, when we make a post request, it just flies on through and the session does not get the expected error. How do we fix it? We again update our rules in the validate method like so.

Now when we make a post request in our test with the null value for body, the validate function should catch it and populate the session with an error. This means the test should pass. Does it?

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

....                                                                4 / 4 (100%)

Time: 1.26 seconds, Memory: 10.00MB

OK (4 tests, 10 assertions)

Yep: All tests are again passing.


Validating A Channel ID

The last thing we’ll add in regards to test cases for validation is to validate that the correct channel id is being sent through on a thread creation request. This one is a little different than the first two. Let’s see how we might do this one.

In this test, we first create two channels in the database. These channels are going to have id 1 and 2. Then, we publish a thread, and assert that the channel_id can not be null. We also do a second run through to assert that the channel_id is not equal to some value that we know does not exist in the database table. Of, fair enough, let’s update the validate method in our controller and run the test.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

....F                                                               5 / 5 (100%)

Time: 1.69 seconds, Memory: 14.00MB

There was 1 failure:

1) Tests\Feature\CreateThreadsTest::test_a_thread_requires_a_valid_channel
Session is missing expected key [errors].
Failed asserting that false is true.

/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:635
/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:673
/home/vagrant/Code/forumio/tests/Feature/CreateThreadsTest.php:52

FAILURES!
Tests: 5, Assertions: 13, Failures: 1.

Hmm. The test is failing, even though we put a required flag on the validate model. What is going on. Well, just because it is required doesn’t prevent the user from sending through a channel_id that is some crazy value that does not exist in the database table. It sounds like the validation needs to be more specific. We need to use this format here:

required|exists:channels,id

What does this rule say? It says that the channel_id is required, but that the value provided must already exist in the channels table on the id column. Make sense? We can update the validate method once more and run the test. It should pass at this point.

vagrant@homestead:~/Code/forumio$ phpunit --filter CreateThreadsTest
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

.....                                                               5 / 5 (100%)

Time: 1.34 seconds, Memory: 10.00MB

OK (5 tests, 14 assertions)

All tests are passing again on the CreateThreadsTest class.


Adding Validation To A Thread Reply

We’ve done a great job getting validation setup and the associated tests for creating new threads. Let’s now tackle creating new replies. It should be easier this time around since we know the basic idea of how to solve this now. Let’s first work out the pseudo code for the test as we usually do.

  • Given there is a signed in user
  • Given we have a thread
  • Given we have a reply with a null body
  • When this faulty reply is posted
  • We should see an error

Great! Let’s create the code to reflect this:

We can now run the test for this new test function.

vagrant@homestead:~/Code/forumio$ phpunit --filter test_a_reply_requires_a_body
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 992 ms, Memory: 8.00MB

There was 1 failure:

1) Tests\Feature\ParticipateInForumTest::test_a_reply_requires_a_body
Session is missing expected key [errors].
Failed asserting that false is true.

/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:635
/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:673
/home/vagrant/Code/forumio/tests/Feature/ParticipateInForumTest.php:42

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

It fails since the session does not contain the expected key. Of course! We need to add the actual validation rule to the store() method of the RepliesController. Let’s do that now.

This test should now pass, and in fact all tests should pass. Let’s run the entire suite of tests that we have built up so far.

vagrant@homestead:~/Code/forumio$ phpunit
PHPUnit 6.5.5 by Sebastian Bergmann and contributors.

..................                                                18 / 18 (100%)

Time: 2.39 seconds, Memory: 12.00MB

OK (18 tests, 32 assertions)

Alright! This is fannnnnnnnnnntastic! 18 tests ran with 32 assertions and everything is passing.


How To Write Validation Test Cases Summary

This was a fun tutorial that had us learning about how to create tests to verify that our validation rules are correct and working properly on our controllers. This is one I was confused a bit myself on, but after this tutorial – things are nice and clear!

|