Keeping Your Tests Clean

Written By Glenn Kimble Jr.
Posted on
Share

It’s no secret that as developers we spend more time reading code than writing code. As such, we understand the importance of keeping our code clean. Yet, one area we tend to fall behind on in this effort is our test files. I believe tests should act as a sort of documentation for our code, and good documentation should be as clear as possible.

One practice I use to help me in this, be it production code or test code, is extracting logic out into helper methods. In this post, I’ll focus on one of my most used testing helper methods: validParameters.

The Problem

One of the most common tests I write is to validate some set of data. For example, I might write a test that says, "An email is required to register."

    /** @test */
    public function an_email_is_required_to_register()
    {
        $request = $this->postJson(route('register'), [
            'name' => 'Jeff Leboswki',
            'email' => null,
            'password' => 'thedudeabides',
            'password_confirmation' => 'thedudeabides',
        ]);

        $request->assertJsonValidationErrors('email');
    }

Perhaps this test is sufficient. It's a total of eight lines of code, so it's short and easy to understand. If we post to the register route without an email parameter, we expect to have a validation error for email.

Consider the other similar tests we need to write. "A valid email is required", "A unique email is required", "Password is required", "Password confirmation is required", "Password must match password confirmation", "The password must be at least eight characters", etc. Your test file can get cluttered fast with tests that all look and feel the same and start to blend together.

Another issue becomes duplication. What if you were to add another required field? Now you need to go to all these separate test methods and update the parameters you are submitting to the register route.

That doesn't sound fun to me.

The Solution

While there are several ways you can go about making this easier, one of my favorites is to extract a helper method called validParameters. This method contains an array of all the "happy path" parameters we need to pass validation. It also accepts an optional array of parameters to override this happy path data. The result allows you to keep your test concerned only with the parameter you are expecting to fail.

    /**
     * @param  array  $override
     * @return array
     */
    private function validParameters($override = [])
    {
        return array_merge([
            'name' => 'Jeff Leboswki',
            'email' => 'dude@zaengle.com',
            'password' => 'thedudeabides',
            'password_confirmation' => 'thedudeabides',
        ], $override);
    }

And then our updated test...

    /** @test */
    public function an_email_is_required_to_register()
    {
        $request = $this->postJson(route('register'), $this->validParameters([
            'email' => null,
        ]));

        $request->assertJsonValidationErrors('email');
    }

It becomes just a little bit more focused, and perhaps a little bit easier to reason about.

The Conclusion

Obviously, there's more than one way to skin a cat and while you can solve this problem in a variety of ways, this is one that I've adopted over time as a favorite. Regardless of your solution, let’s not forget to give our test code the same kind of focus and care we give our production code.