The goal is simple - rollback data inserted by a unit test. Here is how it goes. In a unit test, a method is called that creates a new connection and inserts some data. After that a unit test creates a new connection and tries to find what has been inserted and assert that. I was hoping to wrap these two things with TransactionScope
, not call Complete
and see inserted data rolled back. That's not happening. Am I doing something wrong or I am just missing the point?
目标很简单——通过单元测试插入回滚数据。事情是这样的。在单元测试中,调用一个方法来创建一个新的连接并插入一些数据。之后,单元测试创建一个新的连接,并尝试查找已插入的内容并断言它。我希望用TransactionScope来包装这两个东西,而不是调用Complete,然后看到插入的数据回滚。这是没有发生。我是不是做错了什么,或者我只是忽略了一点?
using (new TransactionScope())
{
// call a method that inserts data
var target = new ....
target.DoStuffAndEndupWithDataInDb();
// Now assert what has been added.
using (var conn = new SqlConnection(connectionString))
using (var cmd = conn.CreateCommand())
{
// Just read the data from DB
cmd.CommandText = "SELECT...";
conn.Open();
int count = 0;
using (var rdr = cmd.ExecuteReader())
{
// Read records here
...
count++;
}
// Expecting, say, 3 records here
Assert.AreEqual(3, count);
}
}
EDIT: I don't think I had DTC running and configured on my machine. So I started the service and tried to configure DTC but I am getting this error.
编辑:我认为我没有在我的机器上运行和配置DTC。所以我启动了服务并尝试配置DTC,但是我得到了这个错误。
4 个解决方案
#1
1
are you using MSTest ? then you can use MsTestExtensions you unit test needs to derive from MSTestExtensionsTestFixture
and your test needs to have TestTransaction
Attribute, it uses AOP to automatically start a transaction and roll it back.
你在用MSTest吗?然后,您可以使用单元测试所需的MsTestExtensions,并且您的测试需要拥有TestTransaction属性,它使用AOP自动启动事务并回滚事务。
#2
1
I don't think you're missing the point but just attacking the problem incorrectly.
我认为你没有抓住要点,只是不正确地解决问题。
In NUnit terms, the concepts are [SetUp]
and [TearDown]
methods. You've already defined the setup method in your description and your tear down method should just undo what the setup method did (assuming what you're unit testing has no residual side effects).
在NUnit中,概念是[SetUp]和[TearDown]方法。您已经在您的描述中定义了setup方法,您的teardown方法应该只撤销setup方法所做的事情(假设您的单元测试没有残留的副作用)。
#3
1
Do you have Distributed Transaction Coordinator properly configured? This is a big gotcha when trying to use TransactionScope
like this... if it isn't configured, sometimes you'll get an error, but other times the transaction will just commit and not rollback.
您是否正确配置了分布式事务协调器?当尝试使用像这样的TransactionScope时,这是一个很大的问题。如果没有配置,有时会出现错误,但有时事务只是提交而不是回滚。
I'd recommend looking at this article, which shows you all the various steps that need to be done in order to rollback your unit tests using MSDTC.
我建议阅读本文,它向您展示了使用MSDTC回滚单元测试所需的所有步骤。
#4
0
Your code should work as you expect. How are you adding data in DoStuffAndEndupWithDataInDb()
? I'm wondering whether the data initialization is not participating in the transaction.
您的代码应该像您期望的那样工作。如何在dostuffandwithdataindb()中添加数据?我想知道数据初始化是否不参与事务。
For reference, the following console application correctly outputs 3 rows
, and does not commit the rows to the database (checked using SSMS).
为了引用,下面的控制台应用程序正确地输出了3行,并且不将行提交到数据库(使用SSMS进行检查)。
public class Program
{
private static void Main(string[] args)
{
using (var trx = new TransactionScope())
{
InitializeData();
using (var connection = new SqlConnection("server=localhost;database=Test;integrated security=true"))
using (var command = connection.CreateCommand())
{
command.CommandText = "select count(*) from MyTable";
connection.Open();
Console.WriteLine("{0} rows", command.ExecuteScalar());
}
}
Console.ReadLine();
}
private static void InitializeData()
{
using (var connection = new SqlConnection("server=localhost;database=Test;integrated security=true"))
using (var command = connection.CreateCommand())
{
command.CommandText = "insert into MyTable values (1),(2),(3)";
connection.Open();
command.ExecuteNonQuery();
}
}
}
#1
1
are you using MSTest ? then you can use MsTestExtensions you unit test needs to derive from MSTestExtensionsTestFixture
and your test needs to have TestTransaction
Attribute, it uses AOP to automatically start a transaction and roll it back.
你在用MSTest吗?然后,您可以使用单元测试所需的MsTestExtensions,并且您的测试需要拥有TestTransaction属性,它使用AOP自动启动事务并回滚事务。
#2
1
I don't think you're missing the point but just attacking the problem incorrectly.
我认为你没有抓住要点,只是不正确地解决问题。
In NUnit terms, the concepts are [SetUp]
and [TearDown]
methods. You've already defined the setup method in your description and your tear down method should just undo what the setup method did (assuming what you're unit testing has no residual side effects).
在NUnit中,概念是[SetUp]和[TearDown]方法。您已经在您的描述中定义了setup方法,您的teardown方法应该只撤销setup方法所做的事情(假设您的单元测试没有残留的副作用)。
#3
1
Do you have Distributed Transaction Coordinator properly configured? This is a big gotcha when trying to use TransactionScope
like this... if it isn't configured, sometimes you'll get an error, but other times the transaction will just commit and not rollback.
您是否正确配置了分布式事务协调器?当尝试使用像这样的TransactionScope时,这是一个很大的问题。如果没有配置,有时会出现错误,但有时事务只是提交而不是回滚。
I'd recommend looking at this article, which shows you all the various steps that need to be done in order to rollback your unit tests using MSDTC.
我建议阅读本文,它向您展示了使用MSDTC回滚单元测试所需的所有步骤。
#4
0
Your code should work as you expect. How are you adding data in DoStuffAndEndupWithDataInDb()
? I'm wondering whether the data initialization is not participating in the transaction.
您的代码应该像您期望的那样工作。如何在dostuffandwithdataindb()中添加数据?我想知道数据初始化是否不参与事务。
For reference, the following console application correctly outputs 3 rows
, and does not commit the rows to the database (checked using SSMS).
为了引用,下面的控制台应用程序正确地输出了3行,并且不将行提交到数据库(使用SSMS进行检查)。
public class Program
{
private static void Main(string[] args)
{
using (var trx = new TransactionScope())
{
InitializeData();
using (var connection = new SqlConnection("server=localhost;database=Test;integrated security=true"))
using (var command = connection.CreateCommand())
{
command.CommandText = "select count(*) from MyTable";
connection.Open();
Console.WriteLine("{0} rows", command.ExecuteScalar());
}
}
Console.ReadLine();
}
private static void InitializeData()
{
using (var connection = new SqlConnection("server=localhost;database=Test;integrated security=true"))
using (var command = connection.CreateCommand())
{
command.CommandText = "insert into MyTable values (1),(2),(3)";
connection.Open();
command.ExecuteNonQuery();
}
}
}