New to AutoFixture trying to get my head around it and I can't see it helping me

StackOverflow https://stackoverflow.com/questions/16455511

  •  14-04-2022
  •  | 
  •  

Question

Currently, I'm using custom made fake objects that behind the scenes use NSubstitute which creates the actual objects but it's becoming very hard to maintain as the project grows, so I'm trying to find alternatives and I'm hoping that AutoFixture is the right tool for the job.

I read the documentation and I'm struggling because there's very little to no documentation and I read most of the blog posts by Mark Seemann including the CheatSheet.

One of the things that I'm having hard time to grasp is how to create an object with a constructor that have parameters, in my case I need to pass argument to CsEmbeddedRazorViewEngine as well as HttpRequestBase to ControllerContext.

The way I see it is that I need to create a fake objects and finally create a customization object that injects them to

I also looked into NBuilder it seems slightly more trivial to pass arguments there but I've heard good things about AutoFixture and I would like to give it a try. :)

I'm trying to reduce the amount of fake objects I have so here is a real test, how can I do the same thing with AutoFixture?

[Theory, 
 InlineData("en-US"), 
 InlineData("en-us"), 
 InlineData("en")]
public void Should_return_the_default_path_of_the_view_for_enUS(string language)
{
    // Arrange
    const string EXPECTED_VIEW_PATH = "~/MyAssemblyName/Views/Home/Index.cshtml";

    CsEmbeddedRazorViewEngine engine = CsEmbeddedRazorViewEngineFactory.Create(ASSEMBLY_NAME, VIEW_PATH, string.Empty);

    string[] userLanguage = { language };

    HttpRequestBase request = FakeHttpRequestFactory.Create(userLanguage);

    ControllerContext controllerContext = FakeControllerContextFactory.Create(request);

    // Act
    ViewEngineResult result = engine.FindPartialView(controllerContext, VIEW_NAME, false);

    // Assert
    RazorView razorView = (RazorView)result.View;

    string actualViewPath = razorView.ViewPath;

    actualViewPath.Should().Be(EXPECTED_VIEW_PATH);
}

P.S. I'm using xUnit as my testing framework and NSubstitute as my mocking framework should I install both AutoFixture.Xunit and AutoFixture.AutoNSubstitute?

UPDATE: After learning more and more about it I guess it is not the right tool for the job because I tried to replace my test doubles factories with AutoFixture rather than setting up my SUT with it.

Due to odd reason I thought it's doing the same thing NBuilder is doing and from what I can see they are very different tools.

So after some thinking I think I'll go and change the methods I have on my test doubles factories to objects then use AutoFixture to create my SUT and inject my test doubles to it.

Was it helpful?

Solution

Note: I don't have the source code for the CsEmbeddedRazorViewEngine type and all the other custom types.

Here is how it could be written with AutoFixture:

[Theory]
[InlineAutoWebData("en-US", "about", "~/MyAssemblyName/Views/Home/Index.cshtml")]
[InlineAutoWebData("en-US", "other", "~/MyAssemblyName/Views/Home/Index.cshtml")]
public void Should_return_the_default_path_of_the_view_for_enUS(
    string language, 
    string viewName,
    string expected,
    ControllerContext controllerContext,
    CsEmbeddedRazorViewEngine sut)
{
    var result = sut.FindPartialView(controllerContext, viewName, false);
    var actual = ((RazorView)result.View).ViewPath;

    actual.Should().Be(expected);
}

How it works:

It uses AutoFixture itself together with it's glue libraries for xUnit.net and NSubstitute:

PM> Install-Package AutoFixture.Xunit
PM> Install-Package AutoFixture.AutoNSubstitute

With InlineAutoWebData you actually combine inline values and auto-generated data values by AutoFixture – also including Auto-Mocking with NSubstitute.

internal class InlineAutoWebDataAttribute : CompositeDataAttribute
{
    internal InlineAutoWebDataAttribute(params object[] values)
        : base(
            new InlineDataAttribute(values),
            new CompositeDataAttribute(
                new AutoDataAttribute(
                    new Fixture().Customize(
                        new WebModelCustomization()))))
    {
    }
}

Remarks:

You could actually replace the WebModelCustomization customization above with AutoNSubstituteCustomization and it could work.

However, assuming that you are using ASP.NET MVC 4, you need to customize the Fixture instance with:

internal class WebModelCustomization : CompositeCustomization
{
    internal WebModelCustomization()
        : base(
            new MvcCustomization(),
            new AutoNSubstituteCustomization())
    {
    }

    private class MvcCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Customize<ControllerContext>(c => c
                .Without(x => x.DisplayMode));

            // Customize the CsEmbeddedRazorViewEngine type here.
        }
    }
}

Further reading:

OTHER TIPS

I ended up doing this.

[Theory,
 InlineData("en-US", "Index", "~/MyAssemblyName/Views/Home/Index.cshtml"),
 InlineData("en-us", "Index", "~/MyAssemblyName/Views/Home/Index.cshtml"),
 InlineData("en", "Index", "~/MyAssemblyName/Views/Home/Index.cshtml")]
public void Should_return_the_default_path_of_the_view(string language, string viewName, string expected)
{
    // Arrange
    CsEmbeddedRazorViewEngine engine = new CsEmbeddedRazorViewEngineFixture();

    ControllerContext controllerContext = FakeControllerContextBuilder.WithLanguage(language).Build();

    // Act
    ViewEngineResult result = engine.FindPartialView(controllerContext, viewName, false);

    // Assert
    string actualViewPath = ((RazorView)result.View).ViewPath;

    actualViewPath.Should().Be(expected);
}

I encapsulated the details to setup my SUT into a fixture and used the builder pattern to handle my fakes, I think that it's readable and pretty straightforward now.

While AutoFixture looks pretty cool, the learning curve seems long and I will need to invest enough time to understand it, for now, I want to clean-up my unit tests and make them more readable. :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top