如何在MSTest单元测试中检查“未发生异常”?


88

我正在为此返回“ void”的方法编写单元测试。我希望有一种情况,即在没有引发异常的情况下测试通过。如何用C#编写?

Assert.IsTrue(????)

(我的猜测是这是我应该检查的方式,但是“ ???”中有什么内容)

我希望我的问题足够清楚。


您正在使用MSTest还是NUnit?
马特·格兰德

2
在MSTest中,未捕获的异常将自动导致测试失败。您是否要考虑捕获的异常?
Phil

您可以查找“ C#的try-catch”,它将指导您如何处理抛出的异常或未抛出的异常。
Foggzie 2012年

1
如果是NUnit,请查看Assert.That(lambda).Throws.Nothing(尽管我认为最近已更改)
Matt Grande

Answers:


138

如果抛出异常,您的单元测试无论如何都会失败-您不需要放入特殊的断言。

这是您将看到根本没有断言的单元测试的少数情况之一-如果引发异常,则测试将隐式失败。

但是,如果您确实确实想为此编写一个断言-也许能够捕获异常并报告“没有异常但得到了此...”,则可以执行以下操作:

[Test]
public void TestNoExceptionIsThrownByMethodUnderTest()
{
    var myObject = new MyObject();

    try
    {
        myObject.MethodUnderTest();
    }
    catch (Exception ex)
    {
        Assert.Fail("Expected no exception, but got: " + ex.Message);
    }
}

(以上是NUnit的示例,但对于MSTest同样适用)


显然,您不应该去捕捉此类异常以使其成立。
Servy

7
只有抛出未捕获的异常,测试才会失败。根据异常处理程序中的代码,单元测试可能会通过。
Deliciouscode

1
这对Unittest女士很有用,因此Unittest中没有Assert.DoesNotThrow(()方法。)
BaşarKaya 18/12/12

25

在NUnit中,您可以使用:

Assert.DoesNotThrow(<expression>); 

断言您的代码不会引发异常。尽管即使没有声明也抛出异常会导致测试失败,但是这种方法的价值在于您可以区分未满足的期望和测试中的错误,并且可以选择添加一条自定义消息,将显示在您的测试输出中。措辞井井有条的测试输出可以帮助您找到导致测试失败的代码错误。

我认为添加测试以确保您的代码不会引发异常是有效的。例如,假设您正在验证输入,并且需要将输入的字符串转换为long。有时字符串为null,这是可以接受的,因此您要确保字符串转换不会引发异常。因此,将有代码来处理这种情况,并且如果您还没有编写测试,那么您将缺少重要逻辑的内容。


1
显式的DidNotThrow很不错。如果您习惯于在测试中看到Assert。*,您可能会认为另一个人很懒惰并忘记了。
Matt Beckman 2014年

在vstest或mstest中是否有与此等效的东西?
Dan Csharpster

