Skip to main content

By Jesse Schutt

Testing 101: Escape Testing Paralysis!

The problem is options

Learning to write good tests is critical to being a programmer. We write tests to make sure the work is solid and doing what it's supposed to, while exerting as little effort as possible (we're not lazy; we're effective...). When I understood that, I determined that I was going to get great at testing. So naturally I started internet research: "Dear Google, show me 'best code testing practices.'"

I quickly gathered that there are a multitude of testing solutions with numerous opinions on each one. In the same way that children can choose blue LEGO bricks, red ones, or two green ones and then argue loudly about which choice is superior, I heard terms like unit testing, functional testing, integration testing, acceptance testing, etc., and all the debates on what should and shouldn’t be tested in our applications.

I soon found myself in testing-paralysis. My overloaded brain said I had to thoroughly understand each of these paradigms before I could begin implementing any kind of testing on my apps. (Which color LEGO is best?) I didn’t feel confident enough to implement any test, for fear of choosing the wrong test, so I resorted to saying, “Let’s just build the app, and I’ll circle back and test it later.

Little did I know that even though I thought avoiding the tests was making my life easier, I was actually digging myself into a hole.

Test code is just as important as production code. It is not a second-class citizen.

- Robert Martin, Clean Code

Tests are only as good as you make them

The Holy Grail of Testing is a myth. There. I said it.

When I began my testing journey, I had an unfounded belief there was some perfect test that - if I could understand it - would cover every aspect of my application.

That is simply not true.

Tests are only as good as you make them, and they make you a better programmer. Testing highlights areas of my code that could be improved. Testing has forced me to think through where my application might break, and what is actually a “correct” approach to a particular business objective.

Even if all your application does is pull a record from a database and pass it on to a view, then test that the route will properly load! You’ve probably heard that you don’t need to test Eloquent, or that you shouldn’t ever touch a database in your unit tests, but there have been numerous times that I have a typo in my controller, or view, and can’t get the page to load properly. So start testing by covering simple scenarios like that.

<?php

class SampleRouteTest extends TestCase {
    
    /** @test */
    public function sample_route_loads() {
        $this->visit(‘page’)
            ->assertResponseStatus(200);
    }
}

But, again: your tests are only as good as you make them. Seeing the green OK after running your test suite can give a false sense of security. Remember, the tests that are passing don’t give a complete picture of how stable your app is. They are just passing on the logic you told them to inspect.

If you let your tests rot, then your code will rot too. Keep your tests clean.

- Robert Martin, Clean Code

Testing uncovers bad design

We did end up building the application I mentioned earlier, and I had scattered a few tests throughout, kidding myself that I could quickly write the tests once we were further along in the production code. By the time I started writing tests, I realized that certain parts of the application were very difficult to test due to the way I’d structured the code.

For example, instead of passing simple arrays from controllers through to repositories, I was passing entire requests. Even though it would only take a few lines of code to parse the request into an array, I wanted my controllers to be super clean, so I passed the raw request.

When I began writing tests to check that the business logic in my repositories was working, I realized I had to simulate requests as well! If I had been testing from the start, I would have been reminded that my repositories don’t need to know anything about the request. All they should care about is storing the data that is passed to them.

Red, green, refactor

Uncle Bob Martin outlines The Three Laws of TDD in his excellent book “Clean Code”:

First Law: You may not write production code until you have written a failing unit test.
Second Law: You may not write more of a unit test than is sufficient to fail…
Third Law: You may not write more production code than is sufficient to pass the currently failing test.

When I first read those “laws” I couldn’t imagine how to actually do what they mandated! I’m a cowboy coder, I like dd()! After all, I know what I’m doing! I don’t need no stinking tests! </rant>

… It turns out that Uncle Bob is exactly right with those laws.

Taking the time to write what the outcome of your code should be - before just mashing away at the keyboard - allows you to set a baseline for what your production code should do. I was surprised by how much cleaner I was able to code by thinking about what I wanted the result to be before writing any production code.

Freedom to explore

Coding is supposed to be fun, right?

How much fun is it to modify that 500+ line method, that is mission critical to your application, and not know if your changes will break the whole thing?

We’ve all been there.

Fortunately, by ensuring you have solid tests before writing your production code, you can freely explore different ways to accomplish your business logic! Refactor that giant method, run your tests, and have the assurance that your application isn’t broken. It’s a beautiful thing… at least for a hacker anyhow :-)

Laravel is like the ultimate LEGO set.

- Taylor Otwell, Laracon 2015

TL;DR

Ultimately what matters is that the business goal is achieved, and that the underlying code is clean, succinct, and easy to understand. And - this is also key - you had fun (at least a little bit).

  • Testing is difficult at first, but not impossible
  • Testing helps you understand your code
  • Testing gives you freedom to refactor
  • Testing is only as good as the tests are
  • Explore and enjoy the process!

Next

In future posts we will explore some of the testing tools Laravel provides and sort through actual tests I’ve been writing for my applications. Hopefully you will see that testing can become a natural fit for you.

Want to read more tips and insights on working with a website development team that wants to help your organization grow for good? Sign up for our bimonthly newsletter.

By Jesse Schutt

Director of Engineering

Jesse is our resident woodworker. His signature is to find the deeper meaning in a project and the right tool for the job.