「ASP.NET Web API/NUnitでユニットテストを行う」の版間の差分

提供:MonoBook
編集の要約なし
1行目: 1行目:
ASP.NET Web APIのユニットテストを[[NUnit]]で行えると[[Xamarin Studio]]での開発が捗る。
[[ASP.NET Web API]]の[[ユニットテスト]]を[[NUnit]]で行えると[[Xamarin Studio]]での[[開発]]が捗る。


==概要==
==概要==
[[Xamarin.iOS]]や[[Xamarin.Android]]は[[ユーザーインターフェース]]と[[ビジネスロジック]]の分離が売り文句である。
[[Xamarin.iOS]]や[[Xamarin.Android]]は[[ユーザーインターフェース]]と[[ビジネスロジック]]の分離が売り文句である。
環境依存のユーザーインターフェイスと、非依存のビジネスロジック。
動作環境に依存する[[ユーザーインターフェース]]と、非依存しない[[ビジネスロジック]]という考えである。


しかし実際のところ猫も杓子も[[クラウドコンピューティング]]を叫ぶご時世であり、核となる[[ビジネスロジック]][[サーバー]]上に実装される案件ばかりである。そうなると[[ビジネスロジック]][[ASP.NET Web API]]を用いて記述し[[JSON]][[XML]][[データ]]のやりとりするのがもっとも手軽な実装方法であると思われる。
しかしならが、実際のところ猫も杓子も[[クラウドコンピューティング]]を叫ぶご時世であり、また[[オンプレミス]]の[[社内システム]]も[[スタンドアローン]]で動くものは皆無である。[[iPhone]][[Android]]のアプリのような[[リッチクライアント]]を用いる場合でも、核となる[[ビジネスロジック]][[サーバー]]上に[[Web API]]として実装される案件ばかりである。


このような状況下ではクライアントもサーバーも[[Xamarin Studio]]で一元的に開発を行えると桁違いに捗り、[[Xamarin Studio]]に統合されている[[NUnit]]が活躍する。
そうなると[[Xamarin]]ホゲホゲを使った[[リッチクライアント]]に対向する[[ビジネスロジック]]は[[ASP.NET Web API]]を用いて記述し、[[JSON]]や[[XML]]で[[データ]]のやりとりするのがもっとも手軽な実装方法であると思われ、このような状況下では[[クライアント]]も[[サーバー]]も[[Xamarin Studio]]で一元的に開発を行えると桁違いに捗り、[[Xamarin Studio]]に統合されている[[NUnit]]が活躍すること間違い無しである。


==実装1==
==実装1==
1. 空のASP.NETプロジェクトを作る
ASP.NET Web APIのユニットテストはAPIコントローラーの各メソッドをNUnitから直接呼び出す方法が手っ取り早い。
*NuGetからASP.NET Web APIを入れる


2. NUnitライブラリプロジェクトを作る
しかしながら、[[Basic認証]]などのテストをしたい場合などもあるので、ここではインメモリサーバーを使用した方法を示す。
*NuGetからASP.NET Web APIを入れる
*ASP.NETプロジェクトを参照する


3. インメモリサーバーの準備をする
===手順1===
*なんでもいいのでNUnitプロジェクトからASP.NETプロジェクトのメソッドを呼び出す。
空のAPS.NETプロジェクトを作成する。
*NUnitライブラリプロジェクトにインメモリサーバー関連のコードを記述する
*ASP.NET Web APIをNuGetから入れる


4. おわり
動作検証用のAPIコントローラーを作る。
: Controllers/HelloController.cs
<source lang="csharp">
using System.Web.Http;
 
namespace SampleWebApi.Controllers
{
    public class HelloController : ApiController
    {
        public string Get()
        {
            return "hello";
        }
 
        public string Post([FromBody] string name)
        {
            return "hello, " + name;
        }
    }
}
</source>
 
===手順2===
NUnitライブラリプロジェクトを作る。
*ASP.NET Web APIをNuGetから入れる
*手順1のASP.NETプロジェクトを参照する
**プロジェクトツリーの「参照」を右クリックし参照アセンブリの編集を選ぶ
**Edit Referencesダイアログが開くので「Projects」タブを選ぶ
**ソリューション内のプロジェクトの一覧が表示されるので手順1で作ったプロジェクトにチェックを入れる
 
===手順3===
インメモリサーバーの準備をする。
 
なんでもいいのでNUnitライブラリプロジェクトからASP.NETプロジェクトのメソッドを呼び出す。
これがとても重要。重要。
なんでもいいがASP.NETプロジェクトにWebApiを初期化するコードを静的クラス・静的メソッドとして用意し、両方のプロジェクトで共有すると捗る。
 
