[ASP.NET Identity] 使用 Microsoft.Owin.Testing 測試 OAuth Server 和 Web API

續上篇,https://dotblogs.com.tw/yc421206/2016/08/02/identity_oauth_owin_setup

隨著功能的演進,原本用 Fiddler 編寫的測試腳本,越來越不容易管理,Web API 的品質也越來越不穩定
有了 TestServer 之後,我的問題就一掃而空,接下來就來分享我的作法,

開發環境

  • Windows 10 Enterprise x64 CHT
  • VS2015 Update3 ENG

目錄

Step1.在 Simple.OAuthServer.Test 測試專案安裝以下套件

版本號會隨著時間不斷的更新,以下是我寫本篇時最新的版本

在 Simple.OAuthServer.Test 測試專案安裝以下套件

Install-Package Microsoft.Owin.Host.SystemWeb (3.0.1)
Dependencies
    Owin (>= 1.0.0)
    Microsoft.Owin (>= 3.0.1)

Install-Package Microsoft.AspNet.Identity.Owin (2.2.1)
Dependencies
    Microsoft.AspNet.Identity.Core (>= 2.2.1)
    Microsoft.Owin.Security (>= 2.1.0)
    Microsoft.Owin.Security.Cookies (>= 2.1.0)
    Microsoft.Owin.Security.OAuth (>= 2.1.0)

Install-Package Microsoft.Owin.Security.OAuth (3.0.1)
Dependencies
    Owin (>= 1.0.0)
    Microsoft.Owin (>= 3.0.1)
    Newtonsoft.Json (>= 6.0.4)
    Microsoft.Owin.Security (>= 3.0.1)

Install-Package Microsoft.AspNet.WebApi.Owin (5.2.3)
Dependencies
    Microsoft.AspNet.WebApi.Core (>= 5.2.3 && < 5.3.0)
    Microsoft.Owin (>= 2.0.2)
    Owin (>= 1.0.0)
     
Install-Package Microsoft.Owin.Testing (3.0.1)
Dependencies
        Owin (>= 1.0.0)
        Microsoft.Owin.Hosting (>= 3.0.1)

Install-Package Microsoft.Owin.Diagnostics (3.0.1)
Dependencies
        Owin (>= 1.0.0)
        Microsoft.Owin (>= 3.0.1)

 

Step2.使用 TestServer 建立 Web API

首先,調用 TestServer.Create 搭建服務

[TestInitialize]
public void Setup()
{
    Server = TestServer.Create(app =>
                               {
                                   var startup = new Startup();
                                   startup.Configuration(app);
                                   app.UseErrorPage(); // See Microsoft.Owin.Diagnostics
                                   app.UseWelcomePage("/Welcome"); // See Microsoft.Owin.Diagnostics
                                   var config = new HttpConfiguration();

                                   WebApiConfig.Register(config);

                                   app.UseWebApi(config);
                               });
}

 

然後使用 TestServer.CreateRequest 發送命令

protected virtual async Task<HttpResponseMessage> PostAsync<TModel>(string uri, TModel model)
{
    return await Server.CreateRequest(uri)
                       .And(
                           request =>
                           request.Content =
                           new ObjectContent(typeof(TModel), model, new JsonMediaTypeFormatter()))
                       .PostAsync();
}

protected virtual async Task<HttpResponseMessage> GetAsync(string uri)
{
    return await Server.CreateRequest(uri)
                       .GetAsync();
}

最後將這些方法集結成抽象類別

public abstract class BaseServerTest
{
    protected TestServer Server;


    protected abstract string Password { get; set; }

    protected abstract string Username { get; set; }

    [TestInitialize]
    public void Setup()
    {
       
        Server = TestServer.Create(app =>
                                   {
                                       var startup = new Startup();
                                       startup.Configuration(app);
                                       app.UseErrorPage(); // See Microsoft.Owin.Diagnostics
                                       app.UseWelcomePage("/Welcome"); // See Microsoft.Owin.Diagnostics
                                       var config = new HttpConfiguration();

                                       WebApiConfig.Register(config);

                                       app.UseWebApi(config);
                                   });
    }

    [TestCleanup]
    public void Teardown()
    {
        if (Server != null)
        {
            Server.Dispose();
        }
    }

    protected virtual async Task<HttpResponseMessage> GetAsync(string uri)
    {
        return await Server.CreateRequest(uri)
                           .GetAsync();
    }

    protected virtual async Task<HttpResponseMessage> PostAsync<TModel>(string uri, TModel model)
    {
        return await Server.CreateRequest(uri)
                           .And(
                               request =>
                               request.Content =
                               new ObjectContent(typeof(TModel), model, new JsonMediaTypeFormatter()))
                           .PostAsync();
    }

    protected virtual async Task<HttpResponseMessage> GetAsync(string uri, string accessToken)
    {
        return await Server.CreateRequest(uri)
                           .AddHeader("Authorization", "Bearer " + accessToken)
                           .GetAsync();
    }

    protected virtual async Task<HttpResponseMessage> PostAsync<TModel>(string uri, TModel model, string accessToken)
    {
        return await Server.CreateRequest(uri)
                           .AddHeader("Authorization", "Bearer " + accessToken)
                           .And(
                               request =>
                               request.Content =
                               new ObjectContent(typeof(TModel), model, new JsonMediaTypeFormatter()))
                           .PostAsync();
    }
}

Step3.加入測試方法

建立一個實作 BaseServerTest 的 AccountApiControllerTest 類,測試方法調用 Web API

測試註冊帳號

[TestMethod]
public async Task Register_Test()
{
    var model = new RegisterBindingModel
    {
        Email = Username,
        Password = Password,
        ConfirmPassword = Password
    };

    var response = await PostAsync(s_baseUri + "/Register", model);
    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}

 

測試OAuth登入

[TestMethod]
public async Task Login_Bearer_Test()
{
    var model = new RegisterBindingModel
    {
        Email = Username,
        Password = Password,
        ConfirmPassword = Password
    };

    await PostAsync(s_baseUri + "/Register", model);

    var form = new Dictionary<string, string>
    {
        {"grant_type", "password"},
        {"username", Username},
        {"password", Password}
    };


    var content = new FormUrlEncodedContent(form);
    var response =
        Server.HttpClient.PostAsync("/oauth/token", content).Result;

    var token = response.Content.ReadAsAsync<Token>(new[] {new JsonMediaTypeFormatter()}).Result;

    response.StatusCode.Should().Be(HttpStatusCode.OK);
    token.AccessToken.Should().NotBeNullOrEmpty();
}

結論

原本我也是使用 PostMan | Fiddler 測試 Web API,寫好的腳本,仍然可以納入版控,不過我一直有個問題,『自動測試』,我不知道如何運行自動測試

現在,在一行 Code 都不需要修改的情況之下,我引入了 TestServer,把原本使用 Fiddler 測試改用 TestServer + Unit Test 測試,大大的提升產品的穩定度,讓測試專案能在版控伺服器裡面被追蹤,也能讓 CI Server 運行自動測試

參考資源

https://blogs.msdn.microsoft.com/webdev/2013/11/26/unit-testing-owin-applications-using-testserver/
http://www.aaron-powell.com/posts/2014-01-12-integration-testing-katana-with-auth.html

專案連結

https://dotblogsamples.codeplex.com/SourceControl/latest#Simple.OAuthServer/

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo