MSTest Test Context Exception Handling

Is there a way that I can get to the exception that was handled by the MSTest framework using the TestContext or some other method on a base test class?

If an unhandled exception occurs in one of my tests, I'd like to spin through all the items in the exception.Data dictionary and display them to the test result to help me figure out why the test failed (we usually add data to the exception to help us debug in the production env, so I'd like to do the same for testing).

Note: I am not testing that an exception was SUPPOSED TO HAPPEN (I have other tests for that), I am testing a valid case, I just need to see the exception data.

Here is a code example of what I'm talking about.

[TestMethod]
public void IsFinanceDeadlineDateValid()
{
    var target = new BusinessObject();
    SetupBusinessObject(target);

    //How can I capture this in the text context so I can display all the data 
    //in the exception in the test result...

    var expected = 100;
    try
    {
        Assert.AreEqual(expected, target.PerformSomeCalculationThatMayDivideByZero());
    }
    catch (Exception ex)
    {
        ex.Data.Add("SomethingImportant", "I want to see this in the test result, as its important");
        ex.Data.Add("Expected", expected);
        throw ex;
    }

}

I understand there are issues around why I probably shouldn't have such an encapsulating method, but we also have sub tests to test all the functionality of PerformSomeCalculation...

However, if the test fails, 99% of the time, I rerun it passes, so I can't debug anything without this information. I would also like to do this on a GLOBAL level, so that if any test fails, I get the information in the test results, as opposed to doing it for each individual test.

Here is the code that would put the exception info in the test results.

    public void AddDataFromExceptionToResults(Exception ex)
    {
        StringBuilder whereAmI = new StringBuilder();
        var holdException = ex;
        while (holdException != null)
        {
            Console.WriteLine(whereAmI.ToString() + "--" + holdException.Message);
            foreach (var item in holdException.Data.Keys)
            {
                Console.WriteLine(whereAmI.ToString() + "--Data--" + item + ":" + holdException.Data[item]);
            }

            holdException = holdException.InnerException;
        }
    }

I've been running into the same issue, there doesn't seem to be support for this. You can't even use ApplicationDomain's unhandled exception hook since if MSTEST didn't catch exceptions before they bubbled up that far, it would itself crash.

Possible workaround:

    private delegate void TestImplDelegate();

    private void RunTestWithExceptionLogging(TestImplDelegate testImpl)
    {
        try
        {
            testImpl();
        }
        catch (Exception e)
        {
            string message = e.Message; // don't warn about unused variables

            // do logging here
        }
    }

    [TestMethod]
    public void test1()
    {
        RunTestWithExceptionLogging(test1Impl);
    }
    private void test1Impl()
    {
        // test code goes here

        throw new Exception("This should get logged by the test wrapper.");
    }

    [TestMethod]
    public void test2()
    {
        RunTestWithExceptionLogging(test2Impl);
    }
    private void test2Impl()
    {
        // test code goes here

        throw new Exception("This should get logged by the test wrapper.");
    }

It's certainly not optimal, but at least this way you don't have multiple copies of the exception handler code.

I would recommend filing a feature request for this at http://connect.microsoft.com/ (or see if someone else has already requested it, and add your vote.)


Here is a "clean" work around which will put the exception into a TestBase.TestContext property (or wherever you want in your TestBase instance).

The example uses postsharp (Which is an AOP lib) to inject try-catch to your code in compile time. It will inject to all methods which has [TestMethod] attribute. - the postsharp free edition will do the work, no need to by a license.

1st thing you have to do is creating the AOP attribute. The attributes defines what will be done in case of exception and + define conditions for injecting, this is how you do it:

/// <summary>
/// Catch exceptions of tests and save them in <see cref="TestBase"/> under TestContext.Properties. This is done since MSTEST does not supply any mechanism to catch tests exceptions.
/// </summary>
[SerializableAttribute]
public class CodedTestsExceptionsHandlingAop : OnExceptionAspect
{
    /// <summary>
    /// The name of the property that will be added to the test context properties object.
    /// </summary>
    public const string FailureExceptionProerty = "FailureException";

    /// <summary>
    /// Save the exception in a <see cref="TestBase"/> and rethrow.
    /// </summary>
    /// <param name="args"></param>
    public override void OnException(MethodExecutionArgs args)
    {
        var testBase = (Framework.TestBase) args.Instance;//The instance running the test inherits from TestBase.
        testBase.TestContext.Properties.Add("FailureException", args.Exception);

        args.FlowBehavior = FlowBehavior.RethrowException;
    }

    /// <summary>
    /// Make sure only test methods will get this AOP.
    /// </summary>
    /// <param name="method"></param>
    /// <returns></returns>
    public override bool CompileTimeValidate(MethodBase method)
    {
        if (method.IsDefined(typeof(TestMethodAttribute)))
            return true;

        return false;
    }
}

2nd thing, you'll have to add the following assembly level attribute to your tests project (which also need to have postsharp installed if it is not in the same project as your TestBase), this will apply the AOP injection over the tests assembly:

[assembly: CodedTestsExceptionsHandlingAop(AttributeTargetTypes = "*", AttributeTargetElements = MulticastTargets.Method)]

If you're just wanting to test expected exceptions you could use the ExpectedExceptionAttribute

I would think being able to get the exception for a unit test would be more useful when it is an unexpected exception and you want to log or gather extra information about the exception, rather than relying on the TestExplorer to find an examine failed tests.

链接地址: http://www.djcxy.com/p/44150.html

上一篇: python父类'包装'的孩子

下一篇: MSTest测试上下文异常处理