1
@DanCsharpster,我认为至少在MSTest中没有-过去在MSTest中需要此功能时,我已经做了类似的事情: public class TestBase { //believe me, I don't like this anymore than you do. protected void AssertDoesNotThrow(Action action, string message) { try { action(); } catch (Exception) { Assert.Fail(message); } } }
Clarkeye

@Clarkeye,这是一个有趣的想法。谢谢!希望他们会在将来的版本中学习更好地复制NUnit。我还考虑过在vstest和NUnit之间编写一个适配器。
Dan Csharpster 2015年

@DanCsharpster,您可能想看看的一件事是流利的断言,它对ShouldThrow和ShouldNotThrow有很好的支持:github.com/dennisdoomen/fluentassertions/wiki#exceptions。文档说它与MSTest兼容(尽管我只将它与XUnit和NUnit一起使用了)。可能无法完成您想要的所有事情,但是您仍然可以将其与MSTest断言混合使用。
Clarkeye

11

不要测试不会发生什么。就像确保代码不会中断一样。这有点暗示,我们都在努力开发不间断的,无错误的代码。您要为此编写测试吗?为什么只是一种方法?您是否不希望所有方法都经过测试以确保它们不会引发任何异常?遵循这条路,您将对代码库中的每个方法进行一次额外的,虚拟的,无需声明的测试。它没有任何价值。

当然,如果您的要求是验证方法是否捕获到异常,则可以进行测试(或将其反转一点;测试它不会抛出应捕获的内容)。

但是,一般的方法/做法仍然完好无损-您不会针对某些超出测试代码范围的人为/模糊的要求编写测试(并且测试“有效”或“不抛出”通常是这样-特别是在方法的职责众所周知的情况下)。

简而言之-专注于代码必须执行的操作为此进行测试。


10
-1我可以想到需要且不抛出异常的积极功能。对于一种用于处理异常的方法,请记录它们并采取措施-而无需进一步抛出异常。您提出了一个很好的总体观点-但然后绝对要讲绝对,就好像它总是对的。
罗布·莱文

3
@RobLevine:我理解您的示例,并且确实意识到您在这种情况下编写了测试。但是,正如您所注意到的,我的意思确实是关于更通用的实践-可以这么说,测试您的代码应该做什么与​​测试您的代码不应该做什么。我对我的帖子做了一些改写,以便使我的观点更加清楚和接近我的初衷。还为您提供了重新考虑投票的机会。感谢您的澄清,也很抱歉延迟回复。
公里

4
downvote已删除-下次我不会那么高兴触发down-vote!
罗布·莱文,2012年

4
在我们的项目中,我们有htmlvalidator类,如果html无效,则会抛出异常。例如,当用户使用丰富的组合输入(使用控制台)javascript时。因此,在我的案例代码中,我的代码要做的是不引发异常(白名单方法),我需要对此进行测试。
Machet 2014年

1
不赞成这个答案。有时在特定情况下测试缺少某些东西可能是有效的测试。
bytedev

7

此类帮助器类使我对MSTest感到不满。也许它也会刮伤您的。

[TestMethod]
public void ScheduleItsIneligibilityJob_HasValid_CronSchedule()
{
    // Arrange
    var factory = new StdSchedulerFactory();
    IScheduler scheduler = factory.GetScheduler();

    // Assert
    AssertEx.NoExceptionThrown<FormatException>(() =>
        // Act
        _service.ScheduleJob(scheduler)
    );
}

public sealed class AssertEx
{
    public static void NoExceptionThrown<T>(Action a) where T:Exception
    {
        try
        {
            a();
        }
        catch (T)
        {
            Assert.Fail("Expected no {0} to be thrown", typeof(T).Name);
        }
    }
}

@Remco Beurskens-在NoExceptionThrown <T>的末尾添加常规catch {}将抑制其他错误,这不是该方法的预期结果。这不是抑制所有异常的通用方法。它仅在抛出已知类型IS的异常时才会失败。
JJS

1
现在,它Assert已经过时了,但是具有单例属性访问器,That可以将其用作扩展方法的挂钩。拥有Assert.That.DoesNotThrow()而不是可能更整洁,更容易发现AssertEx.DoesNotThrow()。这只是一个意见。
理查德·豪尔

3

我希望Assert.Whatever在每次测试结束时都能看到一个,只是为了保持一致性……如果没有,我真的可以确定那里不应该有一个吗?

对我来说,这就像放 Assert.IsTrue(true);

知道我不是无意间将代码放在那里,因此我应该有足够的信心快速浏览一下这是预期的。

    [TestMethod]
    public void ProjectRejectsGappedVersioningByDefault() {

        var files = new List<ScriptFile>();
        files.Add(ScriptProjectTestMocks.GetVersion1to2());
        files.Add(ScriptProjectTestMocks.GetVersion3to4());

        Assert.Throws<ScriptProject.InvalidProjectFormatException>(() => {
            var sut = new ScriptProject(files);
        });

    }

    [TestMethod]
    public void ProjectAcceptsGappedVersionsExplicitly() {

        var files = new List<ScriptFile>();
        files.Add(ScriptProjectTestMocks.GetVersion1to2());
        files.Add(ScriptProjectTestMocks.GetVersion3to4());

        var sut = new ScriptProject(files, true);

        Assert.IsTrue(true);   // Assert.Pass() would be nicer... build it in if you like

    }

这是不一样的。如果您的代码抛出错误,则不会触发任何断言,并且测试运行将失败。您想通过声明条件来加入测试框架。
DvS

1

我的朋友Tim告诉我有关ExpectedException的信息。我真的很喜欢这个b / c,它更简洁,更少的代码,并且非常明确地表明您正在测试异常。

[TestMethod()]
[ExpectedException(typeof(System.Exception))]
public void DivideTest()
{
    int numerator = 4;
    int denominator = 0;
    int actual = numerator / denominator;
}

您可以在此处详细了解它:ExpectedException Attribute Usage


1
OP要求也不例外。
丹尼尔·怀特

我要在这里留下这个答案。我在搜寻Google如何测试例外情况时发现了这个问题,我认为这个答案应该在这里。OP在7年前回答了他们的问题。即使是指向其他答案的链接,我认为也是有帮助的。
杰西(Jess)

好极了蒂姆。🤔
鲁芬
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.