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

时间:2012-10-03 16:33:09

标签: unit-testing cakephp phpunit

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

}

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


更新 - 4/10/2012

我想我是以错误的方式解决这个问题。查看this answer似乎默认情况下甚至没有加载$default配置,您必须通过调用CakeEmail::config()方法指定它或在构造函数中指定它。所以我认为这给我留下了两个选择:

  1. 在控制器中检查我们是否进行单元测试(以某种方式?)然后使用'test'配置。
  2. 设置我的电脑以便能够发送电子邮件。
  3. 我宁愿做第一个但是不确定如果我们进行单元测试而不用检查来控制控制器动作如何做到这一点,这样做似乎是错误的。

1 个答案:

答案 0 :(得分: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,因为它不允许dependency injection,这在测试时是必需的。相反,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对象并期望某些返回变得微不足道。