netdjw's avatar
Level 15

What should I test at witch level?

Hello there!

Today we talked with my collegue about testing and a question was formulated in topic of unit testing:

What should I test at witch level in Laravel?

For example: If I test API routes is it enough for testing the response status code (it proofs the route exists) or should I test the content of response too? In my opinion the response content is uninteresting in the viewpoint of route.

And if we test the content of response, how deep should we dig? Sould I care about what controller are called for example?

I think if we start testing the content of response it looks like an integration / functional test, and not unit test.

Is there any golden rule what can we apply to keep unit tests as a real unit test and separate integration / functional tests from unit tests?

0 likes
6 replies
bugsysha's avatar

That depends on the route. If you have a route that returns a list of posts, then you should assert the response code and the content of the response. If you have a route to create a post, then you should assert the response code, changes in the database, and the content of the response.

Depth depends on how many details you want to cover. Usually, I just assert that the structure is expected. So if I have a list of posts returned, then I would assert the content of the first post, and that structure follows the expected structure. I don't care about what controller is called. Not sure how I would test that. Do you have multiple controllers doing the same thing? If that is true, then why? If that is false then every controller should return a unique response.

I first create an integration/functional test. That covers more ground. Then while writing the implementation in the controller if I create any new class or any new method, I create unit tests for those if applicable. That is why TDD is a very good approach.

1 like
netdjw's avatar
Level 15

@bugsysha I have two similar controllers what do exatly the same with similar data modelst. For example:

Models/Category:

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    protected $fillable = [
        'name',
    ];
}

Models/Status:

use Illuminate\Database\Eloquent\Model;

class Status extends Model
{
    protected $fillable = [
        'name',
    ];
}

These are pretty simple examples, but good to understand the problem.

Controllers are a very basic CRUD style controller.

Now If I try to test these controllers on routes I will get back same result, but if I made a mistake on the delete route and changed the controllers name I'll get back same result, but the delete will be happening on wrong controller, and finally in wrong table.

But if I try to get back model from the database to test it's gone from the right table... so it sounds like an integration test, not a route unit test.

So the question is maybe philosophic, but what should I test on a route if I want to write only a unit test for routes?

Tray2's avatar

@netdjw When it comes to what you describe here, I'd say you should do a feature test and hit the database.

For a delete it could look something like this

    /**
     * @test
     */
    public function after_deleting_an_artist_the_user_is_redirected_to_the_artists_index_view_and_success_message_is_shown()
    {
        $this->signIn();

        $artist = Artist::factory()->create();

        $response = $this->delete('/artists/' . $artist->id);

        $response->assertStatus(302);
        $response->assertLocation('/artists');

        $response = $this->get('/artists');
        $response->assertSee($artist->name . ' successfully deleted.');
    }

You could then if you want add asserts to check if the database has that record or not.

jlrdw's avatar

@netdjw I find that when following laravel conventions many things realy don't need testing, for example saving data using the save method is already tested through the laravel team.

However, one of the testing videos by @jeffreyway talked about how testing enables you to see where you can refactor code. Especially in some complex code.

So a simple save I probably won't test whereas if I have some complex code where multiple if's or a multiple search is involved, then I think it's good to do test.

Not so much:

  • Does the code work
  • Rather looking for a better way to refactor if needed.

Just my thoughts on testing.

Edit

To add, even if you test everything, you still need to test for real. For example a few years ago when I setup paypal for a customer, I tested for real if the transaction worked by me making a purchase.

As I have seen cases where a test passes but somehow for real doesn't work correctly, just keep real World test a higher priority than mock test.

bugsysha's avatar

So the question is maybe philosophic, but what should I test on a route if I want to write only a unit test for routes?

@netdjw nothing. No unit tests for routes. Routes are a concept that is tightly coupled with requests, controllers, and so on. So if you look at the Laravel documentation, you will see that it states that you should never mock Request object. From that you see that no unit tests for Routes should be done.

1 like
mohamedallamthe1's avatar

Quite honestly I think the responses may vary. there is lot of books on such topic. theory: I been played with testing to almost never care about opening a browser. which is a big improvement in my opinion rather than doing so for every single line of code you write.

Now to answer you, model for example who is responsible for persisting data. lets say you have relationships and you have to user()->posts()->save($post); This is redandant so its suggested to do something like $user->savePost(); for example this method has to be tested, in a model, middlewares has to be tested, tables sometimes, and there column names, you test observers mutators accessors and such. this all unit tests.

When it comes to integration test you just make sure the functionality for the entire webapp works, lets say users can see posts, in a specific order, or a guest cannot create a post, or the post should have a valid inputs..ect

1 like

Please or to participate in this conversation.