单元测试通过或失败结果不一致

发布于 2024-11-07 17:52:50 字数 2541 浏览 0 评论 0原文

我的一个单元测试在运行时似乎随机通过或失败。对于我来说,为什么会发生这种情况唯一有意义的是,如果每次运行测试时数据库中的数据都进入不同的状态,但我在每次测试中使用事务回滚数据库 - 除非它无法正常工作。这是我的基本单元测试类和有问题的单元测试类。你能看到我可能缺少的东西或者我还应该寻找什么吗?

TestDriven.Net 和 Visual Studio 单元测试框架会发生这种情况。

Partial Public MustInherit Class TestBase

    Private _scope As Transactions.TransactionScope

    <DebuggerStepThrough()> _
    <TestInitialize()> _
    Public Sub Setup()

            //'Start the Distribution Transaction Coordinator, if it's not already running.
            Using dtcService As New System.ServiceProcess.ServiceController("Distributed Transaction Coordinator", My.Computer.Name)
                    If dtcService.Status = ServiceProcess.ServiceControllerStatus.Stopped Then
                            dtcService.Start()
                    End If
            End Using

            _scope = New TransactionScope(TransactionScopeOption.RequiresNew, New TimeSpan(0))
    End Sub

    <DebuggerStepThrough()> _
    <TestCleanup()>
    Public Sub Teardown()
        If _scope IsNot Nothing Then
            _scope.Dispose()
        End If
    End Sub

    <System.Diagnostics.DebuggerStepThrough()> _
    Public Shared Function ExecSql(ByVal sql As String) As System.Data.DataTable

        Dim connStr = GlobalSettings.GetConnectionString()
        Using conn As New System.Data.SqlClient.SqlConnection(connStr)
            conn.Open()

            Dim cmd As New System.Data.SqlClient.SqlCommand(sql.ToString, conn)
            Dim adapter As System.Data.SqlClient.SqlDataAdapter = New System.Data.SqlClient.SqlDataAdapter(cmd)
            Dim dt As System.Data.DataTable = New System.Data.DataTable
            adapter.Fill(dt)

            Return dt

            If conn.State <> System.Data.ConnectionState.Closed Then
                conn.Close()
            End If
            conn.Dispose()
        End Using
    End Function
End Class

这是我的单元测试:

Partial Public Class CampaignEmailSendLimitServiceTests
    Inherits TestBase

    Private _service = ServiceManager.Current.MyService

    <TestMethod()> _
    Public Sub MyTest()
        //' Set the pre-test condition.
        ExecSql("update base.dbo.tblTableA set someDate = '2010-06-28' where id = 56937 ")

        //' Run the service
        _service.Process()

       //' Verify the expected result
       Dim dt = ExecSql("select deliveryDate from tblTableB ")
       Assert.AreEqual(False, dt.Rows(0).IsNull("deliveryDate"))

    End Sub
End Class

One of my unit tests seems to randomly pass or fail when I run it. The only thing that makes sense to me for why this is happening is if the data in the database is getting into a different state each time the test is ran, but I use transactions to rollback the database in each test - unless it's not working right. Here's my base unit test class and the unit test class that's having the problem. Can you see anything I might be missing or what else I should look for?

This happens with TestDriven.Net and the Visual Studio Unit Test Framework.

Partial Public MustInherit Class TestBase

    Private _scope As Transactions.TransactionScope

    <DebuggerStepThrough()> _
    <TestInitialize()> _
    Public Sub Setup()

            //'Start the Distribution Transaction Coordinator, if it's not already running.
            Using dtcService As New System.ServiceProcess.ServiceController("Distributed Transaction Coordinator", My.Computer.Name)
                    If dtcService.Status = ServiceProcess.ServiceControllerStatus.Stopped Then
                            dtcService.Start()
                    End If
            End Using

            _scope = New TransactionScope(TransactionScopeOption.RequiresNew, New TimeSpan(0))
    End Sub

    <DebuggerStepThrough()> _
    <TestCleanup()>
    Public Sub Teardown()
        If _scope IsNot Nothing Then
            _scope.Dispose()
        End If
    End Sub

    <System.Diagnostics.DebuggerStepThrough()> _
    Public Shared Function ExecSql(ByVal sql As String) As System.Data.DataTable

        Dim connStr = GlobalSettings.GetConnectionString()
        Using conn As New System.Data.SqlClient.SqlConnection(connStr)
            conn.Open()

            Dim cmd As New System.Data.SqlClient.SqlCommand(sql.ToString, conn)
            Dim adapter As System.Data.SqlClient.SqlDataAdapter = New System.Data.SqlClient.SqlDataAdapter(cmd)
            Dim dt As System.Data.DataTable = New System.Data.DataTable
            adapter.Fill(dt)

            Return dt

            If conn.State <> System.Data.ConnectionState.Closed Then
                conn.Close()
            End If
            conn.Dispose()
        End Using
    End Function
End Class

And here's my unit test:

Partial Public Class CampaignEmailSendLimitServiceTests
    Inherits TestBase

    Private _service = ServiceManager.Current.MyService

    <TestMethod()> _
    Public Sub MyTest()
        //' Set the pre-test condition.
        ExecSql("update base.dbo.tblTableA set someDate = '2010-06-28' where id = 56937 ")

        //' Run the service
        _service.Process()

       //' Verify the expected result
       Dim dt = ExecSql("select deliveryDate from tblTableB ")
       Assert.AreEqual(False, dt.Rows(0).IsNull("deliveryDate"))

    End Sub
End Class

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

清风夜微凉 2024-11-14 17:52:50

这对我来说一直很有效。我看到的主要区别是使用 TransactionScopeOption.Required。

[TestClass()]
public class MyTest: DatabaseTestClass
{

    public MyTest()
    {
        InitializeComponent();
    }

    TransactionScope ambientTransaction;

    [TestInitialize()]
    public void TestInitialize()
    {
        ambientTransaction = new TransactionScope(TransactionScopeOption.Required);
        base.InitializeTest();
    }

    [TestCleanup()]
    public void TestCleanup()
    {
        ambientTransaction.Dispose();
        base.CleanupTest();
    }
}

This has always worked for fine for me. The main difference I see is using TransactionScopeOption.Required.

[TestClass()]
public class MyTest: DatabaseTestClass
{

    public MyTest()
    {
        InitializeComponent();
    }

    TransactionScope ambientTransaction;

    [TestInitialize()]
    public void TestInitialize()
    {
        ambientTransaction = new TransactionScope(TransactionScopeOption.Required);
        base.InitializeTest();
    }

    [TestCleanup()]
    public void TestCleanup()
    {
        ambientTransaction.Dispose();
        base.CleanupTest();
    }
}
偏爱自由 2024-11-14 17:52:50

为什么使用 DataAdapter.Fill 来执行更新?它旨在用 select 语句填充 DataTables。

我的猜测是,您首先没有使用 ExecSql 向数据库写入任何内容。

第二件事。该断言尽可能不可读。
我会改为

Assert.AreEqual(False, dt.Rows(0).IsNull("deliveryDate"));

Assert.IsFalse(dt.Rows(0).IsNull("deliveryDate"));

不可读

Assert.That(dt.Rows(0)("deliveryDate"), Is.Not.Null));

的测试是有些人说单元测试不好的原因之一,因为它会减慢你的速度。您必须使单元测试尽可能易于阅读和理解。这样他们就不会反对单元测试;)

可能会有一些拼写错误,因为我不使用 VB.NET。断言示例来自 NUnit。

Why are You using DataAdapter.Fill to execute updates? It is designed to fill DataTables with select statements.

My guess is that You didn't write anything to database using ExecSql in the first place.

And second thing. That assert is as unreadable as possible.
I would change

Assert.AreEqual(False, dt.Rows(0).IsNull("deliveryDate"));

to

Assert.IsFalse(dt.Rows(0).IsNull("deliveryDate"));

or

Assert.That(dt.Rows(0)("deliveryDate"), Is.Not.Null));

Unreadable tests are one of the reasons some people say Unit Testing is bad because it slows You down. You have to make Unit Tests as easy to read and understand as possible. So that they don't have arguments against unit testing ;)

There may be some typos as I don't use VB.NET. Assert examples are from NUnit.

花开柳相依 2024-11-14 17:52:50

我终于明白是怎么回事了。这与交易无关。这一切都很好。我的流程故意造成了不一致的行为。如果没有找到其他排名,该过程的一部分有一个“随机排名”来确定交货日期。单元测试需要重写以更好地反映业务规则。

I finally figured out what was going on. It had nothing to do with transactions. That's all working great. It was my process that was creating inconsistent behavior - by design. There was a part of the process that had a "random ranking" to determine the deliveryDate if no other rankings where found. The unit test needs to be rewritten to better reflect the business rules.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文