views:

352

answers:

2

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?

+2  A: 

xUnit.net's IUseFixture allows you to do per fixture setup. You could therefore define your own fixture class:

public class AdditionFixture : IDisposable
{
  public int Because() 
  {
    return 2 + 2;
  }

  public void Dispose()
  {
   //test tear down code
  }      
}

Your test class can then implement this (with setFixture requiring implementing) :

public class AdditionSpecification : IUseFixture<AdditionFixture>
{
  int result;

  public void SetFixture(AdditionFixture Fixture)
  {
   result = Fixture.Because();
  }

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

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

The xUnit runner will create a single instance of your fixture, and pass it into SetFixture before running each test. After running all of your tests, the runner will then dispose of the fixture if it implements IDisposable. I hope that helps!

The xUnit wiki on codeplex has more information, including a nice example of how to implement IUseFixture to manage a database connection for you test fixtures.

BenA
Thank you for trying to help, but this response does not answer the question that I asked; how do I tell xUnit.NET which methods I want to execute based on some convention without using the [Fact] attribute at all.
David Alpert
The solution I proposed achieves the effect of having Because() executed before each test is run. I realise that I'm still using the [Fact] attribute to achieve this though. May I ask why you wish to avoid using [Fact]s?
BenA
To remove some ceremony from the writing of test fixtures; if I follow a convention for coding my test fixtures then I can code that logic into the runner (or into a custom ITestClassCommand and RunWithAttribute, as it happens) and remove all the [Fact] attributes, thus making my tests a bit more readable and faster to code.
David Alpert
+1  A: 

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

David Alpert