簡體   English   中英

無法在C#WEB API REST服務單元測試中模擬Owin上下文

[英]Unable to mock Owin context in C# WEB API REST service unit test

我試圖在C#WEB API REST服務中模擬OWIN上下文,但OWIN上下文似乎總是為空。 我正在使用moq和.NET framework 4.5.2。

這是我試圖測試的控制器方法:

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);
}

一切似乎都很好,直到我們實際獲取然后返回null的OWIN GetOwinContext調用,因此拋出舊的栗子類型“對象引用未設置為對象的實例”的錯誤。

我對OWIN還很新。 任何人都可以在這里建議我嗎?

首先,我看到你在哪里設置了IOwinContext的模擬,但是你所顯示的代碼中沒有一個實際上將你的模擬與請求綁定在一起。 具體來說,沒有任何設置會導致controller.Request.GetOwinContext()返回您的模擬對象。 因此,由於您尚未設置該調用的期望,因此它將返回null。

測試

你遇到的真正痛苦是試圖模仿別人的非常寬的界面(復數)。 這總是很痛苦的事情。 作為證據,請查看您為初始化和執行測試而必須編寫的代碼量。 我會嘗試隱藏對這些東西的訪問權限,這些界面更加集中,易於模擬/存根/虛擬。

作為一個像我一樣測試狂熱的單元,我不會孤立地測試我的WebAPI或MVC控制器。 它們幾乎總是依賴於我不想虛擬的外部事物,如HttpContext ,身份驗證問題,方法級屬性。 相反,我保持控制器盡可能薄,主要是限制方法將內容編組進出內容等等。所有業務邏輯都被移動到控制器所依賴的類中。 然后,這些類更容易測試,因為它們不依賴於偽造的東西。

我仍然編寫運行控制器方法的測試,通常它們覆蓋控制器的每個分支。 只是這些是系統級功能測試,如MVC應用程序的自動瀏覽器測試或使用HttpClient for WebAPI應用程序訪問API的測試。 如果控制器保持很薄,那么首先就沒有太多的測試覆蓋率了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM