Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InlineAutoDataAttribute but for NUnit (for TestCase and TestCaseSource)

To be succint:

class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute() : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }
}

public interface IWeapon { bool LaunchAtEarth(double probabilityOfHitting); }

public class DeathStar
{
    readonly IWeapon _weapon;

    public DeathStar(IWeapon weapon) // guard clause omitted for brevity
    {
        this._weapon = weapon;
    }

    public bool FireFromOrbit(double probabilityOfHitting)
    {
        return this._weapon.LaunchAtEarth(probabilityOfHitting);
    }
}

// Make me pass, pretty please with a cherry on the top
[Test, AutoMoqData]
[TestCase(0.1), TestCase(0.5), TestCase(1)]
public void AutoMoqData_should_fill_rest_of_arguments_that_are_not_filled_by_TestCase(
    double probabilityOfHitting,
    [Frozen] Mock<IWeapon> weapon,
    DeathStar platform)
{
    Assert.That(weapon, Is.Not.Null);
    Assert.That(platform, Is.Not.Null);
} // Ignored with error: Wrong number of parameters specified.

// Make me pass, too!
[Test, AutoMoqData]
[TestCaseSource("GetTestCases")]
public void AutoMoqData_should_fill_rest_method_arguments_that_are_not_filled_by_TestCaseSource(
    double probabilityOfHitting,
    [Frozen] Mock<IWeapon> weapon,
    DeathStar platform)
{
    Assert.That(weapon, Is.Not.Null);
    Assert.That(platform, Is.Not.Null);
} // Ignored with error: Wrong number of parameters specified.

IEnumerable<TestCaseData> GetTestCases()
{
    yield return new TestCaseData(0.1);
    yield return new TestCaseData(0.5);
    yield return new TestCaseData(1);
}

This seems to do the trick if I were using Xunit: http://nikosbaxevanis.com/blog/2011/08/25/combining-data-theories-in-autofixture-dot-xunit-extension/ I cannot find anything equivalent for NUnit.

This: http://gertjvr.wordpress.com/2013/08/29/my-first-open-source-contribution/ seems to be already working in the current version of AutoFixture.NUnit2 (the AutoData attribute), but it doesn't handle the case when I want to make it take a params object[] such that the first parameters in the test method are filled using the attribute arguments and the rest are passed through to the AutoData attribute.

Can someone guide me towards the right direction ? There seems to be something missing I cannot grasp.

like image 605
Raif Atef Avatar asked Jul 17 '14 07:07

Raif Atef


2 Answers

OP's code here doesn't worked for me in nunit2.6.3. In fact it doesn't even compile, not sure if the nunit infrastructure has changed. It turns out that I've missed the nunit.core.dll reference, after adding it, it compiled. But I used my own implementation.

So I've rolled my own implementation. Implementation goes like this: We'll ask the AutoFixture for the parameters, it will give all the parameters which Test method needs, then we'll just over write the parameter values which we need.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class InlineAutoDataAttribute : AutoDataAttribute
{
    private readonly object[] values;
    public InlineAutoDataAttribute(params object[] values)
        : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
        this.values = values;
    }

    public override IEnumerable<object[]> GetData(MethodInfo method)
    {
        var testCaseData = base.GetData(method);

        foreach (object[] caseValues in testCaseData)
        {
            if (values.Length > caseValues.Length)
            {
                throw new InvalidOperationException("Number of parameters provided is more than expected parameter count");
            }
            Array.Copy(values, caseValues, values.Length);

            yield return caseValues;
        }
    }
}

How to use:

[Test]
[InlineAutoData(1, 2)]
[InlineAutoData(3, 4)]
public void Whatever_Test_Name(int param1, int param2, TemplateOrder sut)
{
    //Your test here
    Assert.That(sut, Is.Not.Null);
}

Test will be invoked with

param1 = 1, param2 = 2, sut = given by auto fixture
param1 = 3, param2 = 4, sut = given by auto fixture

You could argue that this implementation isn't efficient, but at least that works as expected. I've contacted Mark Seemann(Creator of AutoFixture) also, but it seems he also couldn't help with this. So for now I can live with this.

like image 185
Sriram Sakthivel Avatar answered Nov 04 '22 06:11

Sriram Sakthivel


I've got it working.

Unfortunately, due to some bad design choices in NUnit's extensibility API in 2.6 (no way to override or remove any of the built-in test case providers), I was forced to resort to some reflection to get at the internal ExtensionCollection instance in the "TestCaseProviders" class.

TL;DR: This should work in NUnit 2.6.x only. NUnit 3.0 is NOT compatible.

How to use

Just add the supplied [AutoMoqData] on a test already using the regular [TestCase] and [TestCaseSource] attributes. Test case parameters will be filled first and the rest of the test method arguments will be handled via AutoFixture. You may change the AutoMoqDataAttribute to use any different fixture customization (example: AutoRhinoMockCustomization) as you wish.

How to get it working with ReSharper's test runner

If you add this to an external assembly and reference it in your test project, NUnit won't see your add-in (because it only looks in the immediate test assembly being loaded or in DLLs in an "addins" subfolder of the currently running test runner executable.

In such case, just make an empty class in the current test project and make it inherit from AutoMoqDataAddIn. Tested using ReSharper unit test runner and it sees the test cases properly and auto-generates the test case names using only the "real" test parameters.

Show me the code!

GitHub: https://gist.github.com/rwasef1830/ab6353b43bfb6549b396

like image 42
Raif Atef Avatar answered Nov 04 '22 04:11

Raif Atef