Write tests only for your own code

Real-life example to answer 'what should I test?'

The developers who are just getting started with writing tests often get confused about what to test and how to test it 'properly'. Terms like Feature tests, Unit tests, Integration tests, and Acceptance tests are overwhelming.

But It's easier than most people think. You just need to write the test code that confirms whether your web app code does what it is supposed to do. Nothing more, nothing less.

While we have a dedicated article to help you get started with writing tests, this article focuses on how to set boundaries of your tests i.e. what not to test.

Example

Let's take an example to make it clear. We have an open-source package called Laravel Log Enhancer that adds contextual data to the Laravel log entries.

The default Laravel log entry looks something like this:

before.png

And the package improves it to:

after.png

This package has been downloaded 222k+ times but let me tell you a secret 🤫. It is just connecting the dots and has a tiny codebase.

Laravel uses the Monolog library to manage the logging module. And Monolog offers something called processors to add extra data for all the log entries.

Okay, with the introduction out of our way, we can take a look at some code:

foreach ($logger->getHandlers() as $handler) {
    if (config('laravel_log_enhancer.log_request_details')) {
        $handler->pushProcessor(new WebProcessor);
    }

    if (config('laravel_log_enhancer.log_memory_usage')) {
        $handler->pushProcessor(new MemoryUsageProcessor);
    }

    $handler->pushProcessor(new RequestDataProcessor);
}

This piece of code is the heart of the package. Based on the configuration, it is just pushing the processor(s) to the Monolog handlers. The rest of the work is done by the Monolog library.

Tests for our example

Theoretically, our log enhancer package is supposed to add extra data to the log entries. Hence we should write automated tests that verify that behavior. Right?

Should it be something like:

/** @test */
public function it_adds_extra_data_to_the_log_entries()
{
    // Given
    config(['laravel_log_enhancer.log_memory_usage' => true]);
    config(['laravel_log_enhancer.log_request_details' => true]);

    // When
    // Run the code that logs something

    // Then
    // Fetch the log entry and check whether extra details are present in it
}

No!!

The package is only pushing some processors to the handlers. It shouldn't worry about what happens to the final log entry. It is the responsibility of the Monolog tests to verify that part. Makes sense? Good.

With that understanding, let's take a look at the actual test code from the package:

/** @test */
public function it_adds_respective_processors_to_the_log_handler()
{
    config(['laravel_log_enhancer.log_memory_usage' => true]);
    config(['laravel_log_enhancer.log_request_details' => true]);
    $logger = $this->app[Logger::class];

    $handlers = $logger->getHandlers();
    foreach ($handlers as $handler) {
        $this->assertInstanceOf(RequestDataProcessor::class, $handler->popProcessor());
        $this->assertInstanceOf(MemoryUsageProcessor::class, $handler->popProcessor());
        $this->assertInstanceOf(WebProcessor::class, $handler->popProcessor());
    }
}

This test is confirming exactly what our package is doing: adding processors to the handlers. Nothing more, nothing less.

Rest

I hope this example helps you decide the boundaries of your tests and increase your confidence in writing them.

Do you have any other examples in mind that explain what to test? Feel free to comment and share. See you!