AutoFixie

Last week, we saw how the Fixie test framework deals with test method parameters. Your custom convention calls a Parameters(…) hook, supplying an explanation of where parameters should come from and how many times the test method should be called. The example convention was a little underwhelming, since parameters had to be explicitly listed within [Input] attributes. Today, we’ll get our parameter values from somewhere more interesting, AutoFixture.

Parameterized tests are useful when a single test method needs to run many times against a large set of independent inputs. However, it’s perfectly reasonable for parameterized tests to be called a single time, and in this case the purpose of using parameters is to hide away some otherwise-distracting instantiation code. Your test can focus on the actual system-under-test. AutoFixture’s a great example for single-call parameterized test methods, since it’s purpose is to automate boring class-population code you’d rather not clutter your tests with.

Let’s start with a simple Person class:

public class Person
{
    public string Name { get; set; }
    public DateTime BirthDay { get; set; }

    public override string ToString()
    {
        return String.Format("{0}, born on {1}.", Name, BirthDay);
    }
}

Next, I have a test class with three tests. Two of the tests have parameters. Since I’m just investigating Fixie’s ability to pass meaningful values to these tests, they write the actual incoming values to the console instead of making assertions:

public class ParameterizedTests
{
    public void TestWithNoParameters()
    {
        Console.WriteLine("TestWithNoParameters");
        Console.WriteLine();
    }

    public void TestWithTrivialParameters(int i, bool b)
    {
        Console.WriteLine("TestWithTrivialParameters");
        Console.WriteLine("    i: " + i);
        Console.WriteLine("    b: " + b);
        Console.WriteLine();
    }

    public void TestWithInterestingParameters(Person personA, Person personB)
    {
        Console.WriteLine("TestWithInterestingParameters");
        Console.WriteLine("    Person A: " + personA);
        Console.WriteLine("    Person B: " + personB);
        Console.WriteLine();
    }
}

I haven’t told Fixie what it means for a test to have parameters, so when we run our test under the default convention we see some failures:

------ Test started: Assembly: AutoFixtureDemo.dll ------

TestWithNoParameters

Test 'ParameterizedTests.TestWithTrivialParameters' failed: System.Reflection.TargetParameterCountException
  Parameter count mismatch.
  at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
  at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
  at Fixie.Case.Execute(Object instance)

Test 'ParameterizedTests.TestWithInterestingParameters' failed: System.Reflection.TargetParameterCountException
  Parameter count mismatch.
  at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
  at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
  at Fixie.Case.Execute(Object instance)

1 passed, 2 failed, 0 skipped, took 0.25 seconds (Fixie 0.0.1.98).

Today, I’d like parameters to come from AutoFixture, similar to AutoFixture’s support for xUnit Theories. I needed to define an extension method for AutoFixture first, though, because normal AutoFixture usage involves knowing ahead of time what type to instantiate. Instead, we want to be able to ask AutoFixture to instantiate any arbitrary Type:

public static class AutoFixtureExtensions
{
    //Normal AutoFixture usage is to write:
    //
    //  var thing = autoFixture.Create<MyClass>();
    //
    //If all you have is an arbitrary Type object, though,
    //this extension method lets you write:
    //
    //  var thing = autoFixture.Create(type);
    public static object Create(this ISpecimenBuilder builder, Type type)
    {
        return new SpecimenContext(builder).Resolve(type);
    }
}

Finally, I define a custom convention which takes advantage of the Parameters(…) hook introduced last week:

using System;
using System.Linq;
using Fixie;
using Fixie.Conventions;

public class CustomConvention : Convention
{
    readonly Ploeh.AutoFixture.Fixture autoFixture = new Ploeh.AutoFixture.Fixture();

    public CustomConvention()
    {
        Classes
            .NameEndsWith("Tests");

        Methods
            .Where(method => method.IsVoid());

        Parameters(type => autoFixture.Create(type));
    }

    private void Parameters(Func<Type, object> parameterByType)
    {
        Parameters(method =>
        {
            var parameterTypes = method.GetParameters().Select(x => x.ParameterType);

            var parameterValues = parameterTypes.Select(parameterByType).ToArray();

            return new[] { parameterValues };
        });
    }
}

As we saw last week, the inherited Parameters(…) method wants you to provide a method that takes in a MethodInfo and returns an IEnumerable<object[]>, since in general it needs to support any number of calls to a given test method. For today’s AutoFixture convention, though, we really just want to call each parameterized test once, and we want parameters to be based simply on the parameter types. Therefore, this convention includes a convenience overload for Parameters(…) which accepts a Func<Type, object>. This overload finds a single value for each parameter, based on its type, and returns a single-item array to provoke a single call to each test method. If it proves useful in general, I’ll promote this overload to be accessible from any convention.

Running the tests again, we see that each test is successfully passed values generated by AutoFixture:

------ Test started: Assembly: AutoFixtureDemo.dll ------

TestWithNoParameters

TestWithTrivialParameters
    i: 60
    b: True

TestWithInterestingParameters
    Person A: Named5bf97cd-456e-488e-b75c-67c03b10c8f2, born on 8/22/2013 10:09:20 PM.
    Person B: Namec8c74d02-4211-419f-aa09-0ca469271986, born on 2/11/2014 6:29:26 AM.


3 passed, 0 failed, 0 skipped, took 0.26 seconds (Fixie 0.0.1.98).

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Patrick Lioi

I am a Senior Consultant at Headspring in Austin, TX. I created the Parsley text parsing library and the Fixie test framework.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Matt S.

    Very cool! What’s really exceptional about this, is how easy you made it to integrate AutoFixture. Furthermore, it seems reasonably easy to implement something that could use fixture customizations, the way xUnit+AutoFixture does via data attributes. However with Fixie, I could add customizations as a default, then do something different if an attribute is present, or a test has a conventional name, or a specific name.

  • Viktor Voropaev

    Cool! How could I use it in my project? I mean, will the convention become a part of Fixie, or should I copy-paste it from here?

    • Patrick Lioi

      At the moment, a convention class like this can only affect the test project it is defined within. So, yes, you’d want to copy-paste it into your test project. I want to make conventions more easily sharable, though, so this limitation won’t be around for too long.