如何对用于与数据通信的类进行单元测试?
我有一些存储库类,用于与不同类型的数据进行通信,这些类派生自 IRepository 接口。
在实现中,代码与数据源通信,可以是 XML 文件的目录、数据库,甚至只是缓存。是否可以可靠地对这些实现进行单元测试?我没有看到模拟实现起作用,因为那样我只是测试模拟代码而不是实际代码。
I have a few repository classes that are meant to talk to different kinds of data, deriving from an IRepository
interface.
In implementations, the code talks to a data source, be this a directory of XML files or a database or even just a cache. Is it possible to reliably unit test any of these implementations? I don't see a mock implementation working, because then I'm only testing the mock code and not the actual code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不,当您编写一个使用
IRepository
的类时,您会使用模拟。对于IRepository
的实现,您需要针对适当的数据源进行测试。对于数据库来说,这有点痛苦——对于文件系统来说,情况稍微好一些。在可能的情况下,如果您可以用流或读取器来表达您的实现,您将使您的生活变得更轻松:对实现的这些部分的测试可以针对内存中的数据源或来自测试程序集中的资源的流。当然,您可能需要一些针对真实数据库或文件系统的测试,但希望更少。
是否将此类测试称为“单元”测试取决于您如何定义单元测试;就我个人而言,我不太关心所涉及的名称,但我确实关心进行测试。特别是对于数据库,这些可能会有些痛苦(特别是如果您希望能够并行运行测试) - 但根据我的经验,它们也可能非常有价值。
No, you'd use a mock when you were writing a class which uses an
IRepository
. For the implementations ofIRepository
, you'd need to test against the appropriate data source. For databases, that's a bit of a pain - for a file system, slightly less so.Where possible, if you can express your implementation in terms of streams or readers, you will make your life easier: tests for those parts of the implementation can go against in-memory data sources, or streams from resources in the test assembly. Of course you'll probably need some tests which go to a real database or the file system, but hopefully fewer.
Whether you'd call such tests "unit" tests or not is a matter of how you define unit tests; personally I don't care too much about the names involved, but I do care about having tests. For databases in particular, these can be somewhat painful (especially if you want to be able to run tests in parallel) - but they can also be incredibly valuable, in my experience.
我认为如果您正在测试实际持久存在或查询数据的代码,您可能实际上想要访问数据库。
这些是集成测试而不是单元测试。
您可以设置一个测试数据库,在其中了解数据的状态,并针对该数据库运行测试。您可能还想告诉测试它们与您的单元测试不同,并且不需要在每次签入时运行(在 nUnit 中,您可以用一个属性来装饰您的测试类,告诉它不要运行)
I think if you are testing code that actually persists or queries data, you probably actually want to hit a database.
These are integration tests rather than unit tests.
You could set up a test database, in which you know the state of the data, and run the tests against this. You probably also want to tell the tests that they are different from your unit tests and do not need to be run on every check in (in nUnit you can decorate your test class with an attribute telling it not to run)
一般来说,您不会单元测试任何其唯一目的是与数据源通信的代码。您可能仍然希望自动测试存储库,但根据定义,此类测试将是集成测试。您可能不想将这些测试作为“第一遍”构建的一部分来运行,因为例如设置数据库并在您自己之后进行清理可能会花费相当多的时间。
如果您的存储库还有其他职责(例如实现工作单元模式),那么您可能需要单独对它们进行单元测试。
Broadly speaking, you won't unit test any code whose only purpose is to talk to a data source. You may still want to automatically test the repository, but such a test will be an integration test by definition. You probably don't want to run those tests as part of your "first pass" builds as e.g. setting up the database and cleaning up after yourself can take a non-insignificant amount of time.
If your repository has other responsibilities (e.g. implementing the Unit of Work pattern), then you may want to unit test those separately.
在 IRepository 实现的某个时刻,您将使用第三方 API,该 API 将实际从数据库/文件/xml 读取/写入。您想要做的是模拟这些 API,以确保您的代码以正确的顺序调用正确的 API。
因此,如果您正在从数据库中读取数据,则可以模拟 SqlConnection 和 SqlCommand 并确保在这些类上调用正确的方法。如果您正在写入流,则可以模拟该流并确保刷新它并处理它(例如)。
At some point in the implementation of the IRepository you will use a third party API that will actually read/write to/from database/file/xml. What you want to do is to mock those APIs to make sure your code calls the right API in the right order.
So if you are reading from the database you can mock SqlConnection and SqlCommand and make sure you call the right methods on those classes. If you are writing to a stream, you can mock the stream and make sure you Flush it and Dispose it (for example).