Search code examples
c#xunit.netextending

Extend xUnit.NET to use custom code when processing a class and locating test methods


I'm a big fan of the xUnit.NET framework; I find it light, simple, clean, and extensible.

Now let's say that I have a class like so:

public class AdditionSpecification
{
  static int result;

  public void Because()
  {
    result = 2 + 2;
  }

  public void Result_is_non_zero()
  {
    Assert.True(result <> 0);
  }

  public void Result_is_correct()
  {
    Assert.Equal(4, result);
  }
}

With the test class above I want xUnit.NET to see 2 test cases and to run the Because() method before each of them.

Leaving aside any issues you may have with my class or method names, the structure of this test/specification, the xUnit.NET framework, or BDD, here's my question:

How can I tell xUnit.NET that I want to customize how it identifies and executes test methods out of this class without using a custom [Fact]-like attribute on each target test method?

I know that I can derive from BeforeAfterAttribute to decorate each test method with custom before and after execution. How can i do this at the class level? Do i have to write a custom runner?


Solution

  • So it turns out that I was looking for the ITestClassCommand.EnumerateTestMethods() method.

    1. The default xUnit.NET test runner will iterate over all the classes in your test assembly.
    2. For each one it will check for a RunWithAttribute; that's your chance to override the ITestClassCommand implementation that is used to identify methods containing tests. (RunWithNUnit is a good example)
    3. ITestClassCommand.EnumerateTestMethods() is called to process the test class and return an IEnumerable of test methods.
    4. each test IMethodInfo is then passed to ITestClassCommand.EnumerateTestCommands(IMethodInfo testMethod) to get the IEnumerable of ITestCommands
    5. each ITestCommand is then executed and given the opportunity to return a result.

    In the case of my example above, I would need something like:

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class RunWithMyTestClassCommandAttribute : RunWithAttribute
    {
       public RunWithMyTestClassCommandAttribute()
                   : base(typeof(MyTestClassCommand)) {}
    }
    

    Then I could decorate my above example with:

    [RunWithMyTestClassCommand]
    public class AdditionSpecification
    {
      static int result;
    
      public void Because()
      {
        result = 2 + 2;
      }
    
      public void Result_is_non_zero()
      {
        Assert.True(result <> 0);
      }
    
      public void Result_is_correct()
      {
        Assert.Equal(4, result);
      }
    }
    

    Finally, in MyTestClassCommand, I get to opportunity between EnumerateTestMethods() and EnumerateTestCommands(IMethodInfo testMethod) to use whatever logic I want to locate and construct ITestCommand instances that get executed as individual tests.

    BTW, in the process of researching this issue, I ran into a small bug in the xUnit.NET framework where a custom IMethodInfo generated by EnumerateTestMethods() never showed up in EnumerateTestCommands(..) because it was being unwrapped and rewrapped by the test runner or one of it's factories.

    I submitted this issue to the xUnit project on codeplex and it was corrected on May 30th, 2009 for xUnit.NET 1.5 CTP 2