猿问

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

我有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 个字符串更复杂。


叮当猫咪
浏览 177回答 2
2回答

HUX布斯

创建一个工厂并100%测试该工厂。据我所知,您的 Crate 类既是实体又是工厂。您可以通过将此创建责任移动到工厂来重构。Crate::getDetails这样,您只能通过使用“给定,何时,然后”结构对创建逻辑进行单元测试。查看这篇关于干净测试的文章,并导航到“测试应该简洁而有意义”。拥有此结构将帮助您判断输入和输出是什么。例如:板条箱详细信息工厂测试.phpclass 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()        );    }}这样就可以覆盖您的创建逻辑;从这里,你可以简单地将你的工厂注入你需要的地方,在单元测试期间,你只需模拟它:板条箱服务.phpclass 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();    }}我希望这是有用的。干杯!:)

九州编程

使用上面的类,要正确进行单元测试,您必须使用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 属性上设置的。即。测试 \详细信息::类 -//test2public function test() {    $details = new Details('some stuff');    $details->setItem1('expected');    self::assertSame('expected', $details->item1); }
随时随地看视频慕课网APP
我要回答