В PHPUnit, как я должен проверять, когда возвращаемые значения являются сложными объектами - PullRequest
0 голосов
/ 16 января 2020

У меня есть 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() возвращает объект Details с данными из объекта Box. Я хочу написать тесты для этого метода.

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 = "BoxItem"
  • $ item2 = "Detail"
  • $ item3 = null

Я знаю, что могу проверить это, выполнив для каждого элемента $this->assertEquals("BoxItem",$details->item1); et c ... но разве это лучший способ go? Есть ли какой-нибудь инструмент PHPUnit для создания желаемого результата Detials и сравнения его

Например

$this->assertEquals(MockDetailObject,$details)

или мне нужно сделать серию утверждений, чтобы сделать уверен, что результат - то, что я ожидаю.

Примечание *

Я знаю для своего примера, что это не так уж сложно, я быстро объяснил, что я имею в виду. Но в коде, над которым я работаю, я столкнулся с такой же проблемой, за исключением того, что объект Details сложнее, чем просто 3 строки.

Ответы [ 2 ]

2 голосов
/ 18 января 2020

TL; DR: создайте фабрику и протестируйте эту фабрику на 100%.

Из того, что я понял, ваш класс Crate является и сущностью, и фабрикой. Вы можете выполнить рефакторинг Crate::getDetails, перенеся эту ответственность за создание на фабрику.

Таким образом, вы сможете провести модульное тестирование логики создания c только с помощью «Задано, Когда, Тогда». " структура. Прочтите этот пост о чистых тестах и перейдите к разделу «Тесты должны быть краткими и осмысленными».

Наличие этой структуры поможет вам определить входные и выходные данные. Например:

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()
        );
    }
}

Этим покрыты ваши логи создания c; Отсюда вы можете просто ввести свой завод, где вам нужно, и во время модульного тестирования вы просто издеваетесь над ним:

CrateService. 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();
    }
}

Я надеюсь, что это было полезно. Ура! :)

1 голос
/ 17 января 2020

Используя вышеприведенные классы, для правильного модульного тестирования вы должны будете использовать DI для ввода \Details::class в getDetails() или в конструктор __. Затем напишите тесты для каждого метода каждого класса, высмеивая любые зависимости / свойства класса

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 - поставщики данных

Вы также могли бы написать отдельные модульные тесты для класса \ Details ::, который проверял, что передается в \ Details :: setItem1 ($ item) фактически установлен на свойство item1. Ie.

Тестирование \ Подробности :: класс -

//test2

public function test() {
    $details = new Details('some stuff');
    $details->setItem1('expected');
    self::assertSame('expected', $details->item1); 
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...