Unit Testing an Html Helper with AutoFixture
I'm attempting to 开发者_如何学编程Unit Test an Html Helper using AutoFixture. Below is my SUT
public static MvcHtmlString SampleTable(this HtmlHelper helper,
SampleModel model, IDictionary<string, object> htmlAttributes)
{
if (helper == null)
{
throw new ArgumentNullException("helper");
}
if (model == null)
{
throw new ArgumentNullException("model");
}
TagBuilder tagBuilder = new TagBuilder("table");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.GenerateId(helper.ViewContext.HttpContext.Items[Keys.SomeKey].ToString());
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
As you can see it just returns an MVC Html string with table tags and Id attached to it. (See below Unit Test result for an example)
Unit Test with AutoFixture:
[Fact]
public void SampleTableHtmlHelper_WhenKeyExistWithinHttpContext_ReturnsExpectedHtml()
{
var fixture = new Fixture();
//Arrange
fixture.Inject<HttpContextBase>(new FakeHttpContext());
var httpContext = fixture.CreateAnonymous<HttpContextBase>();
fixture.Inject<ViewContext>(new ViewContext());
var vc = fixture.CreateAnonymous<ViewContext>();
vc.HttpContext = httpContext;
vc.HttpContext.Items.Add(Keys.SomeKey, "foo");
fixture.Inject<IViewDataContainer>(new FakeViewDataContainer());
var htmlHelper = fixture.CreateAnonymous<HtmlHelper>();
var sampleModel = fixture.CreateAnonymous<SampleModel>();
//Act
var result = SampleHelpers.SampleTable(htmlHelper, sampleModel, null).ToString();
//Assert
Assert.Equal("<table id=\"foo\"></table>", result);
}
FakeHttpContext and FakeViewDataContainer are just the fake implementations of HttpContextBase and IViewDataContainer.
This test passes and returns the expected result. However, I ‘m not sure I’m correctly utilizing the Autofixture here. Is there a better way to use AutoFixture within this Unit Test?
Based on partial information it's hard to tell exactly how the above test could be further reduced, but I would guess that it could be reduced.
First of all, the combo of invoking Inject
followed by CreateAnonymous
is rather idiomatic - particularly if you reverse the sequence. This is called Freezing the anonymous value (and is equivalent to a DI container's Singleton lifetime scope). It can be stated more succinctly like this:
var vc = fixture.Freeze<ViewContext>();
It also seems as though the test is mapping HttpContext to FakeHttpContext. Mapping can be done a little bit easier, but that'll map Transient instances...
In any case, unless you have compelling reasons to use Manual Mocks instead of a dynamic Mock library, you might as well decide to use AutoFixture as an auto-mocking container. That might rid you of a lot of that type mapping.
So, given all that, I'd guess that you might be able to reduce the test to something like this:
[Fact]
public void SampleTableHtmlHelper_WhenKeyExistWithinHttpContext_ReturnsExpectedHtml()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
//Arrange
var vc = fixture.Freeze<ViewContext>();
vc.HttpContext.Items.Add(Keys.SomeKey, "foo");
var htmlHelper = fixture.CreateAnonymous<HtmlHelper>();
var sampleModel = fixture.CreateAnonymous<SampleModel>();
//Act
var result = SampleHelpers.SampleTable(htmlHelper, sampleModel, null).ToString();
//Assert
Assert.Equal("<table id=\"foo\"></table>", result);
}
However, most of the Arrange part is now purely declarative, and since you seem to already be using xUnit.net, you can use AutoData Theories for AutoFixture to move most of the variables to method arguments:
[Theory, AutoMoqData]
public void SampleTableHtmlHelper_WhenKeyExistWithinHttpContext_ReturnsExpectedHtml(
[Frozen]ViewContext vc,
HtmlHelper htmlHelper,
SampleModel sampleModel)
{
//Arrange
vc.HttpContext.Items.Add(Keys.SomeKey, "foo");
//Act
var result = SampleHelpers.SampleTable(htmlHelper, sampleModel, null).ToString();
//Assert
Assert.Equal("<table id=\"foo\"></table>", result);
}
This assumes that you've bridged the AutoMoqCustomization with the AutoDataAttribute like this:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute :
base(new Fixture().Customize(new AutoMoqCustomization()))
{ }
}
Please keep in mind that you may need to tweak the above code a bit to make it fit the details of your API. This is only meant as a sketch.
精彩评论