使用嵌套依赖关系和工厂类执行单元测试 [英] Performing unit testing with nested dependencies and Factory classes

查看:146
本文介绍了使用嵌套依赖关系和工厂类执行单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新的单元测试和PHPUnit,但是我最近读过很多关于设计模式和孤立的测试,我决定重构一个我正在努力摆脱静态类,单身,硬编码的依赖关系以及在全球范围内定义的任何其他内容,希望能够使其可测试,而不是将来笨拙的痛苦,因为它是一个长期项目。

I'm new to unit testing and PHPUnit, but I've reading a lot lately about design patterns and isolated tests and I've decided to refactor an application I'm working on to get rid of static classes, singletons, hardcoded dependencies and anything else defined on the global scope, hopefully making it "testable" and not a pain in the ass to mantain in the future, since it is meant to be a long term project.

到目前为止,我相信我理解了单元测试背后的理论,但是我想知道,在一个代理将对象的嵌套依赖关系处理到工厂的情况下,应该如何进行单元测试工厂,或者是它只是多余的测试吗?测试依赖关系的最佳方法是什么是最佳方法?

So far I believe I understand the theory behind unit testing, but I was wondering, in a scenario where one delegates handling nested dependencies of objects to a Factory, how should one go about unit testing said Factory, or is it just redundant to test it? And what is the best approach to test that the "chain" of dependencies work well in sync?

让我来说明问题。假设你有以下遗产代码:

Let me illustrate the questions. Suppose you have the following "legacy" code:

class House {
    protected $material;
    protected $door;
    protected $knob;

    public function __construct() {
        $this->door = new Door();
        $this->knob = $this->door->getKnob();
        $this->material = "stone";

        echo "House material: ".$this->material . PHP_EOL . "<br/>";
        echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>";
        echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>";
    }
}

class Door {
    protected $material;
    protected $knob;

    public function __construct() {
        $this->knob = new Knob();
        $this->material = "wood";
    }

    public function getKnob() {
        return $this->knob;
    }

    public function getMaterial () {
        return $this->material;
    }

}

class Knob {
    protected $material;

    public function __construct() {
        $this->material = "metal";
    }

    public function getMaterial () {
        return $this->material;
    }
}

$house = new House();

这是(就我的理解而言)对于单元测试是不好的,所以我们替换硬编码的依赖项与DI +一个工厂类:

This is (as far as my understanding goes) bad for unit testing, so we replace the hardcoded dependencies with DI + a Factory class:

class House {
    protected $material;
    protected $door;
    protected $knob;

    public function __construct($door) {
        $this->door = $door;
        $this->knob = $this->door->getKnob();
        $this->material = "stone";

        echo "House material: ".$this->material . PHP_EOL . "<br/>";
        echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>";
        echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>";
    }
}

class Door {
    protected $material;
    protected $knob;

    public function __construct($knob) {
        $this->knob = $knob;
        $this->material = "wood";
    }

    public function getKnob() {
        return $this->knob;
    }

    public function getMaterial () {
        return $this->material;
    }

}

class Knob {
    protected $material;

    public function __construct() {
        $this->material = "metal";
    }

    public function getMaterial () {
        return $this->material;
    }
}

class HouseFactory {
    public function create() {
        $knob = new Knob();
        $door = new Door($knob);
        $house = new House($door);

        return $house;
    }
}

$houseFactory = new HouseFactory();
$house = $houseFactory->create();

现在(再一次,据我所知)房子,门和旋钮可以进行单元测试嘲笑的依赖性很好。但是:

Now (and again, as far as I understand) House, Door and Knob can be unit tested with mocked dependencies just fine. But:

1)现在HouseFactory会发生什么?

1) What happens with HouseFactory now?

只需一个:


  • 不测试它,因为它没有任何值得测试的应用程序逻辑,工厂通常保持这种方式。假设如果对House,Door& Knob通过工厂应该是好的。

  • 重新确定工厂的方式,即使用类中的函数来获取每个实例,以便通过PHPUnit覆盖这些函数以返回模拟对象,以防在课堂上有一些额外的逻辑可以在将来使用一些测试。

