简体   繁体   English

单元测试数据库驱动的.NET应用程序

[英]Unit Testing Database Driven .NET Applications

What is the best way to unit test heavily database dependant .NET middleware? 对大量依赖数据库的.NET中间件进行单元测试的最佳方法是什么? Eg a process which reads data from multiple databases, manipulates it, and then combines and writes it to other databases? 例如,从多个数据库读取数据,操作它,然后将其组合并写入其他数据库的过程?

Should the databases be filled with static data which is somehow reset on each unit test run? 数据库是否应填充静态数据,这些数据在每次单元测试运行时都会重置? Should the whole SQL Server access be somehow mocked? 是否应该以某种方式模拟整个SQL Server访问? Is it not feasible to unit test such an application in the real world? 在现实世界中对这样的应用进行单元测试是否不可行?

The answer is mocking 答案是嘲笑

However the way to do this I have found is as follows. 然而,我发现的方法如下。

Separate the DAL into 2 layers. 将DAL分成2层。 The bottom simply performs atomic read and writes to the databases - these objects all implement a set of interfaces IReadFromDB and IWriteToDB. 底部只是执行原子读取和写入数据库 - 这些对象都实现了一组接口IReadFromDB和IWriteToDB。

Then you can create your read and write business logic in a higher DAL level but rather than reference the objects that will read and write to the database reference the interfaces and use properties to be able to substitute functionality. 然后,您可以在更高的DAL级别创建读写业务逻辑,而不是引用将读取和写入数据库的对象引用接口并使用属性来替换功能。 I tend to include the desired functional objects in the constructors so that things work 'out of the box' so to speak. 我倾向于在构造函数中包含所需的功能对象,以便可以说“开箱即用”。

This will make it a cinch to 'swap out' the functionality and so to unit test the business logic. 这将使得“交换”功能变得简单,从而对业务逻辑进行单元测试。

As for testing the DB read and writes ... I haven't found a way that doesn't involve work. 至于测试数据库读写......我还没有找到一种不涉及工作的方法。 I usually use a different connection string to a copy of the database and then write data generation and cleanup code for unit tests to leave the state of the db the same as before and after. 我通常使用不同的连接字符串来复制数据库,然后为单元测试编写数据生成和清理代码,使db的状态与之前和之后相同。

Yes, its time consuming ... however it doesn't risk alienating a client. 是的,耗费时间......但是不会有疏远客户的风险。 It depends on your priorities. 这取决于你的优先事项。

Someone else mentioned performance testing. 其他人提到了性能测试。 I would not consider this to be part of a unit test. 我不认为这是单元测试的一部分。 I usually do this with a test harness in combination with debug code simply because performance of small parts is often misleading - when you move to the big picture the parts that are actually casing problems are often not the parts that localised testing would flag in my experience. 我通常将测试工具与调试代码结合使用只是因为小部件的性能通常会产生误导 - 当您转向大图时,实际上存在外壳问题的部件通常不是本地化测试在我的体验中标记的部分。

I think the best practice is really to Mock all access to database - just return some predetermined static data on call - you don't need to test how databases work - your need to test your unit behavior. 我认为最好的做法是模拟对数据库的所有访问 - 只需在调用时返回一些预定的静态数据 - 您不需要测试数据库的工作方式 - 您需要测试单元行为。 Lesser your unit test interacts with outside - better it is. 较小的单元测试与外部交互 - 更好。

With mocking your will be able to check that call to database is valid without really invoking it, so it's what you need i think. 通过模拟,您可以检查对数据库的调用是否有效而无需真正调用它,所以这就是您需要的。

I'd recommend mocking the data access level. 我建议模拟数据访问级别。 The benefit of using mocks in this instance include: 1) unit tests will run quicker. 在这种情况下使用模拟的好处包括:1)单元测试运行得更快。 If they have to do the full work of connecting to the db, extracting data etc. then the cost will become expensive. 如果他们必须完成连接数据库,提取数据等的全部工作,那么成本将变得昂贵。 Expensive tests = people stop running them/start losing faith in them! 昂贵的测试=人们停止运行它们/开始对它们失去信心! 2) You can test a vast range of scenarios without having to go through the hassle of setting up suitable test/static data that you have to ensure is always in the DB before the tests start. 2)您可以测试各种各样的场景,而无需经历设置合适的测试/静态数据的麻烦,您必须确保在测试开始之前始终在DB中。 3) taking away the external db system from the equation means you are testing just the .NET code you want to test. 3)从等式中删除外部数据库系统意味着您只测试要测试的.NET代码。 No external dependency. 没有外部依赖。

