加快命中真实 SQL 服务器数据库的集成测试
Speed up integration test that hits real SQL Server database
我的应用程序有一个与数据库大量对话的业务层。这一层被实现为一堆 ADO.NET
包裹着 Dapper
的调用。为了初始化数据库模式,我使用 Entity Framework 6
Code First,它的一个很好的副作用是能够将我的 C# POCO 用作 DDL 和 DAO。
我在 Dapper
中使用的原始 SQL 是普通旧 SQL、一些用户定义的函数和特定于 SQL Server
的东西的混合体,例如内置函数和全文索引。
在我的 Jenkins CI 构建中,我有一个集成测试套件可以使用 NUnit
测试这一层。为了确保测试彼此隔离,数据库初始化应该在 [SetUp]
方法上进行。
我经历了几个选项来实施集成测试:
- 使用内存数据库。优点:测试设置速度快,运行宁测试逻辑速度快。
缺点:这是一个不可选项,因为我使用的 SQL 命令特定于
SQL Server
.
- 使用
SQLite
或 SQL Compact
。优点:语法类似于SQL Server
,设置和拆除只是删除文件的问题。缺点:在我开始使用用户定义函数和全文索引的那一刻也没有选择。
- 使用本地
SQL Express
服务器只是为了测试。优点:几乎可以保证行为与生产设置相匹配,包括内置函数和全文索引。缺点:设置速度较慢且更复杂。
我选择了选项 3,方法如下:
- 在
[SetUpFixture]
的 [SetUp]
上,初始化种子数据库。初始化后,创建此数据库的备份,将其保存为文件。这只会在测试的整个命名空间上发生一次。
- 在每个
[TestFixture]
的 [SetUp]
上,恢复备份。由于每次测试 运行 之前都会发生这种情况,因此每个测试都与其他测试隔离。
缺点是 运行测试套件现在变得非常慢。步骤号上面的 2 每次测试需要 2-5 秒。最重要的是,每个测试还需要一些时间来执行测试装置本身,这比典型的单元测试要慢很多,因为它会访问真实的数据库。
在我目前的状态下,一个包含 143 个测试的中等大小的测试套件在我的构建机器上需要 13 分钟,这是一个 Azure 上的虚拟机,指定为 A3(4 核 7 GB),运行ning Windows 服务器 2008 R2。
除了向机器添加更多硬件之外,还有什么方法可以加快这个过程吗?
您可以在设置中使用事务范围并在拆卸方法中处理它:
[MyFixture]
public class MyTests
{
private string connString= "...";
private TransactionScope ts= null;
[SetUp]
public void SetUp()
{
ts = new TransactionScope(TransactionScopeOption.Required);
}
[TearDown]
public void TearDown()
{
ts.Dispose();
}
[Test]
public void MyTest()
{
// do my tests etc.
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand cmd = new SqlCommand("exec MyProcecure"))
{
cmd.Connection = conn;
conn.Open();
int retval = cmd.ExecuteNonQuery();
Assert.Greater(retval, 0);
}
}
}
}
编辑
即使被测单元有它自己的事务范围,这也会起作用。
见MSDN
Voting inside a nested scope
Although a nested scope can join the
ambient transaction of the root scope, calling Complete in the nested
scope has no affect on the root scope. Only if all the scopes from the
root scope down to the last nested scope vote to commit the
transaction, will the transaction be committed.
我的应用程序有一个与数据库大量对话的业务层。这一层被实现为一堆 ADO.NET
包裹着 Dapper
的调用。为了初始化数据库模式,我使用 Entity Framework 6
Code First,它的一个很好的副作用是能够将我的 C# POCO 用作 DDL 和 DAO。
我在 Dapper
中使用的原始 SQL 是普通旧 SQL、一些用户定义的函数和特定于 SQL Server
的东西的混合体,例如内置函数和全文索引。
在我的 Jenkins CI 构建中,我有一个集成测试套件可以使用 NUnit
测试这一层。为了确保测试彼此隔离,数据库初始化应该在 [SetUp]
方法上进行。
我经历了几个选项来实施集成测试:
- 使用内存数据库。优点:测试设置速度快,运行宁测试逻辑速度快。
缺点:这是一个不可选项,因为我使用的 SQL 命令特定于
SQL Server
. - 使用
SQLite
或SQL Compact
。优点:语法类似于SQL Server
,设置和拆除只是删除文件的问题。缺点:在我开始使用用户定义函数和全文索引的那一刻也没有选择。 - 使用本地
SQL Express
服务器只是为了测试。优点:几乎可以保证行为与生产设置相匹配,包括内置函数和全文索引。缺点:设置速度较慢且更复杂。
我选择了选项 3,方法如下:
- 在
[SetUpFixture]
的[SetUp]
上,初始化种子数据库。初始化后,创建此数据库的备份,将其保存为文件。这只会在测试的整个命名空间上发生一次。 - 在每个
[TestFixture]
的[SetUp]
上,恢复备份。由于每次测试 运行 之前都会发生这种情况,因此每个测试都与其他测试隔离。
缺点是 运行测试套件现在变得非常慢。步骤号上面的 2 每次测试需要 2-5 秒。最重要的是,每个测试还需要一些时间来执行测试装置本身,这比典型的单元测试要慢很多,因为它会访问真实的数据库。
在我目前的状态下,一个包含 143 个测试的中等大小的测试套件在我的构建机器上需要 13 分钟,这是一个 Azure 上的虚拟机,指定为 A3(4 核 7 GB),运行ning Windows 服务器 2008 R2。
除了向机器添加更多硬件之外,还有什么方法可以加快这个过程吗?
您可以在设置中使用事务范围并在拆卸方法中处理它:
[MyFixture]
public class MyTests
{
private string connString= "...";
private TransactionScope ts= null;
[SetUp]
public void SetUp()
{
ts = new TransactionScope(TransactionScopeOption.Required);
}
[TearDown]
public void TearDown()
{
ts.Dispose();
}
[Test]
public void MyTest()
{
// do my tests etc.
using (SqlConnection conn = new SqlConnection(connString))
{
using (SqlCommand cmd = new SqlCommand("exec MyProcecure"))
{
cmd.Connection = conn;
conn.Open();
int retval = cmd.ExecuteNonQuery();
Assert.Greater(retval, 0);
}
}
}
}
编辑 即使被测单元有它自己的事务范围,这也会起作用。
见MSDN
Voting inside a nested scope
Although a nested scope can join the ambient transaction of the root scope, calling Complete in the nested scope has no affect on the root scope. Only if all the scopes from the root scope down to the last nested scope vote to commit the transaction, will the transaction be committed.