为了账号安全,请及时绑定邮箱和手机立即绑定

在PHPUnit中,当返回值是复杂对象时,我应该如何测试

在PHPUnit中,当返回值是复杂对象时,我应该如何测试

PHP
叮当猫咪 2022-08-05 15:59:12
我有3个班级。class Box{    public $item1;    public $item2;    public function __construct($item1,$item2){        $this->item = $item1;        $this->item2 = $item2;    }    public function getItem1(){        return $this->item1;    }}class Details{    public $stuff    public $item1;    public $item2;    public $item3;    public function __construct($stuff){       $this->stuff = $stuff    }    public function setItem1($item){        $this->item1 = $item;    }    public function setItem2($item){        $this->item2 = $item;    }}class Crate{    public $box;    private $stuffString = "Stuff";    public function __construct(Box $box){        $this->box = $box;    }    public function getDetails(){        $details = new Details($stuffString);        $details->setItem1($box->item1);        $details->setItem2("Detail");        return $details;    }}该方法返回一个 Details 对象,其中包含 Box 对象中的数据。我想为此方法编写测试。Crate->getDetails()function test_get_details(){    $box = Mockery::mock(Box::class);    $box->shouldReceive('getItem1')->andReturn("BoxItem");    $crate= new Crate($box);    $details = $crate->getDetails();    $this->assertInstanceOf(Details::class,$details);}我创建了 Box 类的模拟,并将其传递给 Crate 的构造函数。当我调用它时,它应该返回一个 Details 对象$crate->getDetails();$item 1 = “BoxItem”$item 2 = “详细信息”$item 3 = 空我知道我可以通过为每个项目做测试这一点等等...但这是最好的方法吗?有没有一些PHPUnit工具来构建所需的Detials结果并进行比较$this->assertEquals("BoxItem",$details->item1);例如$this->assertEquals(MockDetailObject,$details)还是我必须做一系列的断言来确保结果是我所期望的。注*我知道对于我的例子来说,这不是什么大不了的事情,我很快就建立了它来解释我的意思。但是在我正在处理的代码中,我遇到了相同类型的问题,除了 Details 对象比 3 个字符串更复杂。
查看完整描述

2 回答

?
HUX布斯

TA贡献1876条经验 获得超6个赞

创建一个工厂并100%测试该工厂。

据我所知,您的 Crate 类既是实体又是工厂。您可以通过将此创建责任移动到工厂来重构。Crate::getDetails

这样,您只能通过使用“给定,何时,然后”结构对创建逻辑进行单元测试。查看这篇关于干净测试的文章,并导航到“测试应该简洁而有意义”。

拥有此结构将帮助您判断输入和输出是什么。例如:

板条箱详细信息工厂测试.php

class CrateDetailFactoryTest extends TestCase

{

    public function testCreateCrateDetail(): void

    {

        // Given

        $crate = $this->givenThereIsACrate();

        $boxes = $this->givenThereAreTwoRedBoxes();


        // When

        $crateDetail = $this->crateDetailFactory->createCrateDetail(

            $crate,

            $boxes

        );


        // Then

        // (Unnecessary instanceof, if you have strict return types)

        self::assertInstanceOf(Detail::class, $crateDetail);


        self::assertCount(2, $crateDetail->getBoxes());

        self::assertEquals(

            'red',

            $crateDetail->getBoxes()->first()->getColor()

        );

    }

}

这样就可以覆盖您的创建逻辑;从这里,你可以简单地将你的工厂注入你需要的地方,在单元测试期间,你只需模拟它:


板条箱服务.php


class CrateServiceTest extends TestCase

{

    private $crateDetailFactory;


    private $crateService;


    public function setUp(): void

    {

        $this->crateDetailFactory = $this->prophesize(CrateDetailFactory::class);


        $this->crateService = new CrateService(

            $this->crateDetailFactory->reveal()

        );

    }


    public function testAMethodThatNeedsCrateDetails(): void

    {

        // Given

        $crate = $this->givenIHaveACrateWithTwoBoxesInIt();

        $boxes = $crate->getBoxes();


        // When

        $result = $this->crateService->AMethodThatNeedsCrateDetails();


        // Then

        $this->crateDetailFactory->createCrateDetail($crate, $boxes)

            ->shouldBeCalledOnce();

    }

}

我希望这是有用的。干杯!:)


查看完整回答
反对 回复 2022-08-05
?
九州编程

TA贡献1785条经验 获得超4个赞

使用上面的类,要正确进行单元测试,您必须使用DI注入其中一个或__constructor。然后为每个类的每个方法编写测试,模拟任何类依赖项/属性\Details::classgetDetails()


class Create

{

    public function getDetails(\Details $details)

}


//test.php

$mockDetails = $this->createMock(\Details::class)

                           ->expects($this-once())

                           ->method('item1')

                           ->with('some_arg')

                           ->willReturn('xyz')


$mockBox = $this-createMock(\Box::class)

   ......


$crate = new Create($boxMock);

$result = $crate->item1($mockDetails);

$this-assertSame('xyz', $result);

如果对于一种方法来说,这感觉像是你的嘲笑方式,那么你应该考虑重构以使代码更具可测试性。


就多个项目的断言而言,在 PHPUnit 中,您可以使用数据提供器将值数组作为单独的测试传递给一个测试方法。PHPUnit Docs - Data Providers


您还将为 \Details::类编写单独的单元测试,该类断言传递给 \Details::setItem1($item) 的内容实际上是在 item1 属性上设置的。即。


测试 \详细信息::类 -


//test2


public function test() {

    $details = new Details('some stuff');

    $details->setItem1('expected');

    self::assertSame('expected', $details->item1); 

}


查看完整回答
反对 回复 2022-08-05
  • 2 回答
  • 0 关注
  • 174 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号