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

Unit testing 在CakePHP中进行单元测试时使用不同的电子邮件配置,unit-testing,cakephp,phpunit,Unit Testing,Cakephp,Phpunit,在我的一个控制器的操作中,我正在使用CakeEmail类发送电子邮件。我对这个控制器进行了一个单元测试,在添加电子邮件代码之前,该测试运行良好。添加电子邮件后,我收到以下错误: SocketException:无法发送电子邮件 这是因为我没有任何办法从本地机器上发送电子邮件 因此,我认为最好在Config/email.php中的EmailConfig类中有两个不同的配置选项(类似于数据库配置文件的工作方式)。默认设置使用邮件传输,测试使用调试传输。问题在于,与数据库配置不同,Cake在测试期间不

在我的一个控制器的操作中,我正在使用CakeEmail类发送电子邮件。我对这个控制器进行了一个单元测试,在添加电子邮件代码之前,该测试运行良好。添加电子邮件后,我收到以下错误:

SocketException:无法发送电子邮件

这是因为我没有任何办法从本地机器上发送电子邮件

因此,我认为最好在Config/email.php中的EmailConfig类中有两个不同的配置选项(类似于数据库配置文件的工作方式)。默认设置使用邮件传输,测试使用调试传输。问题在于,与数据库配置不同,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()
方法或在构造函数中指定它。所以我认为现在我有两个选择:

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

  • 我宁愿做第一步,但不确定如何在不检查控制器操作的情况下完成。如果我们正在进行单元测试,那么这样做似乎是错误的。

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

    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();
      }
    
    }
    
    因为我们添加了一个小的helper函数,所以现在可以测试它(通过模拟helper函数)

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


    当然,因为现在我们正在决定该方法返回什么电子邮件对象,所以模拟
    CakeEmail
    对象并期望某些返回变得很简单。

    只需在测试用例的
    setUp
    中设置配置即可。仅供参考,Cake附带了一个
    DebugTransport
    ,您可以在配置设置中使用它,它将执行除实际发送之外的所有操作,并返回标题和消息,供您在测试中使用。超级有用:)你的问题可能比考试更大。电子邮件在现实生活中经常失败;确保代码说明了这一点。我会1)确保成功和不成功的电子邮件都有测试,2)尝试使用模拟CakeEmail对象。不知道你会怎么做#2;否则我会回答。@jeremyharris我确实知道DebugTransport,事实上我正在寻找替代MailTransport的方法。你能详细介绍一下如何在安装程序中设置配置吗?我还没弄清楚它是如何工作的。@BradKoch感谢这里的提示。是的,我确实想到了模拟CakeEmail对象,但我不确定如何在没有任何set*方法的情况下将其传递给控制器。必须考虑一下。是否可以将_getEmailr函数移动到AppController,以便在多个控制器中使用,并且仍然以这种方式工作?@BoštjanPišler是的,只要这些子控制器不以改变其应做工作的方式覆盖
    _getEmailr()
    。(注意:这是一个非常古老的答案,仅适用于蛋糕2.x。)
    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));
      }
    
    }