guice
guice

Reputation: 1094

Laravel Artisan console output in services, while unit testable

What is the best practice to add a console output logger to service classes called from artisan command classes.

Example Code:

<?php
class Import extends Command
{
    public function handle()
    {
            /** @var \Services\ServiceImport $service */
            $service = resolve($this->resolvers[$db])
                ->setCommand($this);

            # Console output
            $this->info(sprintf('Starting import for "%s"', $service::SERVICE_NAME));
            $imported = $service->import($this->argument('file'));

            $this->info(sprintf('Total rows imported: %d', $imported));
        }
    }
}

/** Different file, service from container */

class ServiceImport extends Service
{
    protected $cmd; 

    public function import($file)
    {
        # Need this console output
        $this->cmd->info(sprintf('Importing file "%s"', $file));
        // [...] More stuff goes on..this illustrates my point
    }

    public function setCommand(Command $cmd)
    {
        $this->cmd = $cmd;
        return $this;
    }
}

This works, but fails when attempting to unit test ServiceImport, because $cmd isn't set... And I hadn't figured out a way to Mock a Command get this working, either. How do I accomplish this?

I'm certain I'm missing something. Is it how I'm using Services? I can't be the only person looking to run a constant running verbose log during processing.

Using Laravel 5.4, artisan command.

I don't want to use Log:: as I specifically write to the console (with nice Symfony colors).

Upvotes: 1

Views: 2410

Answers (1)

Oli
Oli

Reputation: 240

As you only want to create log-output you could solve this with checks for null.

In your service just inject the command dependency with the function parameters like so:

public function import($file, Command $cmd = null)
{
    # Need this console output
    if($cmd != null) {
        $this->cmd->info(sprintf('Importing file "%s"', $file));
    }
    // [...] More stuff goes on..this illustrates my point
}

In your tests you can easily omit the $cmd argument as this should not influence your Services code.

If you excessively use this kind of output, create a Trait or a base class with functions like:

public function info(string $message, Command $cmd = null)
{
    if($cmd != null){
        $cmd->info($message);
    } else {
        //perhaps log message otherwise in test environment 
        // or when service is used outside of artisan commands
    }
}

And you can use this everywhere in your Service

$this->info(sprintf('Importing file "%s"', $file), $cmd);

Upvotes: 1

Related Questions