CodeIgniter 4: Writing a unit test for sending emails

CodeIgniter 4: Writing a unit test for sending emails

Published at: 01/08/2023
Updated at: 01/08/2023

A unit test for testing that emails can be sent within our application

Long story short: I am not very used to unit testing, and I wanted to learn something new. I decided to write a unit test to make sure I can send emails from my CodeIgniter 4 application.

Well, turns out that by default, the email service of CodeIgniter 4 will use a Mocked Class to do this, and therefore my test was always returning true for the send() method of the Email class.

Writing the test

To begin, this is the test I wanted to perform:

$email = service('email');
$email->setFrom(env('email.from'), env('email.fromName'));
$email->setSubject('Email Test');
$email->setMessage('Testing that the email can be sent properly.');
$this->assertTrue($email->send()); // Why does this always return true?

In general the idea seems fine. But there was a problem. My test was always returning true, even if the email was not being sent.

Why does the test return true?

I am not yet an expert of unit testing, so I thought I was doing something wrong. Of course my expected result was to pass the test, but the email was not actually being sent. Moreover, I am still unexperienced with CodeIgniter 4. So, I decided to hunt down the issue, and I found the "culprit", so to say.

By default, CodeIgniter will perform tests using some predefined mock classes, that is a set of fake classes that should not actually perform definitive actions.

In the docs it says that this is made on purpose to prevent intrusive testing behavior (see here: Testing).

My test was always returning true because it was using the send() method of the mocked class, not the real Email class.

MockEmail Class

Indeed, the MockEmail class is as follows:

class MockEmail extends Email
     * Value to return from mocked send().
     * @var bool
    public $returnValue = true;

    public function send($autoClear = true)
        if ($this->returnValue) {

            if ($autoClear) {

            Events::trigger('email', $this->archive);

        return $this->returnValue;

As you can see, the send() method does not really send the email. Or, at least, I needed to send an SMTP message and this method is not doing that.

You could probably just override the MockEmail class, but this didn't seem to be a real case scenario to me. I mean, I want to test that the application can actually send emails to my users, so I need to follow the same path, otherwise the test would not make much sense, would it?

If I have to test that I can receive the email, I don't want a mock send(), right?

Overriding the setUpMethods of the Test Class

In the docs, it is also said that you can remove 'mockEmail' from the Test class, within the $setUpMethods.

In the code for $setUpMethods there is this warning comment:

// CodeIgniter\Test\CIUnitTestCase.php
     * Methods to run during setUp.
     * WARNING: Do not override unless you know exactly what you are doing.
     *          This property may be deprecated in the future.
     * @var array of methods
    protected $setUpMethods = [

So going with that solution seems to be discouraged. But what could a "intrusive behavior" be in the testing phase? I am not sure. Maybe if you make tests to be available with public calls (get, post) there could be a problem. This is not my case.

Solution and working test

My solution is to bypass the mocked class, as follows.

// tests\app\Emails\SendEmailTest.php
namespace App\Emails;

use CodeIgniter\Test\CIUnitTestCase;

final class SendEmailTest extends CIUnitTestCase
    private $email = null;

    public function __construct()

        // Removing the mockEmail from this test
        $k = array_keys($this->setUpMethods, 'mockEmail')[0];

    public static function setUpBeforeClass(): void

    protected function setUp(): void

        $this->email = service('email');
        $this->email->setFrom(env('email.from'), env('email.fromName'));


    final public function testCanSendEmails()
        $this->email->setSubject('Email Test');
        $this->email->setMessage('Testing that the email can be sent properly.');
        $result = $this->email->send();

        if (!$result) {



The question is: am I correct to think that it is not a logical behavior and it should be improved, or is there a good reason for doing so, that I do not know of? Is there a better way to achieve this?

Let me know your opinions in the comments below! If you liked this article, please follow me on Facebook and Youtube!

Leave a comment

All comments will be subject to approval after being sent. They might be published after several hours.

You can just use a random nickname, it is usefull to allow me to at least answer your comments. And if you choose to submit your email, you can receive a notification whenever I reply to your comment.

  • 2023-07-01 09:43:07

    Author: eartahhj

    @Atiab Jobayer I am glad it helped you!

  • 2023-06-30 23:28:35

    Author: Atiab Jobayer

    It was super helpful. Thanks a lot!