在CakePHP中进行单元测试期间使用不同的电子邮件配置

Jos*_*osh 5 phpunit unit-testing cakephp

我正在我的一个控制器的动作中使用CakeEmail类发送电子邮件.在添加电子邮件代码之前,我对此控制器进行了单元测试.添加电子邮件后,我收到此错误:

SocketException:无法发送电子邮件.

这可以归结为我无法通过本地机器发送电子邮件.

所以我认为也许一个好主意是在Config/email.php中的EmailConfig类中有两个不同的配置选项(类似于数据库配置文件的工作方式).默认使用Mail传输,使用Debug传输进行测试.这个问题是,与数据库配置不同,Cake在测试期间不会在两者之间自动切换.

我唯一想到的是在EmailConfig类中添加一个构造函数并测试我们是否进行单元测试,但我不确定该检查应该是什么.

有点像这样:

class EmailConfig {

    public $default = array(
        'transport' => 'Mail'
    );

    public $test = array(
        'transport' => 'Debug'
    );

    public function __construct() {
        if ($isUnitTesting) {
            $this->default = $this->test;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

我上面的建议是不是一个好主意?如果没有,在单元测试期间,我还可以使用其他方式为电子邮件使用不同的传输方式?


更新 - 2012年4月10日

我想我的做法是错误的.看看这个答案,似乎即使$default默认情况下没有加载配置,你必须通过调用CakeEmail::config()方法指定它或在构造函数中给它.所以我认为这给我留下了两个选择:

  1. 在控制器中检查我们是否进行单元测试(以某种方式?)然后使用'test'配置.
  2. 设置我的电脑以便能够发送电子邮件.

我宁愿做第一个,但不确定如果我们进行单元测试,如果不对控制器动作进行检查,这怎么办呢,这样做似乎是错误的.

jer*_*ris 5

最简单的方法可能是在测试时切换到 DebugTransport。测试的一部分是您需要将程序设计为可测试的。事实上,Cake 中到处都有一些函数就是为了实现这一点而设计的。对于您的应用程序,我们假设您在用户注册时发送一封电子邮件:

App::uses('CakeEmail', 'Network/Email');
App::uses('AppController', 'Controller');

class UsersController extends AppController {

  public function register() {
    //registration logic
    $email = new CakeEmail();
    $email->from(array('site@example.com' => 'Site'));
    $email->to('you@example.com');
    $email->subject('Registered');
    $email->send('Thanks for registering!');
  }

}
Run Code Online (Sandbox Code Playgroud)

这看起来无害,但你不能模拟CakeEmail,因为它不允许依赖注入,而这在测试时是必需的。相反,该类CakeEmail应该以允许我们稍后更改它的方式实例化。例如:

App::uses('CakeEmail', 'Network/Email');
App::uses('AppController', 'Controller');

class UsersController extends AppController {

  public function register() {
    //registration logic
    $email = $this->_getEmailer();
    $email->from(array('site@example.com' => 'Site'));
    $email->to('you@example.com');
    $email->subject('Registered');
    $email->send('Thanks for registering!');
  }

  public function _getEmailer() {
    return new CakeEmail();
  }

}
Run Code Online (Sandbox Code Playgroud)

因为我们添加了一些辅助函数,所以我们现在可以测试它(通过模拟辅助函数)。

App::uses('CakeEmail', 'Network/Email');
App::uses('UsersController', 'Controller');

class UsersControllerTest extends ControllerTestCase {

  public function testRegister() {
    $controller = $this->generate('Users', array(
      'methods' => array(
        '_getEmailer'
      )
    ));
    $emailer = new CakeEmail();
    $emailer->transport('Debug');
    $controller
      ->expects($this->any())
      ->method('_getEmailer')
      ->will($this->returnValue($emailer));
  }

}
Run Code Online (Sandbox Code Playgroud)

此测试为我们的控制器创建一个模拟对象,并告诉它在调用该方法时返回我们新创建的$emailer对象。_getEmailer由于$emailer已将传输设置为“调试”,因此测试是安全的。

当然,由于现在我们正在决定该方法返回什么电子邮件对象,因此模拟该CakeEmail对象并期望某些返回变得微不足道。