我有至少 2 个其他类使用的存储库类。 该存储库类需要初始化 - 这成本很高(查询数据库)。 现在,我可以在需要的地方创建单独的存储库实例。 问题是,每次我创建存储库时都必须对其进行初始化。 如何设计这样的存储库以使其对 TDD 友好? 我首先想到的是 Singleton,但它不是解决方案。
I have repository class that is used by at least 2 other classes. This repository class needs to be initialized - which is high in cost (querying database). Now, I create separate instances of repository wherever I need it. The thing is, that everytime I create repository it has to be initialized. How to design such repository to be TDD-friendly? The first thing in my mind was Singleton but it's not the solution.
发布评论
评论(5)
我希望 TDD 友好是指“可测试”代码。 对于 Singleton ObjectX,我认为最常见的方法是将“控制创建”的责任分割(SRP)给另一个类,以便 ObjectX 完成它应该做的所有事情。
然后,您有另一个类 ObjectXFactory 或 Host 或任何您想要的名称,负责为所有客户端提供单个实例(并在需要时提供线程同步等),
您只需要教育并遵守团队约定,即 ObjectX 构造函数是不被调用 - 始终使用 ObjectXFactory.CreateInstance()。 (如果您发现自己有意识/纪律问题,请通过偷偷摸摸的 InternalsVisibleToAttribute)
华泰
I hope by TDD-friendly you mean 'testable' code. For a Singleton ObjectX, I think the most common way is to split the responsibility (SRP) of 'controlling creation' to another class so ObjectX does all the things it is supposed to do.
Then you have another class ObjectXFactory or Host or whatever you wanna call it that is responsible for providing a single instance for all clients (and providing thread sync if needed and so on)
You just need to educate and conform to a Team convention that ObjectX constructor is not to be called - always use ObjectXFactory.CreateInstance(). (If you find that you have a awareness/discipline problem, mark ObjectX's ctor as internal and visible to only to the test assembly via the sneaky InternalsVisibleToAttribute)
HTH
TDD 部分的答案之一是学习模拟。
查看 Stephen Walther 撰写的这篇优秀文章:
http://stephenwalther.com/blog/archive/2008/03/23/tdd-introduction-to-rhino-mocks.aspx
One answer for the TDD part is learn mocking.
Check out this excellent article by Stephen Walther:
http://stephenwalther.com/blog/archive/2008/03/23/tdd-introduction-to-rhino-mocks.aspx
您使用任何类型的 IOC 容器吗? Unity 是我选择的容器,它包含一个 ContainerControledLifetimeManager 使您的类成为单例,但不由您自己管理。
Do you use any type of IOC container? Unity is my container of choice, and it contains a ContainerControledLifetimeManager which makes your class a singleton, but not managed by yourself.
在考虑单例之前,请考虑缓存实例以提高性能。 但对于 TDD 友好的设计,请考虑策略注入,以便可以删除“慢”位进行测试并用存根和模拟替换。 如果可以的话,尽量不要在测试中进行数据库调用。
Consider caching instances for performance improvement before you consider singletons. But for TDD friendly designs consider strategy injection so that 'slow' bits can be removed for testing and replaced with stubs and mocks. Try not to do db calls in tests if you can.
你不能那样做——至少在真正的 TDD 意义上不能这样做。
依赖 Unity 等 DI/IoC 策略意味着您的测试依赖于外部组件,而不是孤立进行测试。
然后测试就变成了集成测试,而不是单元测试。
==忽略下面的答案==
我想您想知道如何使存储库可测试。
为它引入一个接口将允许您模拟或存根它,这反过来将确保您可以独立于存储库的任何具体实现来测试您的对象。
我将使用 Rhino Mocks 3.5 for .NET 3.5 来说明这一点
: Repository 中的一个接口,我们称之为 IRepository
现在,由于您需要对两个不同的对象使用 IRepository,那么我们就使用泛型,这样您就可以用它实例化您的存储库:
当然,这意味着您将有某种 find 方法:
其中您的条件对象是一些允许您设置要查找的对象的对象,例如,您的 where 子句。
现在,你有了你的对象:
你想要测试
SomeObject
,这样 FindAll() 将返回一组预期的结果——这就是 Rhino Mocks 的用武之地:请注意,上面的代码不是TDD 在各个方面都是最佳实践,但它只是一个起点。
这里的关键概念是引入接口以允许对象的松散耦合,特别是当对象倾向于执行访问数据库、文件系统等操作时。
Ben Hall 关于 Rhino Mocks 的文章。
You can't do that -- at least not in a true TDD sense.
Relying on DI/IoC strategies such as Unity means your tests are dependent on an external component and are not tested in isolation.
The tests then become integration tests, not unit tests.
==Ignore the answer below here==
I guess you wanted to know how to make Repository testable.
Introducing an interface for it would allow you to mock or stub it, which will in turn make sure that you can test your objects independent of any concrete implementation of Repository.
I'll illustrate this using Rhino Mocks 3.5 for .NET 3.5:
Let's make an interface out of Repository, let's call that
IRepository
Now, since you need to use IRepository for two different objects, then let's just use generics so you can instantiate your repository with that:
of course that would mean that you would have some sort of find method:
where your criteria object is some object that allows you to set what to look for, e.g., your where clause.
Now, you have your object:
You want to to test
SomeObject
such that FindAll() will return an expected set of results -- this is where Rhino Mocks would come in:Note that the code above is not TDD best practice in all respects, but it's a place to start.
Key concept here is introducing interfaces to allow for loose coupling of objects, especially when the object tends to do things like access databases, file systems, etc.
There is a more comprehensive example and better examples on Ben Hall's article on Rhino Mocks.