0

I need to check one from three possible types of exceptions. If one of these exceptions thrown, the test considered passed. I'm using [Theory] and [MemberData] for several scenarios.

[Theory]
[MemberData(nameof(GetInvalidMimeMessages))]
public async Task ProcessAsync_TestFail(MimeMessage message)
{
    var stub = Mock.Of<IOptions<ScrapyardFilesOptions>>(s => s.Value.ConnectionString == "UseDevelopmentStorage=true" && s.Value.Container == "exchange");
    var loggerMock = new Mock<ILogger<ScrapyardFilesHandler>>(MockBehavior.Loose);
    var scrapyard = new ScrapyardFilesHandler(loggerMock.Object, stub);
    var ex = await Assert.ThrowsAnyAsync<Exception>(() => scrapyard.ProcessAsync(message));

    // imagine solution somehow like that
    Assert.IsType( 
                    { 
                      typeof(NullReferenceException)    ||
                      typeof(KeyNotFoundException)      ||
                      typeof(InvalidOperationException) ||
                    },
                    ex); 
}

private static IEnumerable<object[]> GetInvalidMimeMessages()
{
    yield return new object[] { null };
    yield return new object[] { new MimeMessage() };
    yield return new object[]
    {
        new MimeMessage(
            new List<InternetAddress>(),
            new InternetAddressList() { new MailboxAddress("ExchangeScrapyard@ol.su"), new MailboxAddress("exchange+scrapyard@Olex.su"), },
            string.Empty,
            MimeEntity.Load(ParserOptions.Default, Stream.Null))
    };
    yield return new object[]
    {
        new MimeMessage(
            new List<InternetAddress>(),
            new InternetAddressList() { new MailboxAddress("ExchangeOLC449@ol.su"), new MailboxAddress("exchange+olc@Ol.su"), },
            string.Empty,
            MimeEntity.Load(ParserOptions.Default, Stream.Null))
    };
}

How can I get such assert?

anatol
  • 1,680
  • 2
  • 24
  • 47
  • 2
    Why not use `Assert.Throws` instead? – MakePeaceGreatAgain Nov 15 '17 at 08:03
  • Why can one action with one input result in multiple exceptions? – CodeCaster Nov 15 '17 at 08:23
  • One message should return in one exception, because now you pass multiple messages into your test, but you don't know which message threw which exception. – CodeCaster Nov 15 '17 at 08:38
  • @CodeCaster not sure that I've got you correctly, but I should not know which message threw which exception (*actually I already know that*) – anatol Nov 15 '17 at 08:47
  • Yes, you should. You pass four different messages and expect three different exceptions. You need to know which message results in which exception. What if the null message throws an InvalidOperationException? Does that mean your code works correctly? – CodeCaster Nov 15 '17 at 09:05
  • @CodeCaster I do some tolerance on it because `null` message will never throw an `InvalidOperationException`. Your way is very strict but I agree, it's need sometimes. So if you can offer solution based on your strict strategy it will be great – anatol Nov 15 '17 at 09:34
  • @CodeCaster also I have to note that if `null` message will throw an `InvalidOperationException` then test will be failed and it is ok. It will mean that something going wrong – anatol Nov 15 '17 at 09:39
  • You could put the expected exception type in your test data, something like `Tuple`. – CodeCaster Nov 15 '17 at 10:16

2 Answers2

5
var exceptions = new List<Type>()
            {
                typeof(NullReferenceException),
                typeof(KeyNotFoundException),
                typeof(InvalidOperationException),
            };

var ex = await Assert.ThrowsAnyAsync<Exception>(() => foo.Bar());
Assert.Contains(ex.GetType(), exceptions);
anatol
  • 1,680
  • 2
  • 24
  • 47
  • 2
    Interesting solution, but will only work on the exact types, not on derived ones. But I suppose that´s good enough as you won´t derive from `NullReferenceException` or any of the others. – MakePeaceGreatAgain Nov 15 '17 at 08:23
  • 1
    You can check for derived exceptions with reflection's `IsAssignable` and `IsSubClass`: https://stackoverflow.com/a/2742288/213550 – VMAtm Nov 17 '17 at 00:37
2

As you have multiple possible exceptions to be thrown there´s no built-in way in NUnit to check this. If you´re only up for one exception you could use this:

Assert.Throws<NullReferenceException>(() => DoSomething);

However with multiple ones you can use good old try-catch:

try
{
    DoSomething();
}
catch(NullReferenceException) { Assert.Pass(); }
catch(KeyNotFoundException) { Assert.Pass(); }
catch(InvalidOperationException) { Assert.Pass(); }
catch(Exception) { Assert.Fail("Unexpected exception was caught"); }
Assert.Fail("No exception was caught");
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111