PhpUnit模拟数据库对象

8hhllhi2  于 2023-04-19  发布在  PHP
关注(0)|答案(1)|浏览(120)

我想测试不同的客户从网上商店购买.客户来自不同的国家和不同的增值税适用于他们.通常da是来自数据库.现在我想模拟它.我已经做了它与产品在一个购物车.我已经这样做了:

protected function setUp(): void {
$this->stub = $this->createMock(Cart::class);
$product = (object) [
        'did' => '212814',
        'amount' => 1,
        'id' => '1664442400',
        'price' => '49.22',
        'name' => 'Table 150 cm',
];
$this->stub->method('getCartProducts')->willReturn([$product]);

我不知道这样做是否正确。
对于购物车来说,只有一个产品并在不同的测试中使用它是可以的。但是我有不同的客户,他们有不同的价值观。我应该如何创建他们?我应该这样做吗:

$this->stubCustomer1 = $this->createMock(Customer::class);
$customer1 = (object) ['name' => 'Paul', 'country' => 'UK']

$this->stubCustomer2 = $this->createMock(Customer::class);
$customer2 = (object) ['name' => 'Paulo', 'country' => 'Italy']

等等。就像这样,每次执行设置时都会创建所有这些。但并不是所有都需要。在每个测试方法中创建客户会更好吗?或者还有其他方法?

pgky5nke

pgky5nke1#

单元测试的常见模式是Arrange-Act-Assert(AAA)。这意味着每个测试(即测试类中的每个方法)都应该为其本身准备所有需要的状态。这使得测试彼此更加隔离,并且更容易推理。
我建议遵循这种模式。这意味着您直接在测试方法中进行所有特定于用例的准备,而不是在setUp()方法中。setUp()只能用于一些通用的准备,大多数是技术性的,而不是域感知的,例如重置一些依赖项,清除存储等。
为了简化测试的Arrange步骤,您可以为不同类型的客户创建工厂方法。并且没有必要像$this->stubCustomer2那样将每个存根保存在class属性中,只需创建它们并在case中使用,然后在new case中创建new。
所以,你的测试可能看起来像这样:

// ...

public function test_appropriate_vat_is_applied(): void
{
  // arrange
  $britain_customer = $this->makeCustomer('GB');
  $product_1 = $this->makeProduct('Eggs');
  $product_2 = $this->makeProduct('Milk');
  $cart = $this->makeCart($product_1, $product_2);

  // act
  // ... do something using prepared objects

  // assert
  // ... perform assertions
}

private function makeCustomer(string $country_code): object
{
  // create and return customer with given country code
}

private function makeProduct(string $name): object
{
  // create and return product with given name
}

private function makeCart(object ...$products): object
{
  // create and return cart containinig given products
}

另外,考虑尽可能地避免测试替身,以支持真实的的实现。也就是说,创建真正的客户,产品和购物车,而不是存根可能会更好。这将使未来的支持测试更容易。在单元测试中,真正需要替身的只有依赖于进程外的东西的类,如DB,网络或文件系统。

相关问题