WebApiを初期化するコードの例
:App_Startup/WebApiConfig.cs
<source lang="csharp">
using System.Web.Http;
 
namespace SampleWebApi
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Attribute routing.
            config.MapHttpAttributeRoutes();
 
            // Convention-based routing.
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}
</source>
 
ASP.NETプロジェクトはGlobal.asax.csから呼び出す。
<source lang="csharp">
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Http;
 
namespace SampleWebApi
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }
}
</source>


NUnitプロジェクトはTestFixtureSetUp属性を付けたメソッドから呼び出す。
<source lang="csharp">
<source lang="csharp">
        [TestFixtureSetUp]
        public void Setup()
        {
            var config = new HttpConfiguration();
            SampleWebApi.WebApiConfig.Register(config); // ←←←これ重要
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
        }
</source>
</source>
===手順4===
NUnitライブラリプロジェクトにインメモリサーバー関連のコードを記述する。
<source lang="csharp">
using NUnit.Framework;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Formatting;
using System.Web.Http;
namespace SampleWebApi.Tests
{
    [TestFixture]
    public class InMemoryTests
    {
        private const string UrlBase = "http://127.0.0.1:8080/";
        private HttpServer _server;
        [TestFixtureSetUp]
        public void Setup()
        {
            var config = new HttpConfiguration();
            // ASP.NETプロジェクトの何かしらのメソッドを呼び出す。
            // 初期化コードを共通化しておくのが間違いないと思われる。
            SampleWebApi.WebApiConfig.Register(config);
            // エラーをいっぱいだす
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
            // 重要
            // ASP.NETプロジェクトの何かしらのメソッドを呼び出した後にインメモリサーバーのインスタンスを作ること
            _server = new HttpServer(config);
        }
           
        public void Dispose()
        {
            if (_server != null)
            {
                _server.Dispose();
                _server = null;
            }
        }
        [Test]
        public void Get()
        {
            using (var client = new HttpClient(_server))
            {
                var request = CreateRequest(
                    "api/Hello",
                    "application/json",
                    HttpMethod.Get);
                using (var response = client.SendAsync(request).Result)
                {
                    Assert.IsNotNull(response);
                    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                    Assert.NotNull(response.Content);
                }
            }
        }
        [Test]
        public async void Post()
        {
            using (var client = new HttpClient(_server))
            {
                var request = CreateRequest(
                    "api/Hello",
                    "application/json",
                    HttpMethod.Post,
                    "monobook" ,
                    new JsonMediaTypeFormatter());
                using (var response = client.SendAsync(request).Result)
                {
                    Assert.IsNotNull(response);
                    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                    Assert.NotNull(response.Content);
                }
            }
        }
        private HttpRequestMessage CreateRequest(string url, string mthv, HttpMethod method)
        {
            var request = new HttpRequestMessage();
            request.RequestUri = new Uri(UrlBase + url);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mthv));
            request.Method = method;
            return request;
        }
        private HttpRequestMessage CreateRequest<T>(string url, string mthv, HttpMethod method, T content, MediaTypeFormatter formatter) where T : class
        {
            HttpRequestMessage request = CreateRequest(url, mthv, method);
            request.Content = new ObjectContent<T>(content, formatter);
            return request;
        }
    }
}
</source>
===おわり===


==関連項目==
==関連項目==

2015年8月19日 (水) 02:36時点における版

ASP.NET Web APIユニットテストNUnitで行えるとXamarin Studioでの開発が捗る。

概要

Xamarin.iOSXamarin.Androidユーザーインターフェースビジネスロジックの分離が売り文句である。 動作環境に依存するユーザーインターフェースと、非依存しないビジネスロジックという考えである。

しかしならが、実際のところ猫も杓子もクラウドコンピューティングを叫ぶご時世であり、またオンプレミス社内システムスタンドアローンで動くものは皆無である。iPhoneAndroidのアプリのようなリッチクライアントを用いる場合でも、核となるビジネスロジックサーバー上にWeb APIとして実装される案件ばかりである。

そうなるとXamarinホゲホゲを使ったリッチクライアントに対向するビジネスロジックASP.NET Web APIを用いて記述し、JSONXMLデータのやりとりするのがもっとも手軽な実装方法であると思われ、このような状況下ではクライアントサーバーXamarin Studioで一元的に開発を行えると桁違いに捗り、Xamarin Studioに統合されているNUnitが活躍すること間違い無しである。

実装1

