Skip to content

Use custom Mailer class for running tests #14718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
bologer opened this issue Aug 24, 2017 · 12 comments
Closed

Use custom Mailer class for running tests #14718

bologer opened this issue Aug 24, 2017 · 12 comments

Comments

@bologer
Copy link

bologer commented Aug 24, 2017

What steps will reproduce the problem?

I'm overriding Mailer class with my own and use it to create queue of emails.

When I run tests, it seems to be using another class (Codeception\Lib\Connector\Yii2\TestMailer).

What is the expected result?

Use custom Mailing class.

What do you get instead?

Exception with message for each test where mailing class with custom method myOwnMathod() is used:

[yii\base\UnknownMethodException] Calling unknown method: Codeception\Lib\Connector\Yii2\TestMailer::myOwnMathod()

Where myOwnMethod() is defined in my overriten mailer class, but as in tests it is using \yii\swiftmailer\Mailer, which certainly is missing that method.

Is there a way to set-up custom mailing class for tests?

Q A
Yii version 2.0.12
PHP version 5.5+
Operating system Ubuntu 14.04
@bologer bologer changed the title User custom Mailer class for running tests Use custom Mailer class for running tests Aug 24, 2017
@JunelanMe
Copy link

I have the same problem Codeception/Codeception#4011 when running the functional case, DavertMik mark it not a problem . so I temporary add the class in the $allowedOptions to use my own mailer class.
image

@StalkAlex
Copy link
Contributor

StalkAlex commented Aug 27, 2017

@bologer, you can use something like this Yii::$app->set('mailer', $mockMailer); in your test and Codeception hardcoded version will not be called.

@JunelanMe
Copy link

JunelanMe commented Aug 27, 2017

@bologer you can try to redefine the malier's class before the test case start
maybe in the _before function of the cest file
public function _before(CpdfapiTester $I){
\Yii::$app->set('mailer', 'app\common\components\Mailer');
\Codeception\Lib\Connector\Yii2::$mailer= \Yii::$app->get('mailer');
}

or you can new a extention file to listen the case start . http://codeception.com/docs/08-Customization
then do the redefine class in the beforeTest
the Extension file
`<?php
use \Codeception\Events;

class MyExtension extends \Codeception\Extension
{
// list events to listen to
// Codeception\Events constants used to set the event

public static $events = array(
    Events::TEST_BEFORE => 'beforeTest',
);

public function beforeTest(\Codeception\Event\TestEvent $e) {
    \Yii::$app->set('mailer', 'app\common\components\Mailer');
    \Codeception\Lib\Connector\Yii2::$mailer= \Yii::$app->get('mailer');
}

}`

@bologer
Copy link
Author

bologer commented Aug 28, 2017

@JunelanMe this is nice, but it is kind of a work around.

@cebe , @samdark is there solutiuon Yii-like way to define custom mailing class for tests only?

@StalkAlex
Copy link
Contributor

@JunelanMe there is no need in both two lines in beforeTest method, each of the line gives the expected result but in a different way.
@bologer This is one of the way how you use DI containers, you're able to substitute implementations where you need it, in test environment, for example. So imho it will be preferable way.
Yii::$app->set('mailer', $mockMailer);

@bologer
Copy link
Author

bologer commented Aug 28, 2017

@StalkAlex, alright, when I try to make the following:

public function __before() {
    Yii::$app->set('mailer', 'common\models\mailer\Mailer');
}

I get the following error:

[ModuleException] Yii2: Mailer module is not mocked, can't test emails 

I get the same error message if I put this:

Yii::$app->set('mailer', 'common\models\mailer\Mailer');

Inside of the test methods that use mailing class.

I tried it like this:

Yii::$app->set('mailer', ['class' => 'common\models\mailer\Mailer'])

Still get the same error.

Any idea what is wrong with it?

@StalkAlex
Copy link
Contributor

StalkAlex commented Aug 28, 2017

@bologer just checked by myself, here's green test:

class TestTest extends Unit
{
    public function _before()
    {
        Yii::$app->set('mailer', ['class' => 'tests\codeception\_extensions\TestMailer']);
    }

    public function testIntanceIsValid()
    {
        $test = Yii::$app->get('mailer');
        expect_that($test instanceof TestMailer);
    }
}

Exception says that there is a problem with you configuration. In yii module there are different parts that can be included: init, orm, email and fixtures. Check those. Also I'm using last Codeception and Yii2 versions.

P.S. Are you using grabSentEmails method? Then here's problem in extra check.

@bologer
Copy link
Author

bologer commented Aug 28, 2017

@StalkAlex, thank you very much! It worked.

@bologer bologer closed this as completed Aug 28, 2017
@Gupta987946
Copy link

hello

@Eseperio
Copy link
Contributor

Eseperio commented Feb 22, 2025

The most correct solution at the moment is to use Yii2’s container to override TestMailer. You can do this by adding the following to your configuration:

$config['container']['definitions'][\Codeception\Lib\Connector\Yii2\TestMailer::class] = \app\components\mailer\CustomMailer::class;

Then, in your custom mailer class, you should add the following methods:

public $callback;

protected function sendMessage($message)
{
    if (YII_ENV_TEST) {
        call_user_func($this->callback, $message);
        return true;

    }

    return parent::sendMessage($message);
}

protected function saveMessage($message)
{
    if (YII_ENV_TEST) {
        call_user_func($this->callback, $message);
        return true;
    }
    
    return parent::saveMessage($message);
}

With this approach, when running tests (YII_ENV_TEST), methods like $I->grabLastSentEmail() and others will continue to work while keeping all your original mailer logic intact.

In my case, this even allows me to properly test a custom MailQueue.

Hope this helps! 🚀

@mikk150
Copy link
Contributor

mikk150 commented Feb 26, 2025

The most correct solution at the moment is to use Yii2’s container to override TestMailer. You can do this by adding the following to your configuration:

$config['container']['definitions'][\Codeception\Lib\Connector\Yii2\TestMailer::class] = \app\components\mailer\CustomMailer::class;
Then, in your custom mailer class, you should add the following methods:

public $callback;

protected function sendMessage($message)
{
if (YII_ENV_TEST) {
call_user_func($this->callback, $message);
return true;

}

return parent::sendMessage($message);

}

protected function saveMessage($message)
{
if (YII_ENV_TEST) {
call_user_func($this->callback, $message);
return true;
}

return parent::saveMessage($message);

}
With this approach, when running tests (YII_ENV_TEST), methods like $I->grabLastSentEmail() and others will continue to work while keeping all your original mailer logic intact.

In my case, this even allows me to properly test a custom MailQueue.

Hope this helps! 🚀

@Eseperio could you also test Codeception/module-yii2#120

@Eseperio
Copy link
Contributor

I cannot try on my current project due to it not being still updated to latest php version. But i´ve checked the code and it looks like a really much clean solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants