【问题标题】:Preventing catch of NUnit AssertionException?防止捕获 NUnit AssertionException?
【发布时间】:2011-04-12 09:24:14
【问题描述】:

我正在处理一个项目,我需要与吞下异常的代码进行互操作。特别是,我正在编写 NUnit 单元测试。在某些地方,我想在作为委托传递的代码中嵌入断言,作为模拟特定行为的一部分。我遇到的问题是 AssertionException 被调用委托的代码吞噬,这意味着测试通过,即使测试断言失败。

有什么方法可以通知 NUnit 一个测试应该失败并且不能通过捕获 AssertionException 来规避?我无法修改吞下异常的代码,因为我没有完全的所有权并且它已经在半生产中使用。我希望有一个干净的方法来完成这个。

我想出的最好的是这样的:

    private static string _assertionFailure;
    public static void AssertWrapper(Action action)
    {
        try
        {
            action();
        }
        catch (AssertionException ex)
        {
            _assertionFailure = ex.Message;
            throw;
        }
    }

    [Test]
    [ExpectedException(typeof(AssertionException))]
    public void TestDefeatSwallowing()
    {
        Action failure = () => AssertWrapper(() => Assert.Fail("This is a failure"));

        EvilSwallowingMethod(failure);

        if (_assertionFailure != null)
            Assert.Fail(_assertionFailure);
    }

    private void EvilSwallowingMethod(Action action)
    {
        try
        {
            action();
        }
        catch
        {
        }
    }

它有效,但它很丑陋。我必须包装每个 Assert 调用,并且必须在每个测试结束时检查断言是否被吞下。

【问题讨论】:

  • 不完全相关 - 你是否推动了这段代码吞下异常链的事实?半生产代码应该更容易更改(当然取决于具体情况)。相关 - 这看起来是一种非常聪明的方法。你必须解决一个极其丑陋的模式——这很难干净地做到。
  • @arootbeer,我正在与之合作的团队意识到这是一个问题,但这是一个非常系统性的问题,并且与最终用户认为“崩溃”是最糟糕的事情有关系统可以做到。有人开玩笑地建议可以将代码更改为仅在晚上 7 点到早上 7 点之间吞下异常。
  • 哇 - 那我就不说了 :) 你测试的所有方法都接受一个动作作为参数吗?
  • 我同意@arootbeer - 解决这个问题(并解决它)不会变得更容易。当它在生产中吞下一个 IOException 并且没有任何东西被保存时会发生什么?放入一个全局异常处理程序,记录详细信息并为用户提供友好的对话。另外,考虑用异常断言替换 ExpectedExceptionAttribute:nunit.org/index.php?p=exceptionAsserts&r=2.5.7
  • @arootbeer,它比我给出的示例稍微复杂一些。断言嵌入在模拟接口调用的回调中,因为我试图感知我正在测试的代码何时调用特定服务以及它是否正确使用服务调用。这相当于传入一个接口的测试实现,在其方法中带有测试断言。是使用服务的代码吞下异常,包括 AssertionException。

标签: c# unit-testing exception nunit assert


【解决方案1】:

所以你正在做这样的事情? (这是使用 Moq 语法)

var dependency1 = new Mock<IDependency1>();
dependency1.Setup(d => d.CalledMethod([Args])
    .Callback(TestOutArgsAndPossiblyThrow);

var objectUnderTest = new TestedObject(dependency1.Object);
objectUnderTest.MethodThatCallsIDependency1dotCalledMethod();

您已经将TestOutArgsAndPossiblyThrow 封装在您的AssertWrapper 类中了吗?

除非那是很遥远的事,否则我会说你做得恰到好处。您可以在可以记录对依赖项的调用状态的位置执行重新进入测试的执行。无论是通过捕获异常并分析它们还是直接检查方法参数的值,您都必须完成这项工作。如果您在黑匣子中吞下异常,您将不得不在它们重新进入黑匣子之前对其进行监控。

我仍然认为,适当的日志记录和通知会更好(您不必通知最终用户)。对于@TrueWill 的观点-当有IOException 或数据库不可用时你会做什么做什么

讨论编辑

你的场景结构是这样的吗?

TEST -> TESTED CODE -> SWALLOWING CODE -> THROWING MOCK

【讨论】:

  • 以上基本上就是我在做的,是的。生产代码确实记录了(大多数)捕获位置,但它仍然阻止异常传播。吞咽发生在我所依赖的代码中,但这是实现我编写和正在测试的代码逻辑的核心,因此不应嘲笑它。我想我可以创建一个不会吞下异常的类的副本,并将该副本仅用于我的代码,但这会产生其他政治和维护问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多