ASP.NET Web APIのユニットテストはAPIコントローラーの各メソッドをNUnitから直接呼び出す方法が手っ取り早い。

しかしながら、Basic認証などのテストをしたい場合などもあるので、ここではインメモリサーバーを使用した方法を示す。

手順1

空のAPS.NETプロジェクトを作成する。

  • ASP.NET Web APIをNuGetから入れる

動作検証用のAPIコントローラーを作る。

Controllers/HelloController.cs
using System.Web.Http;

namespace SampleWebApi.Controllers
{
    public class HelloController : ApiController
    {
        public string Get()
        {
            return "hello";
        }

        public string Post([FromBody] string name)
        {
            return "hello, " + name;
        }
    }
}

手順2

NUnitライブラリプロジェクトを作る。

  • ASP.NET Web APIをNuGetから入れる
  • 手順1のASP.NETプロジェクトを参照する
    • プロジェクトツリーの「参照」を右クリックし参照アセンブリの編集を選ぶ
    • Edit Referencesダイアログが開くので「Projects」タブを選ぶ
    • ソリューション内のプロジェクトの一覧が表示されるので手順1で作ったプロジェクトにチェックを入れる

手順3

インメモリサーバーの準備をする。

なんでもいいのでNUnitライブラリプロジェクトからASP.NETプロジェクトのメソッドを呼び出す。 これがとても重要。重要。 なんでもいいがASP.NETプロジェクトにWebApiを初期化するコードを静的クラス・静的メソッドとして用意し、両方のプロジェクトで共有すると捗る。

WebApiを初期化するコードの例

App_Startup/WebApiConfig.cs
using System.Web.Http;

namespace SampleWebApi
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Attribute routing.
            config.MapHttpAttributeRoutes();

            // Convention-based routing.
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

ASP.NETプロジェクトはGlobal.asax.csから呼び出す。

using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Http;

namespace SampleWebApi
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }
}

NUnitプロジェクトはTestFixtureSetUp属性を付けたメソッドから呼び出す。

        [TestFixtureSetUp]
        public void Setup()
        {
            var config = new HttpConfiguration();
            SampleWebApi.WebApiConfig.Register(config); // ←←←これ重要
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
        }

手順4

NUnitライブラリプロジェクトにインメモリサーバー関連のコードを記述する。

using NUnit.Framework;
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Formatting;
using System.Web.Http;

namespace SampleWebApi.Tests
{
    [TestFixture]
    public class InMemoryTests
    {
        private const string UrlBase = "http://127.0.0.1:8080/";

        private HttpServer _server;

        [TestFixtureSetUp]
        public void Setup()
        {
            var config = new HttpConfiguration();
            // ASP.NETプロジェクトの何かしらのメソッドを呼び出す。
            // 初期化コードを共通化しておくのが間違いないと思われる。
            SampleWebApi.WebApiConfig.Register(config);
            // エラーをいっぱいだす
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;

            // 重要
            // ASP.NETプロジェクトの何かしらのメソッドを呼び出した後にインメモリサーバーのインスタンスを作ること
            _server = new HttpServer(config);
        }
            
        public void Dispose()
        {
            if (_server != null)
            {
                _server.Dispose();
                _server = null;
            }
        }

        [Test]
        public void Get()
        {
            using (var client = new HttpClient(_server))
            {
                var request = CreateRequest(
                    "api/Hello", 
                    "application/json", 
                    HttpMethod.Get);

                using (var response = client.SendAsync(request).Result)
                {
                    Assert.IsNotNull(response);
                    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                    Assert.NotNull(response.Content);
                }
            }
        }

        [Test]
        public async void Post()
        {
            using (var client = new HttpClient(_server))
            {
                var request = CreateRequest(
                    "api/Hello", 
                    "application/json", 
                    HttpMethod.Post, 
                    "monobook" ,
                    new JsonMediaTypeFormatter());

                using (var response = client.SendAsync(request).Result)
                {
                    Assert.IsNotNull(response);
                    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                    Assert.NotNull(response.Content);
                }
            }
        }

        private HttpRequestMessage CreateRequest(string url, string mthv, HttpMethod method)
        {
            var request = new HttpRequestMessage();
            request.RequestUri = new Uri(UrlBase + url);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mthv));
            request.Method = method;
            return request;
        }

        private HttpRequestMessage CreateRequest<T>(string url, string mthv, HttpMethod method, T content, MediaTypeFormatter formatter) where T : class
        {
            HttpRequestMessage request = CreateRequest(url, mthv, method);
            request.Content = new ObjectContent<T>(content, formatter);
            return request;
        }
    }
}

おわり

関連項目

参考文献