tug*_*erk 6 asp.net-mvc unit-testing dependency-injection moq asp.net-mvc-3
关于这个主题,我问了另一个问题:
这是当前的情况:在我的ASP.NET MVC 3 App上,我有一个如下定义的路由约束:
public class CountryRouteConstraint : IRouteConstraint {
private readonly ICountryRepository<Country> _countryRepo;
public CountryRouteConstraint(ICountryRepository<Country> countryRepo) {
_countryRepo = countryRepo;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
//do the database look-up here
//return the result according the value you got from DB
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
我正在使用如下:
routes.MapRoute(
"Countries",
"countries/{country}",
new {
controller = "Countries",
action = "Index"
},
new {
country = new CountryRouteConstraint(
DependencyResolver.Current.GetService<ICountryRepository<Country>>()
)
}
);
Run Code Online (Sandbox Code Playgroud)
在单元测试部分,我使用了以下代码:
[Fact]
public void country_route_should_pass() {
var mockContext = new Mock<HttpContextBase>();
mockContext.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath).Returns("~/countries/italy");
var routes = new RouteCollection();
TugberkUgurlu.ReservationHub.Web.Routes.RegisterRoutes(routes);
RouteData routeData = routes.GetRouteData(mockContext.Object);
Assert.NotNull(routeData);
Assert.Equal("Countries", routeData.Values["controller"]);
Assert.Equal("Index", routeData.Values["action"]);
Assert.Equal("italy", routeData.Values["country"]);
}
Run Code Online (Sandbox Code Playgroud)
在这里,我无法弄清楚如何传递依赖.任何的想法?
Ben*_*ter 11
我个人尝试避免在路线约束中执行此类验证,因为用这种方式表达您的意图要困难得多.相反,我使用约束来确保参数的格式/类型正确,并将这些逻辑放在我的控制器中.
在你的例子中,我假设如果国家无效,那么你将回到不同的路线(比如说"未找到国家"页面).依赖于您的路由配置比接受所有国家/地区参数并在您的控制器中检查它们更不可靠(并且更可能被破坏):
public ActionResult Country(string country)
{
if (country == "france") // lookup to db here
{
// valid
return View();
}
// invalid
return RedirectToAction("NotFound");
}
Run Code Online (Sandbox Code Playgroud)
除此之外,你在这里想要实现的(正如已经提到的)实际上是一个集成测试.当您发现框架的某些部分妨碍了测试时,那么它可能是重构的时候了.在你的例子中,我想测试
我们可以做的第一件事就是将Country验证移到一个单独的类中:
public interface ICountryValidator
{
bool IsValid(string country);
}
public class CountryValidator : ICountryValidator
{
public bool IsValid(string country)
{
// you'll probably want to access your db here
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以测试这个单元:
[Test]
public void Country_validator_test()
{
var validator = new CountryValidator();
// Valid Country
Assert.IsTrue(validator.IsValid("france"));
// Invalid Country
Assert.IsFalse(validator.IsValid("england"));
}
Run Code Online (Sandbox Code Playgroud)
CountryRouteConstraint然后我们改为:
public class CountryRouteConstraint : IRouteConstraint
{
private readonly ICountryValidator countryValidator;
public CountryRouteConstraint(ICountryValidator countryValidator)
{
this.countryValidator = countryValidator;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
object country = null;
values.TryGetValue("country", out country);
return countryValidator.IsValid(country as string);
}
}
Run Code Online (Sandbox Code Playgroud)
我们像这样映射我们的路线:
routes.MapRoute(
"Valid Country Route",
"countries/{country}",
new { controller = "Home", action = "Country" },
new { country = new CountryRouteConstraint(new CountryValidator())
});
Run Code Online (Sandbox Code Playgroud)
现在,如果你真的觉得有必要测试RouteConstraint,你可以独立测试它:
[Test]
public void RouteContraint_test()
{
var constraint = new CountryRouteConstraint(new CountryValidator());
var testRoute = new Route("countries/{country}",
new RouteValueDictionary(new { controller = "Home", action = "Country" }),
new RouteValueDictionary(new { country = constraint }),
new MvcRouteHandler());
var match = constraint.Match(GetTestContext(), testRoute, "country",
new RouteValueDictionary(new { country = "france" }), RouteDirection.IncomingRequest);
Assert.IsTrue(match);
}
Run Code Online (Sandbox Code Playgroud)
我个人不打算执行这个测试,因为我们已经抽象了验证代码,所以这只是测试框架.
为了测试路由映射,我们可以使用MvcContrib的TestHelper.
[Test]
public void Valid_country_maps_to_country_route()
{
"~/countries/france".ShouldMapTo<HomeController>(x => x.Country("france"));
}
[Test]
public void Invalid_country_falls_back_to_default_route()
{
"~/countries/england".ShouldMapTo<HomeController>(x => x.Index());
}
Run Code Online (Sandbox Code Playgroud)
根据我们的路由配置,我们可以验证有效国家/地区是否映射到国家/地区路由,无效国家/地区是否映射到后备路由.
但是,问题的主要内容是如何处理路径约束的依赖关系.上面的测试实际上测试了很多东西 - 我们的路由配置,路由约束,验证器以及可能访问存储库/数据库.
如果您依靠IoC工具为您注入这些工具,那么您将需要模拟验证器和存储库/数据库,并在设置测试时使用IoC工具注册这些工具.
如果我们可以控制如何创建约束会更好:
public interface IRouteConstraintFactory
{
IRouteConstraint Create<TRouteConstraint>()
where TRouteConstraint : IRouteConstraint;
}
Run Code Online (Sandbox Code Playgroud)
您的"真实"实现可以使用您的IoC工具来创建IRouteConstraint实例.
我喜欢将我的路由配置放在一个单独的类中,如下所示:
public interface IRouteRegistry
{
void RegisterRoutes(RouteCollection routes);
}
public class MyRouteRegistry : IRouteRegistry
{
private readonly IRouteConstraintFactory routeConstraintFactory;
public MyRouteRegistry(IRouteConstraintFactory routeConstraintFactory)
{
this.routeConstraintFactory = routeConstraintFactory;
}
public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Valid Country",
"countries/{country}",
new { controller = "Home", action = "Country" },
new { country = routeConstraintFactory.Create<CountryRouteConstraint>() });
routes.MapRoute("Invalid Country",
"countries/{country}",
new { controller = "Home", action = "index" });
}
}
Run Code Online (Sandbox Code Playgroud)
可以使用工厂创建具有外部依赖性的约束.
这使测试更容易.由于我们只对测试国家路线感兴趣,因此我们可以创建一个仅满足我们需求的测试工厂:
private class TestRouteConstraintFactory : IRouteConstraintFactory
{
public IRouteConstraint Create<TRouteConstraint>() where TRouteConstraint : IRouteConstraint
{
return new CountryRouteConstraint(new FakeCountryValidator());
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,这次我们使用的FakeCountryValidator包含足够的逻辑来测试我们的路线:
public class FakeCountryValidator : ICountryValidator
{
public bool IsValid(string country)
{
return country.Equals("france", StringComparison.InvariantCultureIgnoreCase);
}
}
Run Code Online (Sandbox Code Playgroud)
当我们设置测试时,我们将传递TestRouteFactoryConstraint给路由注册表:
[SetUp]
public void SetUp()
{
new MyRouteRegistry(new TestRouteConstraintFactory()).RegisterRoutes(RouteTable.Routes);
}
Run Code Online (Sandbox Code Playgroud)
这次我们运行路由测试时,我们没有测试验证逻辑或数据库访问.相反,当提供有效或无效的国家/地区时,我们会对我们的路由配置进行单元测试
| 归档时间: |
|
| 查看次数: |
3305 次 |
| 最近记录: |