You could either have a data access layer that does pure db interaction, and then mock that. 您可以拥有一个执行纯数据库交互的数据访问层,然后模拟它。 Or, use mock SqlCommands etc in your .NET code. 或者,在.NET代码中使用mock SqlCommands等。

I think you should separate your concerns into different kinds of tests. 我认为你应该将你的顾虑分成不同类型的测试。

  • Write unit tests for the data manipulation logic implemented in .NET. 为.NET中实现的数据操作逻辑编写单元测试。 Either mock the databases or completly separate the data massaging logic from the data access stuff, and just feed your data manipulation routine with pre-baked input data. 模拟数据库或完全将数据按摩逻辑与数据访问内容分开,然后使用预先烘焙的输入数据提供数据操作例程。
  • Create an integration test that exercises the whole data flow. 创建一个运行整个数据流的集成测试。 This test should also be automated. 该测试也应该是自动化的。 Set up the source and destination database(s) (either from DB script or from DB backup) to a well-known state, run your data manipulation logic, then check the results. 将源数据库和目标数据库(从DB脚本或从DB备份)设置为已知状态,运行数据操作逻辑,然后检查结果。
  • You may also want to create performance and stress tests if you deal with lots of data. 如果处理大量数据,您可能还需要创建性能和压力测试。 You can set up a database(s) the same way you did in the integration test, generate a bunch of test data, run your component and check either run times, or that it completes at all (eg. no concurency problems, deadlocks, etc.) 您可以像在集成测试中那样设置数据库,生成一堆测试数据,运行组件并检查运行时间,或者它完全完成(例如,没有可靠性问题,死锁,等等。)

I have two methods for unit testing code which has database dependancies: 我有两种单元测试代码的方法,它具有数据库依赖性:

  • Mock objects ie TypeMock 模拟对象即TypeMock
  • Custom SQL scripts running in the TestInitialise() and TestCleanup() subs. 在TestInitialise()和TestCleanup()子系统中运行的自定义SQL脚本。

With TypeMock, I can fake database data coming into my code, which helps me run my tests without having to worry about cleaning up the database, or restoring backups etc. I use mocking for testing code outside of the database logic, but still requires fake data from the database. 使用TypeMock,我可以伪造进入我的代码的数据库数据,这可以帮助我运行我的测试,而不必担心清理数据库,或恢复备份等。我使用mocking来测试数据库逻辑之外的代码,但仍然需要假的来自数据库的数据。

With custom SQL scripts, I can run SQL commands on the TestInitialise() sub and, when the unit test finishes, I have cleanup SQL run on the TestCleanup() sub. 使用自定义SQL脚本,我可以在TestInitialise()子上运行SQL命令,并且当单元测试完成时,我在TestCleanup()子上运行清理SQL。 I use this method for testing code where the database logic is more complicated. 我使用这种方法来测试数据库逻辑更复杂的代码。 For example, I might need to insert some rows into tables which will help with testing the code. 例如,我可能需要在表中插入一些行来帮助测试代码。 Then on the TestCleanup() I will have my delete SQL commands which remove all the records that were inserted via the TestInitialise() sub. 然后在TestCleanup()上,我将删除SQL命令,删除通过TestInitialise()子插入的所有记录。

You should try to layer your code so that you can mock the operation of your functions. 您应该尝试对代码进行分层,以便模拟函数的操作。

The layer that does actually need to talk to the database could use a system of setting up the data and working with it within a transaction, which you would rollback at the end of the test. 实际需要与数据库通信的层可以使用一个系统来设置数据并在事务中使用它,您可以在测试结束时回滚。

I would abstract the data access layer and then mock it. 我会抽象数据访问层,然后模拟它。 With mocking you will be able to increase the code coverage of your tests. 通过模拟,您将能够增加测试的代码覆盖率。 It will also prevent the idea of ' Test Cancer ' that will happen by doing expensive calls to network/filesystem/database because people will stop running them. 它还会通过对网络/文件系统/数据库进行昂贵的调用来阻止“ 测试癌症 ”的想法,因为人们将停止运行它们。

You should have a few tests that call to the database but they should be limited as much as possible. 您应该有一些调用数据库的测试,但应尽可能地限制它们。

In my work we use Nunit and Typemock for our Unit testing. 在我的工作中,我们使用Nunit和Typemock进行单元测试。 The fact that we don't need to change our code for the tests is a big plus. 我们不需要为测试更改代码这一事实是一大优点。 Mocking the DAL is the way to go. 模拟DAL是要走的路。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM