c# - 无法在 C# WEB API REST 服务单元测试中模拟 Owin 上下文

标签 c# unit-testing moq owin

我试图在 C# WEB API REST 服务中模拟 OWIN 上下文,但 OWIN 上下文似乎始终为空。我正在使用最小起订量和 .NET Framework 4.5.2。

这是我要测试的 Controller 方法:

public class CustomerController : ApiController
{
    // GET: api/Customer/n
    [HttpGet]
    [ClaimsPrincipalPermission(SecurityAction.Demand, Operation = Actions.View, Resource = Resources.Self)]
    public IHttpActionResult Get(int id)
    {
        if (id <= 0)
            return BadRequest();

        ClaimsPrincipalPermission.CheckAccess(id.ToString(),"UserId");

        string jsonResponse = string.Empty;
        HttpStatusCode returnCode = this.ForwardGetRequestToWallet(String.Format("customer/gethistory/{0}", id), out jsonResponse);
        if (returnCode== HttpStatusCode.OK)
            return Ok(jsonResponse);
        return StatusCode(returnCode);            
    } 

在辅助类中出错的代码,在 controller.Request.GetOwinContext 调用上:

public static HttpClient GetClient(this ApiController controller)
{
    HttpClient client = new HttpClient();
    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
    client.DefaultRequestHeaders.Authorization = controller.Request.Headers.Authorization;
    var test = controller.Request.GetOwinContext().Request.RemoteIpAddress;
    return client;
} 

我的测试初始化​​:

[TestInitialize()]
public void MyTestInitialize() 
{
    player_user_id = Convert.ToInt32(ConfigurationManager.AppSettings["PLAYER_USER_ID"]);
    player_user_name = ConfigurationManager.AppSettings["PLAYER_USER_NAME"];
    API_protocol = ConfigurationManager.AppSettings["WEB_API_PROTOCOL"];
    API_host = ConfigurationManager.AppSettings["WEB_API_HOST"];
    API_port = ConfigurationManager.AppSettings["WEB_API_PORT"];

    wrapper = new ClaimsPrincipalWrapper();

    server = new Mock<HttpServerUtilityBase>();
    response = new Mock<HttpResponseBase>();

    request = new Mock<HttpRequestBase>();

    session = new Mock<HttpSessionStateBase>();
    session.Setup(s => s.SessionID).Returns(Guid.NewGuid().ToString());

    config = new HttpConfiguration();

    owinContext = new Mock<IOwinContext>();

    identity = new GenericIdentity(player_user_name);
    identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", player_user_id.ToString()));

    principal = new GenericPrincipal(identity, new[] { "User" });

    context = new Mock<HttpContextBase>();
    context.SetupGet(c => c.Request).Returns(request.Object);
    context.SetupGet(c => c.Response).Returns(response.Object);
    context.SetupGet(c => c.Server).Returns(server.Object);
    context.SetupGet(c => c.Session).Returns(session.Object);
    context.SetupGet(p => p.User.Identity.Name).Returns(player_user_name);
    context.SetupGet(p => p.Request.IsAuthenticated).Returns(true);
    context.SetupGet(s => s.User).Returns(principal);

}

我的测试:

[TestMethod]
public void Get()
{
    var queryMock = new Mock<IReadableStringCollection>();

    var requestMock = new Mock<IOwinRequest>();

    requestMock.Setup(m => m.Query).Returns(queryMock.Object);

    owinContext.Setup(m => m.Request).Returns(requestMock.Object);

    testURI = new Uri(String.Format("{0}://{1}:{2}/Get/{3}", API_protocol, API_host, API_port, player_user_id));
    request.Setup(r => r.Url).Returns(testURI);

    message = new HttpRequestMessage(HttpMethod.Get, testURI);
    var route = config.Routes.MapHttpRoute("Default", "api/{controller}/{id}");
    var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "customerController", "get" } });

    var testControllerContext = new HttpControllerContext(config, routeData, message);

    var controller = new SharedWalletAPI.Controllers.CustomerController()
    {
        ControllerContext = testControllerContext,
        Request = new HttpRequestMessage(HttpMethod.Get, testURI),
        User = new System.Security.Principal.GenericPrincipal(identity, new string[0])
    };

    // Act
    var _result = controller.Get(player_user_id);

    // get JSON
    //CustomerHistoryList jsonObj = JsonConvert.DeserializeObject<CustomerHistoryList>(_result);

    // Assert
    Assert.IsNotNull(_result);
}

在我们实际执行 get 之前,一切似乎都很好,然后调用 OWIN GetOwinContext,该调用始终返回 null,因此抛出旧栗子类型的错误“对象引用未设置为对象的实例”。

我对 OWIN 还是很陌生。有人可以在这里给我建议吗?

最佳答案

首先,我看到您在哪里设置了 IOwinContext 的 mock,但是您显示的代码都没有实际将您的 mock 绑定(bind)到请求中。具体来说,没有任何设置会导致 controller.Request.GetOwinContext() 返回模拟对象。因此,由于您没有为该调用设置预期,它将返回 null。

测试

您遇到的真正痛苦是试图模拟其他人非常广泛的界面(复数)。这总是一件痛苦的事情。作为证据,请查看您为初始化和执行测试而必须编写的代码量。我会尝试将对这些东西的访问隐藏在更加集中且易于模拟/ stub /虚拟的界面后面。

尽管我是一个单元测试狂热者,但我从不单独测试我的 WebAPI 或 MVC Controller 。它们几乎总是依赖于我不想伪装的外部事物,例如 HttpContext、身份验证问题、方法级属性。相反,我让 Controller 尽可能薄,主要将方法限制为在上下文中编码事物等。所有业务逻辑都移到 Controller 所依赖的类中。然后,这些类更容易测试,因为它们不依赖于难以伪造的东西。

我仍然编写测试 Controller 方法,通常它们覆盖 Controller 的每个分支。只是这些是系统级功能测试,例如针对 MVC 应用程序的自动浏览器测试或针对 WebAPI 应用程序使用 HttpClient 访问 API 的测试。如果 Controller 保持纤薄,那么首先就没有太多可用于测试的地方。

关于c# - 无法在 C# WEB API REST 服务单元测试中模拟 Owin 上下文,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38919939/

相关文章:

c# - 哪种数据包捕获格式更好?

c# - 如何验证 Moq 中的对象类型参数

c# - 将具有非托管导出的 C# DLL 加载到 Python 中

c# - 在输入键按下时单击特定按钮

bash - 使用 kcov/shunit2 测量已执行 shell 脚本的代码覆盖率

java - 是否有创建 JUnit 测试用例的程序?

java - 如何告诉 JUnit 忽略嵌套异常?

c# - 如何验证调用了一个方法?

c# - 发布 Moq'ing HttpResponseMessage

c# - Tfs 联机并安装 .pfx