使用 DI 进行单元测试
我有一个关于依赖注入的问题。到目前为止,我一直保持简单,我的方法基本上是在对象内分解对象创建并将其传递到构造函数中。我已经到了攻击需要多个对象的更大类的地步。有些甚至拥有包含其他对象的对象,到处都有快乐的小单身人士。当测试这些类时,它会很快变得丑陋,因为它们远非“隔离”,它们仍然被硬编码到它们的依赖项。
所以。为一个简单的类注入一个或两个对象很简单,
我研究了依赖容器,看到了许多实现,现在想知道使用容器与注册表相比有什么优势。难道不能轻松地使用注册表来保存匿名函数,以便在调用时创建所需的依赖项吗?
我查看的 2 个容器, Php Dependency 和 Pimple 在实现上有很大不同。
我想知道使用容器与传递直接对象相比的优势。我无法理解如何测试 php-dependency 的实现,即如何在 phpunit 中实现模拟数据库对象而不在测试时注入实际的类?在这样的文档标签中映射和使用依赖项有好处吗?
Class Book {
private $_database;
/**
* @PdInject database
*/
public function setDatabase($database) {
$this->_database = $database;
}
}
另一方面,粉刺采取了完全不同的方法。没有文档块标签,没有单独文件中的映射,看起来像是某种增强的注册表......
Objects are defined by anonymous functions that return an instance of the object:
// define some parameters
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';
可以同时充当工厂:
$container['session'] = function ($c) {
return new Session($c['session_storage']);
};
声明共享资源始终服务于同一个实例(单例!?):
$c['session'] = $c->share(function ($c) {
return new Session($c['session_storage']);
});
这是我想到使用一个简单的注册表来保存对象或匿名函数的想法。但是我在这种方法中遗漏了一些东西吗? Pimple,我可以看到如何测试,但从测试的角度来看,Php-Dependency 对我来说不清楚。
I have a question concerning dependency injection. I have been keeping it simple so far, my methodology is basically to factor out object creation within objects and passing it instead in the constructer. I have come to a point where I am attacking larger classes that require multiple oblects. Some even have objects that contain other objects, with merry little singletons here and there. It gets ugly fast when testing these classes, as they are far from 'isolated' they are still hard-coded to their dependencies.
So. Injecting an object or 2 for a trivial class is straightforward,
I have looked into dependency containers, saw many implementations and am now wondering what is the advantage of using container vs. a registry for example. Couldn't one just as easily use a registry to hold anonymous functions that create the needed dependencies when called upon?
The 2 containers I peeked into, Php Dependency and Pimple differ greatly in the implementation.
I am wondering on the advantages of user a container vs. passing straight objects. I fail to understand how php-dependency's implementation would be tested, ie how would one implement the mock database object in phpunit without the actual class being injected when tested? Is there advantage to having dependency mapped out and used in doctags like this?
Class Book {
private $_database;
/**
* @PdInject database
*/
public function setDatabase($database) {
$this->_database = $database;
}
}
Pimple, on the other hand takes a totally different approach. No docblock tags, no mapping in seperate file, it seems like some kind of souped up registry ....
Objects are defined by anonymous functions that return an instance of the object:
// define some parameters
$container['cookie_name'] = 'SESSION_ID';
$container['session_storage_class'] = 'SessionStorage';
... that can behave as a factory at same time:
$container['session'] = function ($c) {
return new Session($c['session_storage']);
};
Declaring shared ressources always serves the same instance (singleton!?):
$c['session'] = $c->share(function ($c) {
return new Session($c['session_storage']);
});
This is were I got the idea of using a simple registry that holds either objects or anonymous functions. BUt am I missing something in this approach? Pimple, I can see to how to test, but Php-Dependency is unclear to me from a testing point of view.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
通常在我们的应用程序中,我们进行构造函数注入并为系统中的所有组件定义一个接口:
当然,我们有一个标准的 Db_Adapter ,然后是另一个 Db_TestAdapter 。在 Db_TestAdapter 中,我们可以定义测试中 SQL 查询的结果。
对于我们的普通应用程序,我们的容器有这样的东西:
然后在我们的测试中,我们有这一行:
要获取 Book 的实例,我们只需向容器询问它:
然后容器注入所有需要的依赖项进入物体。
如果您保持所有对象松散耦合,即对象 A 仅需要一个接口 B,那么您始终可以为对象 A 提供测试实现。那时您使用什么容器并不重要。
我们有一个非常简单的 IoC 容器,可以执行基本的构造函数注入。我们的测试继承自一个基类,该基类用标准测试对象填充容器。这样我们就不需要大量代码来构造我们想要测试的对象。
更新:
我添加了一个在容器中连接事物的示例。
Normally in our apps, we do constructor injection and define an interface for all components in our system:
We have then of course a standard Db_Adapter and then another Db_TestAdapter. In Db_TestAdapter we can define results of SQL queries in our tests.
For our normal app, we have something like this for our container:
And then in our tests, we have this line instead:
To get an instance of a Book, we simply ask the container for it:
And the container injects all the needed dependencies into the object.
If you keep all your objects loosely coupled, ie object A only needs an interface B then you can always provide object A with a test implementation. At that point what container you use doesn't matter.
We have a very simple IoC container which can do basic constructor injection. Our tests inherit from a base class which fills the container with standard test objects. That way we don't have a lot of code just to construct an object we want to test.
Update:
I added an example of wiring things up in the container.