无法在C#WEB API REST服务单元测试中模拟Owin上下文 [英] Unable to mock Owin context in C# WEB API REST service unit test

查看:96
本文介绍了无法在C#WEB API REST服务单元测试中模拟Owin上下文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to mock the OWIN context in a C# WEB API REST service but the OWIN context always seems to be null. I am using moq and .NET framework 4.5.2.

这是我要测试的控制器方法:

Here is the controller method I am trying to test:

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调用上出错的代码:

And in a helper class the code that goes wrong, on the controller.Request.GetOwinContext call:

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,因此会引发旧的栗子类型对象引用未设置为对象实例"的错误.

All seems fine until we actually do the get then the OWIN GetOwinContext call which always returns null and hence throws an error of the old chestnut type "Object Reference not set to an instance of an object".

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

I am still pretty new to OWIN. Can anyone advise me here?

推荐答案

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

First off, I see where you've setup your mock of IOwinContext, but none of the code that you've shown has actually tied your mock into the request. Specifically, there's nothing setup that causes controller.Request.GetOwinContext() to return your mocked object. So, since you haven't setup an expectation for that call, it will return null.

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

The real pain that you're running into is from trying to Mock someone else's very wide interfaces (plural). That's always a painful thing to do. As evidence, look at the amount of code you've had to write to initialize and execute your test. I would try and hide the access to these things behind interfaces that are much more focused and easy to mock / stub / dummy.

就像我对单元狂热者一样,我从来没有孤立地测试过我的WebAPI或MVC控制器.它们几乎总是依赖于我不想伪装的外部事物,例如HttpContext,身份验证问题,方法级属性.取而代之的是,我将控制器保持尽可能的薄,主要是限制了将事物编入和传出上下文的方法等.所有业务逻辑都移入了控制器所依赖的类中.然后,这些类将更易于测试,因为它们不依赖于难以伪造的东西.

As much of a unit testing zealot as I am, I don't ever test my WebAPI or MVC controllers in isolation. They almost always depend on external things that I don't want to dummy up like HttpContext, authentication concerns, method-level attributes. Instead, I keep the controllers as thin as possible, mostly limiting the methods to marshalling things in and out of the context, etc. All of the business logic gets moved into classes that the controllers depend on. Then, those classes are easier to test because they don't depend on things that are a pain to fake.

我仍会编写测试控制器方法的测试,通常它们涵盖了控制器的每个分支.仅仅是这些是系统级功能测试,例如针对MVC应用程序的自动浏览器测试或针对WebAPI应用程序使用HttpClient命中API的测试.如果控制器保持精简,那么首先就没有太多可以覆盖测试的地方了.

I still write tests that exercise the controller methods, and generally they cover every branch of the controllers. It's just that these are system-level functional tests like automated browser tests for MVC apps or tests that hit the API using an HttpClient for WebAPI apps. If the controllers are kept thin, then there isn't much there to get test coverage on in the first place.

这篇关于无法在C#WEB API REST服务单元测试中模拟Owin上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