5

我在我的一个控制器的操作中使用 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;
        }
    }

}

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


更新 - 2012 年 4 月 10 日

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

  1. 在控制器中检查我们是否在进行单元测试(不知何故?),然后使用“测试”配置。
  2. 将我的计算机设置为能够发送电子邮件。

我宁愿做第一个,但不确定如何在不通过检查我们是否进行单元测试来膨胀控制器操作的情况下做到这一点,这样做似乎是错误的。

4

1 回答 1

4

最简单的方法可能是在测试时切换到 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!');
  }

}

这看起来无害,但你不能模拟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();
  }

}

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

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));
  }

}

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

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

于 2012-10-10T14:28:06.143 回答