3

I just started on a rather extensive automation project that uses MS's UnitTestFramework. One thing I noticed is that when there's an error in my code - not the app I test - the framework catches that error and fails the test in a nice happy way that allows for the test iteration to complete. However, I want to be able to see those exceptions & stack trace in my log4net logs, and so far I have found no way to grab them in my test cleanup (or anywhere outside of a try catch block which I have no intention of splattering in every method).

Anyone know how to get these exceptions into my logs?

allie
  • 369
  • 1
  • 15

2 Answers2

4

You could use First-Chance Exception Notifications via the AppDomain.FirstChanceException Event -

This event is only a notification. Handling this event does not handle the exception or affect subsequent exception handling in any way. After the event has been raised and event handlers have been invoked, the common language runtime (CLR) begins to search for a handler for the exception. FirstChanceException provides the application domain with a first chance to examine any managed exception.

So something like this (note it is in a method marked as AssemblyInitialize, which means it runs once per test run, and the code is excluding the AssertFailedException thrown by MSTest when a test fails. You might want to exclude other exceptions as well, as otherwise there could be a lot of 'noise' in the logs.)

[TestClass]
public class Initialize
{
    [AssemblyInitialize]
    public static void InitializeLogging(TestContext testContext)
    {
         AppDomain.CurrentDomain.FirstChanceException += (source, e) =>
         {
           if (e.Exception is AssertFailedException == false)
                LogManager.GetLogger("TestExceptions").Error(e.Exception);
         };
    }
}
Community
  • 1
  • 1
stuartd
  • 70,509
  • 14
  • 132
  • 163
  • 1
    I wish I could give you more than 1 upvote, this has been driving me nuts. For anyone else who uses this solution, I threw this into the if to filter out noise since I really only need inner exceptions: e.Exception.InnerException != null – allie Jan 26 '16 at 17:12
0

If it feasible for you to replace the [TestMethod] attribute then you can define your own attribute MyTestMethod, say, by deriving from the default one like this:

public class MyTestMethodAttribute : TestMethodAttribute
{
    public override TestResult[] Execute(ITestMethod testMethod)
    {
        TestResult[] testResults = base.Execute(testMethod);
        foreach (var testResult in testResults.Where(e => e.Outcome == UnitTestOutcome.Failed))
            testResult.LogOutput += $"Exception `{testResult.TestFailureException.GetType().Name}` with message `{testResult.TestFailureException.Message}`.";
        return testResults;
    }
}

The following test then produces the expected log message Exception TestFailedException with message Assert.Fail failed. Some exception text. in the standard output panel of Visual Studio's Test Explorer.

[TestClass]
public class Tests
{
    [MyTestMethod]
    public void Test()
        => Assert.Fail("Some exception text");
}

The approach also works when tests execute in parallel.