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

我正在为这个返回“ void”的方法编写一个单元测试。我希望有一个测试通过时没有抛出异常的情况。我该怎么用 C # 写呢?

Assert.IsTrue(????)

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

我希望我的问题够清楚。

86614 次浏览

Your unit test will fail anyway if an exception is thrown - you don't need to put in a special assert.

This is one of the few scenarios where you will see unit tests with no assertions at all - the test will implicitly fail if an exception is raised.

However, if you really did want to write an assertion for this - perhaps to be able to catch the exception and report "expected no exception but got this...", you can do this:

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


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

(the above is an example for NUnit, but the same holds true for MSTest)

Don't test that something doesn't happen. It's like assuring that code doesn't break. That's sort of implied, we all strive for non-breaking, bug-less code. You want to write tests for that? Why just one method? Don't you want all your methods being tested that they don't throw some exception? Following that road, you'll end up with one extra, dummy, assert-less test for every method in your code base. It brings no value.

Of course, if your requirement is to verify method does catch exceptions, you do test that (or reversing it a bit; test that it does not throw what it is supposed to catch).

However, the general approach/practices remain intact - you don't write tests for some artificial/vague requirements that are out of scope of tested code (and testing that "it works" or "doesn't throw" is usually an example of such - especially in scenario when method's responsibilities are well known).

To put it simple - focus on what your code has to do and test for that.

In NUnit, you can use:

Assert.DoesNotThrow(<expression>);

to assert that your code does not throw an exception. Although the test would fail if an exception is thrown even if there was no Assert around it, the value of this approach is that you can then distinguish between unmet expectations and bugs in your tests, and you have the option of adding a custom message that will be displayed in your test output. A well-worded test output can help you locate errors in your code that have caused a test to fail.

I think it's valid to add tests to ensure that your code is not throwing exceptions; for example, imagine you are validating input and need to convert an incoming string to a long. There may be occasions when the string is null, and this is acceptable, so you want to ensure that the string conversion does not throw an exception. There will therefore be code to handle this occasion, and if you haven't written a test for it you will be missing coverage around an important piece of logic.

This helper class scratched my itch with MSTest. Maybe it can scratch yours also.

[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);
}
}
}

I like to see an Assert.Whatever at the end of each test, just for consistency... without one, can I really be sure there's not supposed to be one there?

For me, this is as simple as putting Assert.IsTrue(true);

I know I didn't accidentally put that code in there, and thus I should be confident enough at quick a skim through that this was as intended.

    [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


}

My friend Tim told me about ExpectedException. I really like this b/c it is more succinct, less code, and very explicit that you are testing for an exception.

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

You can read way more about it here: ExpectedException Attribute Usage.

Another way which worked for me is to store it in a variable and check output.

var result = service.Run()
Assert.IsFalse(result.Errors.Any())

With Xunit you can use this:

var exception = Record.Exception(() =>
MethodUnderTest());


Assert.Null(exception);

or for async operations

var exception = await Record.ExceptionAsync(async () =>
await MethodUnderTestAsync());


Assert.Null(exception);
using Moq;
using Xunit;


[Fact]
public void UnitTest_DoesNotThrow_Exception()
{
var builder = new Mock<ISomething>().Object;


//Act
var exception = Record.Exception(() => builder.SomeMethod());


//Assert
Assert.Null(exception);
}