Corben's avatar
Level 13

How to test piping email to Laravel app?

My App is receiving emails, and I can import them with an artisan command. Inspired by this using the php-mime-mail-parser.

Unfortunately I had no idea how to create a test for this, so I couldn't start with a TDD approach, and I wrote the implementation first. But now I'd like to have an automated test for this. How would I set this up? How can I create an email or .eml-file (Mail facade and saving to unittest-disk?), and send/pipe it then via artisan command to my application within phpunittest?

I've read about Testing Artisan Commands in Laravel 5.7+, but how would that piping work then?

0 likes
1 reply
Corben's avatar
Level 13

My solution so far is now this:

I'm using themsaid/laravel-mail-preview to get an .eml file of sent emails. Only in my phpunit.xml I set:

<env name="MAIL_DRIVER" value="preview"/>

I don't fake the Mail facade, so it uses this mail driver only during the test. One hurdle I came across was, to get the .eml in a faked storage. In the config/mailpreview.php is the path to save the file set to storage_path('email-preview'). This is using app('path.storage') and not as I thought Storage::path(''). Running the test would save the preview file permanently to app/storage/email-preview then. So at runtime I set:

Storage::fake('local');
config(['mailpreview.path' => Storage::path('email-previews')]);

For my artisan command test I do accept now an option, which is optional. So for the tests I use the option, for production I use the pipe mechanism. For parsing the mail, I'm using php-mime-mail-parser as already mentioned.

The interesting parts of my artisan command is now:

protected $signature = 'email:parse {--f|file= : .eml file to parse, optional, can also be piped}';

public function handle()
{
    $option = $this->option('file');
    $parser = new Parser();

    $file = $option ?: 'php://stdin';

    $parser->setStream(fopen($file, 'r'));
    
    // code
}

And my test is calling the command like this:

$files = Storage::files('email-previews');

$emlfiles = array_filter($files, function($array) {
    return preg_match("/.eml$/i", $array);
});

$this->assertCount(1, $emlfiles);

$emlfile = Storage::path('') . array_values($emlfiles)[0];

$this->artisan('email:parse', ['-f' => $emlfile]);

Any ideas to improve this are appreciated, as using the option is just a workaround. I still couldn't figure out how to test the pipe mechanism. e.g. cat mail.eml | php artisan email:parse.

Please or to participate in this conversation.