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

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

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

PHP
收到一只叮咚 2022-07-22 16:46:04
我有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 个字符串更复杂。
查看完整描述

2 回答

?
手掌心

TA贡献1942条经验 获得超3个赞

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


据我了解,您的Crate班级既是实体又是工厂。Crate::getDetails您可以通过将此创建责任转移到工厂来进行重构。


这样,您就可以仅使用“Given, When, Then”结构对创建逻辑进行单元测试。查看这篇关于干净测试的帖子并导航到“测试应该简洁且有意义”。


拥有这种结构将帮助您分辨输入和输出是什么。例如:


CrateDetailsFactoryTest.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-07-22
?
宝慕林4294392

TA贡献2021条经验 获得超8个赞

使用上面的类,要正确地对其进行单元测试,您必须使用 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 -


//test2


public function test() {

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

    $details->setItem1('expected');

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

}


查看完整回答
反对 回复 2022-07-22
  • 2 回答
  • 0 关注
  • 145 浏览

添加回答

举报

0/150
提交
取消
微信客服

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

帮助反馈 APP下载

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

公众号

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