phpunit-使用在不同测试中测试的类的测试类

发布于 2025-02-01 05:40:13 字数 1392 浏览 2 评论 0原文

我找不到有关此主题的好信息,所以我正在召唤这个问题。我们正在为应用程序编写测试。现在有使用其他类的嵌套类,依此类推。每个班级都有自己的测试。例如:

// Some class
class SomeCalculator {
    public function add($a, $b): int {
        return $a + $b;
    }
}

// Its trivial test
class SomeCalculatorTest {
    public function testAdding() {
        $calc = new SomeCalculator();
        $this->assertEquals(2, $calc->add(1, 1));
    }
}

现在考虑一下:

// Some other class that uses SomeCalculator()
class SomePerson {
    protected $name;
    protected $age;

    public function __construct($name, $a, $b) {
        $this->name = $name;
        $this->age = (new SomeCalculator())->add($a, $b);
    }
    public function getName(): string {
        return $this->name;
    }
    public function getAge(): int {
        return $this->age;
    }
}

// Trivial test
class SomePersonTest {
    public function testCreating() {
        $person = new SomePerson('Horst', 1, 1);
        $this->assert('Horst', $person->getName());
        $this->assert(2, $person->getAge()); // should I remove this assert?
    }
}

现在的问题是 - 在为someperson编写测试时,我是否还要断言嵌套类somecalculator的计算结果?类somecalculator有自己的测试,因此我相信可以。 我觉得我们正在复制somecalculatorsomecalculatortestestsomeperStesteanteTEST中的断言。

谢谢。

I can't find good information about this topic so I'm summoning this question. We're writing tests for our application. Now there are nested classes which use other classes and so on. Each class has its own test. E.g.:

// Some class
class SomeCalculator {
    public function add($a, $b): int {
        return $a + $b;
    }
}

// Its trivial test
class SomeCalculatorTest {
    public function testAdding() {
        $calc = new SomeCalculator();
        $this->assertEquals(2, $calc->add(1, 1));
    }
}

Now consider this:

// Some other class that uses SomeCalculator()
class SomePerson {
    protected $name;
    protected $age;

    public function __construct($name, $a, $b) {
        $this->name = $name;
        $this->age = (new SomeCalculator())->add($a, $b);
    }
    public function getName(): string {
        return $this->name;
    }
    public function getAge(): int {
        return $this->age;
    }
}

// Trivial test
class SomePersonTest {
    public function testCreating() {
        $person = new SomePerson('Horst', 1, 1);
        $this->assert('Horst', $person->getName());
        $this->assert(2, $person->getAge()); // should I remove this assert?
    }
}

Now the question is - while writing test for SomePerson should I assert also result of calculation of nested class SomeCalculator? Class SomeCalculator has its own tests so I am confident that it is OK.
I feel like we're duplicating SomeCalculator's asserts in SomeCalculatorTest and in SomePersonTest.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

彩扇题诗 2025-02-08 05:40:13

好的,因此此代码的主要故障是很难测试。假设testCreating是单元测试,则不必检查与正在测试的服务不同的其他组件的行为。因此,让我们以例如测试更容易进行重构来看一下:

        $this->age = (new SomeCalculator())->add($a, $b);

上面的主要问题。由于在构造函数中使用了新的创建对象,因此两种分类都非常耦合。为了解决我们应该使用依赖性反转,我们也应该将唯一责任的类别分开,即包含数据(dto?),例如通过使用例如 factory模式。在这种情况下,重构示例应该看起来像这样:

class SomePerson {
    protected $name;
    protected $age;

    public function __construct(string $name, int $age) {
        $this->name = $name;
        $this->age = $age;
    }
    public function getName(): string {
        return $this->name;
    }
    public function getAge(): int {
        return $this->age;
    }
}

class PersonFactory {

    private SomeCalculator $calculator;

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

    public function create($name, $a, $b): SomePerson 
    {
        return new SomePerson($name, $this->calculator->add($a, $b));
    }
}

然后对其进行测试可能是这样的:

class PersonFactoryTest extends TestCase {

    public function testCreate(): string 
    {
        $a = 1;
        $b = 1;
        $age = 2;
        $name = 'Horst'
        $calculator = $this->createMock(SomeCalculator::class);
        $calculator->method('calculate')->with($a, $b)->willReturn($age);
        $factory = new PersonFactory($calculator);

        $person = $factory->create($name, $a, $b);

        $this->assertEqual($name, $person->getName());
        $this->assertEqual($age, $person->getAge());
    }
} 

使用模拟这里很重要,因为从PersonFactory计算器的观点行为行为't重要,因此在这种情况下,测试应只能检查是否提供了适当的参数来添加甲基,并且结果是否用于创建新对象。检查是否正确添加计算器应在其他测试中进行。

编辑:

由于模拟,它也是有效的测试(模拟的值只要被模拟就无关紧要,但是对于其他程序员来说,这将使更难理解):

public function testCreate(): string 
{
    $a = 50;
    $b = -50;
    $age = 999;
    $name = 'Horst'
    $calculator = $this->createMock(SomeCalculator::class);
    $calculator->method('calculate')->with($a, $b)->willReturn($age);
    $factory = new PersonFactory($calculator);

    $person = $factory->create($name, $a, $b);

    $this->assertEqual($name, $person->getName());
    $this->assertEqual($age, $person->getAge());
}

Okay, so main fault of this code is that it is hard to test. Assuming that testCreating is unit test it shouldn't have to check behavior of other components different than service under test. So let's take a look on example, and refactor it to be easier for testing:

        $this->age = (new SomeCalculator())->add($a, $b);

Main problem with example is above. Because of using new created object in constructor both classed are extremely coupled. To resolve that we should use dependency inversion, also we should separate class that only responsibility is to contain data (DTO?), from it creation by using for example factory pattern. In this case refactored example should look like this:

class SomePerson {
    protected $name;
    protected $age;

    public function __construct(string $name, int $age) {
        $this->name = $name;
        $this->age = $age;
    }
    public function getName(): string {
        return $this->name;
    }
    public function getAge(): int {
        return $this->age;
    }
}

class PersonFactory {

    private SomeCalculator $calculator;

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

    public function create($name, $a, $b): SomePerson 
    {
        return new SomePerson($name, $this->calculator->add($a, $b));
    }
}

And then test for it could look like this:

class PersonFactoryTest extends TestCase {

    public function testCreate(): string 
    {
        $a = 1;
        $b = 1;
        $age = 2;
        $name = 'Horst'
        $calculator = $this->createMock(SomeCalculator::class);
        $calculator->method('calculate')->with($a, $b)->willReturn($age);
        $factory = new PersonFactory($calculator);

        $person = $factory->create($name, $a, $b);

        $this->assertEqual($name, $person->getName());
        $this->assertEqual($age, $person->getAge());
    }
} 

Using mock here is important, because from PersonFactory point of view behavior of calculator doesn't matter, so test should in this case test can only check if proper parameters are provided to add methody and if it result is used to creating new objects. Checking if calculator is adding correctly should be done in another tests.

Edit:

Because of mock it would be also be valid test (mocked values doesn't matter as long as they are mocked, but it would make it harder to understand for others programmers):

public function testCreate(): string 
{
    $a = 50;
    $b = -50;
    $age = 999;
    $name = 'Horst'
    $calculator = $this->createMock(SomeCalculator::class);
    $calculator->method('calculate')->with($a, $b)->willReturn($age);
    $factory = new PersonFactory($calculator);

    $person = $factory->create($name, $a, $b);

    $this->assertEqual($name, $person->getName());
    $this->assertEqual($age, $person->getAge());
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文