【问题标题】:PhpSpec testing service layers correctlyPhpSpec 正确测试服务层
【发布时间】:2018-08-27 16:45:59
【问题描述】:

该应用程序具有多个服务,并且某些功能与多个服务重叠。由于 PhpSpec wraps objects 我一次只能测试一项服务。

背景:

群组服务可以创建“红车”、“蓝车”等群组。 汽车服务可以将汽车分配到组中。

团体服务:

function createGroup($name);

汽车服务:

function assignCarToGroup($car, $group);

问题:

当我在 PhpSpec 中描述 汽车服务 时,我想确保它可以检查汽车是否已成功分配到组。

为此,组需要在分配汽车之前存在

问题:

我需要在运行文件之前创建组。这就是我面临的问题。

我是否会依赖前面的示例(GroupServiceSpec)来创建组,我会让测试变得非常脆弱。

我如何测试汽车是否已在汽车服务中成功分配?

GroupService.php

<?php

namespace App;

class GroupService
{
    private $group_repository;

    public function __construct($group_repository)
    {
        $this->group_repository = $group_repository;
    }

    public function CreateGroupServiceCall($name)
    {
        return $this->group_repository->createGroup($name);
    }
}

GroupRepository.php

<?php

namespace App;

class GroupRepository
{
    public function createGroup($name)
    {
        file_put_contents($name . '.db', '');

        return true;
    }
}

汽车服务.php

<?php

namespace App;

class CarService
{
    private $car_repository;

    public function __construct($car_repository)
    {
        $this->car_repository = $car_repository;
    }

    public function AssignCarToGroupServiceCall($car, $group_name)
    {
        return $this->car_repository->assignCarToGroup($car, $group_name);
    }
}

CarRepository.php

<?php

namespace app;

class CarRepository
{
    public function assignCarToGroup($car, $name)
    {
        $file_path = $name . '.db';

        if (file_exists($file_path) == true) {
            file_put_contents($file_path, $car);

            return true;
        } else {
            return false;
        }
    }
}

这里是测试:

GroupServiceSpec.php

<?php

namespace spec\App;

use App\GroupRepository;
use App\GroupService;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class GroupServiceSpec extends ObjectBehavior
{

    function let()
    {
        $group_repository = new GroupRepository();
        $this->beConstructedWith($group_repository);
    }

    function letGo()
    {
        # Clean up and remove all DB files
        array_map('unlink', glob(__DIR__ . "/../../*.db"));
    }

    function it_is_initializable()
    {
        $this->shouldHaveType(GroupService::class);
    }

    function it_can_create_a_group()
    {
        $this->CreateGroupServiceCall('New Group')->shouldBe(true);
    }

}

GroupRepositorySpec.php

<?php

namespace spec\App;

use App\GroupRepository;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class GroupRepositorySpec extends ObjectBehavior
{

    function letGo()
    {
        # Clean up and remove all DB files
        array_map('unlink', glob(__DIR__ . "/../../*.db"));
    }

    function it_is_initializable()
    {
        $this->shouldHaveType(GroupRepository::class);
    }

    function it_can_create_group()
    {
        $status = $this->createGroup('test');
        $status->shouldBe(true);
    }
}

CarServiceSpec.php

<?php

namespace spec\App;

use App\CarRepository;
use App\CarService;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class CarServiceSpec extends ObjectBehavior
{

    function let()
    {
        $car_repository = new CarRepository();
        $this->beConstructedWith($car_repository);
    }

    function it_is_initializable()
    {
        $this->shouldHaveType(CarService::class);
    }

    function it_can_assign_car_to_group()
    {
        $car = 'Car I';
        $group_name = 'Group1';

        $this->AssignCarToGroupServiceCall($car, $group_name)->shouldBe(true);
    }
}

CarRepositorySpec.php

<?php

namespace spec\App;

use app\CarRepository;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class CarRepositorySpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType(CarRepository::class);
    }

    function it_can_assign_car_to_group()
    {
        # how can I test this part, as the group is not existing now.
        # If I'm not to create the group, how can I test if this code works at all?
        # Do I have a design error?

        $car = 'Car1';
        $group_name = 'Company';

        $this->assignCarToGroup($car, $group_name)->shouldBe(true);
    }
}

【问题讨论】:

  • 我认为您的用例可能使用了错误的工具。 Phpspec 用于单元测试,因此如果您指定汽车服务,您可能必须存根组服务。如果您想测试两者之间的集成,请使用 Behat。
  • 你好@gvf 非常感谢你。我实际上也在使用 Behat。但是我也想确保这个特定的功能在单元测试级别上工作。我认为嘲笑它是不够的,因为需要真实的条目。如果您还想在单元测试级别对其进行测试,您会留下没有单元测试的解决方案吗?
  • 单元级测试单独测试一个类,您想测试两个类之间的交互,所以我要做的就是将测试移至 Behat。如果您发布两个课程的完整代码,我可能会为您提供进一步的帮助。
  • @gvf 这里是我编写的代码的快速实现:ufile.io/i4rt9 我也将其添加到问题中
  • 好吧,首先,永远不要调用一个类 '*Service.php` :-) 它太模糊了,如果你有多个服务会发生什么?在您的情况下,在 CarServiceSpec 中您只能指定行为:您只能指定对 car_repository-&gt;assignCarToGroup 的调用已进行,仅此而已。

标签: architecture tdd repository-pattern behat phpspec


【解决方案1】:

您的汽车服务规范应如下所示:

class CarServiceSpec extends ObjectBehavior
{

  function let(CarRepository $carRepository)
  {
    $this->beConstructedWith($carRepository);
  }

  function it_is_initializable()
  {
    $this->shouldHaveType(CarService::class);
  }

  function it_can_assign_car_to_group(CarRepository $carRepository)
  {
    $car = 'Car I';
    $group_name = 'Group1';

    $carRepository->assignCarToGroup($car, $group_name)->shouldBeCalled()->willReturn(true);

    $this->AssignCarToGroupServiceCall($car, $group_name)->shouldReturn(true);
  }
}

【讨论】:

  • 谢谢@gvf 在这部分之后,即使我知道结果不存在,我是否应该将其视为“已验证”?我可以说,“我的重点是部分,是否调用了正确的方法?”非常感谢
  • 是的,这就是你能做的。如果您想确保它在 DB 中持久存在,请进行 Behat 测试。
  • 非常感谢!整个事情也有它的顶部,我只是想确定一下。谢谢!!
猜你喜欢
  • 1970-01-01
  • 2015-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-01
  • 2012-01-25
  • 2013-03-04
  • 1970-01-01
相关资源
最近更新 更多