2017-02-17 12 views
2

私は電子メールクラスオブジェクトを作成し、それを第3のクラス(電子メール送信者)に渡すサービスを持っています。スタブ関数の引数からプロパティを取得するには?

この機能によって生成される電子メールの本文を確認したいと思います。

Service.php

class Service 
{ 
    /** @var EmailService */ 
    protected $emailService; 

    public function __construct(EmailService $emailService) 
    { 
     $this->emailService = $emailService; 
    } 

    public function testFunc() 
    { 
     $email = new Email(); 
     $email->setBody('abc'); // I want to test this attribute 

     $this->emailService->send($email); 
    } 
} 

Email.php:

class Email 
{ 
    protected $body; 

    public function setBody($body) 
    { 
     $this->body = $body; 
    } 
    public function getBody() 
    { 
     return $this->body; 
    } 
} 

EmailService.php

interface EmailService 
{ 
    public function send(Email $email); 
} 

だから私はemailServiceや電子メールのスタブクラスを作成します。しかし、私は電子メールの本文を検証することはできません。体が生成され、実際のアプリで

Call to undefined method Prophecy\Prophecy\MethodProphecy::shouldBe() in /private/tmp/phpspec/spec/App/ServiceSpec.php on line 18 

:電子メールがテスト機能

class ServiceSpec extends ObjectBehavior 
{ 
    function it_creates_email_with_body_abc(EmailService $emailService, Email $email) 
    { 
     $this->beConstructedWith($emailService); 

     $emailService->send($email); 
     $email->getBody()->shouldBe('abc'); 
     $this->testFunc(); 
    } 
} 

内に作成されるため、$メール:> setBody()が呼び出された場合、また、私はこれを持ってチェックすることはできませんそれが正しく生成されているかどうかをテストします。どうやってやるの? PHPSpecので

答えて

2

あなたが(とさえスタブ上か、スペックファイルでそれらを作成すると皮肉って)作成したオブジェクトへのアサーションのこの種を作ることができません:あなたは上で一致させることができます唯一のことは、SUS(S ystem Uですnder S pec)とその戻り値(存在する場合)。

私はあなたのテストが


私の視点から、間違って何

new使い方Service

内部のデザインとテスト容易性を向上させるために に合格するために少しのガイドを書くつもりです

なぜ間違っているか

Serviceには2つの責任があります:Emailオブジェクトを作成し、その仕事をします。 SRPのブレークはSOLID principlesです。 (私は以下の表示されるように)また、スペックパス

を作るために

回避策をテストするには、オブジェクトの作成に対する制御を失い、これはなる、あなたが見つけたとして、非常に困難な私は、この種の工場を使用することをお勧めしますタスク劇的に増加するテスト容易ので、しかし、この場合には、あなたは限りsetBodyは、SUSの実装では変更されないよう

class ServiceSpec extends ObjectBehavior 
{ 
    function it_creates_email_with_body_abc(EmailService $emailService) 
    { 
     $this->beConstructedWith($emailService); 

     //arrange data 
     $email = new Email(); 
     $email->setBody('abc'); 

     //assert 
     $emailService->send($email)->shouldBeCalled(); 

     //act 
     $this->testFunc(); 
    } 
} 

を次のようにスペックを書き換えることで、あなたのテストパスを作ることができ、これは動作します。 しかし、これはPHPSpecの観点からの匂いでなければならないので、私はそれをお勧めしません。

は工場

を使用してください今、あなたは工場のcreateEmailがあなたの期待していることを確認している工場

class EmailFactory() 
{ 
    public function createEmail($body) 
    { 
     $email = new Email(); 
     $email->setBody($body); 

     return $email; 
    } 
} 

とそのスペック

public function EmailFactorySpec extends ObjectBehavior 
{ 
    function it_is_initializable() 
    { 
     $this->shouldHaveType(EmailFactory::class); 
    } 

    function it_creates_email_with_body_content() 
    { 
     $body = 'abc'; 
     $email = $this->createEmail($body); 

     $email->shouldBeAnInstanceOf(Email::class); 
     $email->getBody()->shouldBeEqualTo($body); 
    } 
} 

を作成します。あなたが気づくことができるように、ここでは責任が隠されています。他の場所で心配する必要はありません(メールの送信方法を選択できる戦略について考えてください。直接待ち行列に入れておくなど、元のアプローチで対処すれば電子メールが期待どおりに作成され、今はそうではないという具体的な戦略をテストする必要があります)。

最後に、私は、この例で自分を試していなかったし、いくつかのタイプミスがあるかもしれませんが、私は100よスペックパス(正しい道)

function it_creates_email_with_body_abc(
    EmailService $emailService, EmailFactory $emailFactory, Email $mail 
) { 
    $this->beConstructedWith($emailService); 
    // if you need to be sure that body will be 'abc', 
    // otherwise you can use Argument::type('string') wildcard 
    $emailFactory->createEmail('abc')->willReturn($email); 
    $emailService->send($email)->shouldBeCalled(); 

    $this->testFunc(); 
} 

を作るSUSで

class Service 
{ 
    /** @var EmailService */ 
    protected $emailService; 

    /** @var EmailFactory */ 
    protected $emailFactory; 

    public function __construct(
     EmailService $emailService, EmailFactory $emailFactory 
    ) { 
     $this->emailService = $emailService; 
     $this->emailFactory = $emailFactory; 
    } 

    public function testFunc() 
    { 
     $email = $this->emailFactory->createEmail('abc'); 
     $this->emailService->send($email); 
    } 
} 

を工場を統合このアプローチの確かさ:私はすべての読者には明らかであることを願っています。

+0

ありがとうございます。本当に感謝しております! – xorik

+0

ここに作業コードをアップロードしました:https://gist.github.com/xorik/89da2c3c065d1b6b6d10d915191305f2 – xorik

関連する問題