2)是否可行要立即依赖几个(不是嘲笑的)依赖关系进行测试?我明白这是技术上不是单元测试(集成测试也许?),但我猜这仍然是完美可行的使用PHPUnit?考虑到上面的例子,我想要建立一个测试,不仅可以隔离测试House,Door,Knob和HouseFactory,还可以将真实对象彼此交互的结果,也许与他们的一些功能嘲笑,如处理数据的功能。 PHPUnit是这种测试的不好选择吗?

2) Is it feasible to set up tests that rely on several (not mocked) dependencies at once? I understand this is technically not unit testing (integration testing perhaps?) but I guess it's still perfectly doable using PHPUnit? Given the example above, I would like to be able to set up a test that not only tests House, Door, Knob and HouseFactory in isolation, but also the results of the interaction of the real objects with each other, perhaps with some of their functions mocked, such as the ones which deal with data. Is PHPUnit a bad choice for this kind of tests?

提前感谢您的时间。我意识到我所做的一些假设可能不正确,因为我显然不是这个问题的专家;欢迎和赞赏。

Thanks in advance for your time. I realize some of the assumptions I'm making may not be correct, since I'm obviously not an expert on the matter; corrections are welcome and appreciated.

推荐答案

工厂就像新的关键字。您是否测试新的关键字?不,你测试你是否可以构建一个类。但是,这与工厂本身和设备的一部分是独立的,所以已经是单元测试的一部分。

The factory is just like the new keyword. Do you test the new keyword? No, you test if you can construct a class. But that's independent to the factory itself and part of the unit so already part of your unit tests.

2)被称为集成测试。你也可以用PHPUnit来实现。

2) is called integration testing. And you can do that with PHPUnit as well.


编辑 - 在评论中有一些讨论:

Edit - As there was some discussion in comments:

就单元测试而言,您可以对您的工厂进行单位测试,它的工作原理是:返回具体类型,类型或任何类型。

As far as Unit testing is concerned, you could unit-test your factory that it does for what it is for: return a concrete type, a type or any type at all.

没有任何错误,但是通常不需要返回类型的构造函数已经在单元测试之下,而且测试真的很简单,只是数据检查闻起来像集成测试。还有那些从工厂中以依赖关系(也是单元测试)类型的那些类型将使得编译/执行失败,如果不能提供依赖关系。所以工厂所做的一切都已经被测试了,甚至是从双方进行的。如果工厂没有消耗,那么你不需要测试它。

There is nothing wrong with that, however it's normally not necessary as constructors of the returned type(s) are already under unit-tests and well that test is really trivial and just data-checking which smells like integration testing. Also those types which have that type from the factory as dependency (and which are under unit-test as well) will make compilation/execution fail if the dependency can not be provided. So everything the factory is for, is already tested, even from both sides. And if the factory does not get consumed, well then you don't need to test it.

我建议你创建一个纯粹的TDD风格的工厂,制定使用,然后你会得到一个这样的感觉。您可能想要测试工厂类的其他方面,但是这可能比单元测试更适合集成。

I suggest you create once a factory purely TDD style, so to pre-formulate the use and then you'll get a feeling for this. You might want to test other aspects of your factory class(es), but probably this belongs more into integration than unit testing.

而且我不想创建印象其他单位实际上应该对工厂进行硬编码调用创建方法,而不是注入依赖关系。因为你不应该在你的单位内使用 new ,所以你也不应该使用 Factory :: create 。类似于 new ,类名( Factory )是硬编码的,而不是注入。那就是一个隐藏的依赖。但依赖不应该隐藏;但可见。

And I don't wanted to create the impression that other of your units should actually have hardcoded calls to the factory create method(s) instead of getting the dependency injected. As you should not use new inside your units, you should not use Factory::create therein either. Similar to new, the class-name (Factory) is hard-encoded, not injected. It is a hidden dependency then. But dependencies should not be hidden; but made visible.

这篇关于使用嵌套依赖关系和工厂类执行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