猿问

在 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;

    }

}

该Crate->getDetails()方法返回一个带有来自 Box 对象的数据的 Details 对象。我想为此方法编写测试。


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 的构造函数。当我调用$crate->getDetails();它时,它应该返回一个 Details 对象


$item1 = "盒子物品"

$item2 = "详细信息"

$item3 = 空

我知道我可以通过对每个项目$this->assertEquals("BoxItem",$details->item1);等进行测试...但这是最好的方法吗?是否有一些 PHPUnit 工具来构建所需的详细信息结果并进行比较


例如


$this->assertEquals(MockDetailObject,$details)


还是我必须做一系列的断言来确保结果是我所期望的。


笔记*


对于我的示例,我知道这没什么大不了的,我快速构建它以解释我的意思。但是在我正在处理的代码中,我遇到了相同类型的问题,除了 Details 对象比 3 个字符串更复杂。


收到一只叮咚
浏览 147回答 2
2回答

手掌心

创建工厂并 100% 测试该工厂。据我了解,您的Crate班级既是实体又是工厂。Crate::getDetails您可以通过将此创建责任转移到工厂来进行重构。这样,您就可以仅使用“Given, When, Then”结构对创建逻辑进行单元测试。查看这篇关于干净测试的帖子并导航到“测试应该简洁且有意义”。拥有这种结构将帮助您分辨输入和输出是什么。例如:CrateDetailsFactoryTest.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();    }}我希望这很有用。干杯! :)

宝慕林4294392

使用上面的类,要正确地对其进行单元测试,您必须使用 DI 注入\Details::class其中一个getDetails()或 __constructor。然后为每个类的每个方法编写测试,模拟任何类依赖项/属性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 中,您可以使用 dataprovider 将一组值作为单独的测试传递给一个测试方法。 PHPUnit Docs - 数据提供者您还可以为 \Details::class 编写单独的单元测试,断言传递给 \Details::setItem1($item) 的内容实际上是在 item1 属性上设置的。IE。测试 \Details::class -//test2public function test() {    $details = new Details('some stuff');    $details->setItem1('expected');    self::assertSame('expected', $details->item1); }
随时随地看视频慕课网APP
我要回答