5

コントローラーの1つのアクションでCakeEmailクラスを使用して電子メールを送信しています。電子メールコードを追加する前に正常に機能していたこのコントローラーの単体テストがあります。メールを追加した後、次のエラーが発生します。

SocketException:メールを送信できませんでした。

これは、ローカルマシンから電子メールを送信する方法がないという事実によるものです。

したがって、Config / email.phpのEmailConfigクラス内に2つの異なる構成オプションを含めることをお勧めします(データベース構成ファイルの動作と同様)。デフォルトはメールトランスポートを使用し、テストはデバッグトランスポートを使用します。これに関する問題は、データベース構成とは異なり、Cakeはテスト中に2つを自動的に切り替えないことです。

私が考えた唯一のことは、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()か、コンストラクターで指定する必要があります。だから私はこれが今私に2つの選択肢を残していると思います:

  1. コントローラーで、単体テストを行っているかどうかを確認してから(どういうわけか?)、「test」構成を使用します。
  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 に答える