miso_soup3 Blog

主に ASP.NET 関連について書いています。

ASP.NET Web API のテスト

ASP.NET Web API ではルーティングが少し特殊なため、(特殊ではなく RESTful に沿っているだけですが)オレオレな API を定義してルーティングをカスタマイズすると、
すぐに ”マッチするアクションメソッドが複数あります”等のエラーが起きます。

API なのでエンドポイントを強固で正確なものにしたいため、
Writing tests for an ASP.NET Web API service を参考にしてテストを書きました。
(リンク先では三種類のテスト方法が記載されています。)

大雑把にまとめると以下の通りです。

  • SelfHost は使用しない
  • Unity を使って外部コンポーネントを利用しているので、Moq で置き換える。
  • DpendencyResolver は Unity.WebAPI を利用。
テスト対象となる ApiController
namespace WebApiTestTrain.Controllers
{
    public class PersonsController : ApiController
    {
        public PersonsController(IPersonService personService)
        {
            _personService = personService;
        }

        private IPersonService _personService;

        [HttpGet]
        public IEnumerable<Person> FindAll()
        {
            return _personService.FindAll();
        }
    }
}
テストコード
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web.Http;
using Microsoft.Practices.Unity;
using Moq;
using NUnit.Framework;
using WebApiTestTrain.Models;

namespace Test
{   
    [TestFixture]
    public class PersonApiTest
    {
        //IPersonServiceのモック
        Mock<IPersonService> personServiceMock = new Mock<IPersonService>();

        [Test]
        public void FindAll_Test()
        {
            var config = new HttpConfiguration();

            //WebApiTestTrain プロジェクトの API をテストしたい。
            //HttpConfiguration に、対象プロジェクトの設定を反映させる。(ルーティングとかフォーマッタとか)
            WebApiTestTrain.WebApiConfig.Register(config);
            //Unityを使い、モックが使用されるようにDpendencyResolverを設定する。
            UnitySetup(config);

            //IPersonServiceが返す値を設定する。
            var persons = new Person[] { new Person() { Id = 1 }, new Person() { Id = 2 } };
            personServiceMock.Setup(e =>
                e.FindAll())
                .Returns(persons);
   
            var httpServer = new HttpServer(config);
            using (var client = new HttpMessageInvoker(httpServer))
            {
                using (var request = 
                    new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/persons/"))
                using (var response = client.SendAsync(request, CancellationToken.None).Result)
                {
                    //検証
                    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);

                    //値の検証
                    var actual = response.Content.ReadAsAsync<IEnumerable<Person>>().Result;
                    Assert.AreEqual(2, actual.Count());
                }
            }

        }

        /// <summary>
        /// Unityを使いDpendencyResolverを設定します。
        /// </summary>
        /// <param name="config"></param>
        private void UnitySetup(HttpConfiguration config)
        {
            var container = new UnityContainer();

            //IPersonServiceは、モックのインスタンスが使用されるよう登録する。
            container.RegisterInstance<IPersonService>(personServiceMock.Object);

            //IDependencyResolverを登録
            config.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
        }
    }
}

煩雑なコードになりましたが、実際はこんな風にまとめています。

ASP.NET Web API は本当に柔軟です…。
ここまでくるとテストというよりは、Web API の別の形のように見えます。