ASP.NET MVC - 单元测试在业务程序集中创建操作方法 URL

ASP.NET MVC - unit testing creation of action method URL's in a business assembly

我正在尝试为我的 ASP.NET MVC 代码编写一些单元测试,但遇到了障碍。

我有实体部分 classes(在业务 class 库程序集中)需要确定 URL 以调用操作方法和控制器。为了做到这一点,我发现这段代码效果很好——但是,唉,它使用了 HttpContext.Current,因此阻止我编写任何单元测试:

public string NavigateUrl 
{
    get 
    {
            HttpContextWrapper httpContextWrapper = new HttpContextWrapper(HttpContext.Current);
            UrlHelper urlHelper = new UrlHelper(new RequestContext(httpContextWrapper, RouteTable.Routes.GetRouteData(httpContextWrapper)));

            string url = urlHelper.Action("SomeAction", "MyController");
    }
}

我正在阅读有关 HttpContextBase 的内容 - 但是这在这里如何发挥作用?或者是否有另一种方法来确定实体 class 内的操作 URL (即在业务程序集中 - NOT MVC 项目而不是在控制器内或其他 MVC 基础设施 class)?

更新: 我需要 return 来自实体 class 的这个 URL 作为字符串,因为我需要在作为超链接导航 URL 的网格。实际上,有许多条件正在检查,URL 字符串 returned 可能是几种可能性之一 - 所以我不能只用一个控制器调用来替换它...

创建一个抽象来表示所需的功能。

例如

public interface IUrlHelper {
    string Action(string actionName, string controllerName);
    //TODO: create other desired members to be exposed
}

然后您为该抽象创建一个工厂。由于您没有将其注入我们使用服务定位器 ani 模式的实体。

public static class UrlHelperFactory {

    public static Func<IUrlHelper> Create = () => {
        throw new NotImplementedException();
    };

}

helper 和 factory 不耦合任何东西,可以存在于解决方案中的任何地方。

以下测试模拟服务以允许对实体进行隔离测试。

[TestClass]
public class UrlHelperFactory_Should {

    public class MyTestEntity {
        public string NavigateUrl {
            get {
                var urlHelper = UrlHelperFactory.Create();
                string url = urlHelper.Action("SomeAction", "MyController");
                return url;
            }
        }
    }

    [TestMethod]
    public void Generate_NavigationUrl() {
        //Arrange
        var mockHelper = Mock.Of<IUrlHelper>();
        UrlHelperFactory.Create = () => {
            return mockHelper;
        };
        var expected = "http://my_fake_url";
        Mock.Get(mockHelper)
            .Setup(_ => _.Action(It.IsAny<string>(), It.IsAny<string>()))
            .Returns(expected);

        var sut = new MyTestEntity();

        //Act
        var actual = sut.NavigateUrl;

        //Assert
        actual.Should().NotBeNullOrWhiteSpace()
            .And.Be(expected);
    }
}

在组合根的生产代码中,确保工厂知道如何构建服务

UrlHelperFactory.Create = () => {
    var httpContextWrapper = new HttpContextWrapper(HttpContext.Current);
    var urlHelper = new UrlHelper(new RequestContext(httpContextWrapper, RouteTable.Routes.GetRouteData(httpContextWrapper)));
    return new DefaultUrlHelperWrapper(urlHelper);
};

包装器可能看起来像这样...

internal class DefaultUrlHelperWrapper : IUrlHelper {
    private UrlHelper urlHelper;

    public DefaultUrlHelperWrapper(UrlHelper urlHelper) {
        this.urlHelper = urlHelper;
    }

    public string Action(string actionName, string controllerName) {
        return urlHelper.Action(actionName, controllerName);
    }

    //TODO: Implement other members
}