Introduction
I'm writing unit tests for an extension method I wrote. Its only purpose it is to wrap startup logic which extends ViewLocationExpanders
list by an instance implementating IViewLocationExpander
. ViewLocationExpanders
is a property of RazorViewEngineOptions
, which can be configured in application startup in ConfigureServices()
method. I'm using XUnit 2.4.1.
Usage
Instead of:
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new ViewLocationExpander());
});
I can use:
services.AddViewLocationExpander(new ViewLocationExpander());
ViewLocationExpander
public class ViewLocationExpander : IViewLocationExpander
{
public IEnumerable<string> ExpandViewLocations(
ViewLocationExpanderContext context,
IEnumerable<string> viewLocations)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (viewLocations == null)
{
throw new ArgumentNullException(nameof(viewLocations));
}
/*
* Note:
* {0} = action name
* {1} = controller name
* {2} = area name
*/
var newViewLocations = new string[]
{
// Example: '/Views/Home/_Partials/FooBar.cshtml'
"/Views/{1}/_Partials/{0}.cshtml",
};
// Add new locations *AFTER* MVC default locations.
return viewLocations.Union(newViewLocations);
}
public void PopulateValues(ViewLocationExpanderContext context)
{
context.Values["customviewlocation"] = nameof(ViewLocationExpander);
}
}
Extension method
public static IServiceCollection AddViewLocationExpander(
this IServiceCollection services,
IViewLocationExpander viewLocationExpander)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (viewLocationExpander == null)
{
throw new ArgumentNullException(nameof(viewLocationExpander));
}
return services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(viewLocationExpander);
});
}
Unit tests
[Fact]
public void ExtensionMethodAddsNewViewLocationExpander()
{
// Arrange
var services = new ServiceCollection();
services.AddMvc();
// These two are required to active the RazorViewEngineOptions.
services.AddSingleton<IHostingEnvironment, HostingEnvironment>();
services.AddSingleton<ILoggerFactory, LoggerFactory>();
// Act
var serviceProvider = services.BuildServiceProvider();
var oldOptions = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>().Value;
services.AddViewLocationExpander(new ViewLocationExpander());
serviceProvider = services.BuildServiceProvider();
var newOptions = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>().Value;
// Assert
Assert.True(newOptions.ViewLocationExpanders.Count > oldOptions.ViewLocationExpanders.Count);
}
Questions
- Am I going beyond the scope of what unit testing should include? I'm afraid that my code is actually testing the basic functions of generic collections and/or aspects of ASP.NET Core.
- If my concern above is true, that should I write unit tests to this at all? How should it work?
1 Answer 1
I find your test is excellent (and I'll borrow it from you). I would, however, make it a little bit more exact and not only test the counts of generated locations but also whether they have the expected values.
You are not testing basic functions of collections or ASP.NET-Core aspects because it doesn't matter how it's implemented. What you are testing is whether your application has all the view locations it needs to work correctly.
This is a very important (integration) test and you should not remove it but improve it by asserting the actual locations too. If this breaks then your app wont't work anymore so it's a real lifesaver.
-
\$\begingroup\$ Thank you for answer. Maybe I should write further integration test checking if appropriate views are found with the new
ViewLocationExpanderadded
? But that would probably be outside the scope of unit testing and secondly inside that unit test method there is not much more to test (or at least I see it that way in debugger). And please, go ahead and borrow anything you want from my post. I'm happy it's useful. \$\endgroup\$Prolog– Prolog2019年04月19日 21:32:23 +00:00Commented Apr 19, 2019 at 21:32