我正在为使用数据库的应用程序编写单元测试,我希望能够针对某些示例/测试数据运行该应用程序 - 但我不确定设置该数据库的最佳方法测试的初始测试数据。
我正在寻找的是一种在测试时针对我目前使用的相同数据库(或示意图相同)运行待测代码的方法 - 在每次测试之前,我想确保数据库在插入测试数据之前,将重置为干净的平板。
我意识到使用IRepository模式可以让我去除针对实际数据库的测试的复杂性,但我不确定在我的情况下是否可行。
任何可以指出我正确方向的建议或文章?
谢谢!
- 编辑 -
谢谢大家,这些都是很棒的建议!我可能会去模拟我的数据访问层,结合一些简单的设置类来准确生成每次测试所需的数据。
答案 0 :(得分:14)
这是我尝试使用的一般方法。我设想了大约三到四个级别的测试::单元测试,交互测试,集成测试,验收测试。
在单元测试级别,它只是代码。任何数据库交互都是模拟的,可以手动或使用其中一个流行的框架,因此加载数据不是问题。它们运行速度很快,并确保对象按预期工作。这允许非常快速的写测试/写代码/运行测试周期。模拟对象提供每个测试所需的数据。
交互测试测试非平凡类交互的交互。同样,不需要数据库,它被嘲笑了。
现在处于集成级别,我正在测试组件的集成,这就是真正的数据库,队列,服务,yada yada被抛入的地方。如果可以的话,我将使用一个流行的内存数据库,所以初始化不是问题。它始终是空的,我使用实用程序类来清理数据库并在每次测试之前加载我想要的数据,这样测试之间就没有耦合。
我使用内存数据库遇到的问题是它们通常不支持我需要的所有功能。例如,我可能需要外连接,内存数据库不支持。在这种情况下,我通常会在本地传统数据库(如MySQL)上进行测试,再次在每次测试之前擦除它。由于应用程序在单独的环境中部署到生产环境,因此测试周期不会触及该数据。
答案 1 :(得分:3)
我发现处理此问题的最佳方法是使用具有已知数据的静态测试数据库,并使用事务来确保您的测试不会更改任何内容。
在您的测试设置中,您将启动一个事务,并且在您的测试清理中,您将回滚事务。这使您可以修改测试中的数据,但也可以确保在测试完成时所有内容都恢复到原始状态。
答案 2 :(得分:1)
我知道你正在使用C#,但在Java World中有Spring框架。它允许您在事务中运行数据库微型操作,在此事务之后,您可以回滚它。这意味着您可以在测试完成后不触及状态而对真实数据库进行操作。也许这可能是对C#进一步调查的暗示。
答案 3 :(得分:1)
模拟是对代码进行单元测试的最佳方式。
就集成测试而言,我在使用像SQLite这样的内存数据库时遇到了一些问题,主要是因为行为和/或语法方面的差异很小。
我一直在使用MySql的本地实例进行多个项目的集成测试。返回的问题是服务器设置和测试数据的创建。 我创建了一个名为Mysql.Server的小型Nuget包(请参阅https://github.com/stumpdk/MySql.Server中的更多内容),每次运行测试时都会设置MySql的本地实例。
运行此实例后,您可以轻松地为测试设置表结构和示例数据,而无需关注生产环境或本地服务器设置。
答案 4 :(得分:0)
我认为没有一种简单的方法可以完成这项工作。您只需创建那些预测试sql安装脚本和测试后的Tear-down脚本。然后,您需要为每次运行触发这些脚本。很多人建议使用SQLLite进行单元测试设置。
答案 5 :(得分:0)
我发现最好让我的测试进入不同的数据库,这样我就可以将其擦干净并输入我想要测试的数据。
您可能希望将数据库设置为可在程序中设置的内容,然后您的测试可以告诉类更改数据库。
答案 6 :(得分:0)
此代码清除MS SQL Server中所有用户表中的所有数据:
private DateTime _timeout;
public void ClearDatabase(SqlConnection connection)
{
_timeout = DateTime.Now + TimeSpan.FromSeconds(30);
do
{
SqlCommand command = connection.CreateCommand();
command.CommandText = "exec sp_MSforeachtable 'DELETE FROM ?'";
try
{
command.ExecuteNonQuery();
return;
}
catch (SqlException)
{
}
} while (!TimeOut());
if (TimeOut())
Assert.Fail("Fail to clear DB");
}
private bool TimeOut()
{
return DateTime.Now > _timeout;
}