Asp.NetWebAPIユニットテスト
22322 ワード
今Netwebapiの運用はますます多くなり、そのユニットもますます重要になっています.一般的なソフトウェア開発は多層構造であり,上層は下層のインタフェースを呼び出すが,各層の実装者は異なり,一般的には自己対応ユニットテストのみを書く.下層への依存はIOCを通じて行います.まず、Controllerの定義と実装を見てみましょう.
まず、コンストラクション関数にはIArticleServiceインスタンスが必要であり、Controllerのactionでこのサービスが必要になります.【ここのコントロールはとても簡単で、ユニットテストは不要だと言わないでください.実際のプロジェクトでは、コントロールラーのユニットテストをしないために、その中のロジックをすべて抽出してHelperの中に入れて、Helperにユニットテストをする人がいます】
IOCの実現:
ここではUnityを使っているので、まずInstall-Package Unityをインストールする必要があります.WebApi.5.1
一般的に我々のコントローラはDependencyResolverとIHttpControllerActivvatorのみで制御反転を実現している.
ユニットテストは普通Moqとnunitを使うので、Install-Package MoqとInstall-Package NUnitが必要です.
ユニットテストのcodeは以下の通りです.
コントローラのインスタンスの場合はIArticleServiceインスタンスを直接渡します.
WebApiは一般的にインタフェースを提供するために使用されるため、使用者はhttpリクエストを送信してデータを取得することが多い.このような方法でユニットテストを行うこともできます.
では、このウェブプログラムをどのように構築し、IOCをどのように使用するか、
ここでIOCはAutofacを使用しており、そのパッケージは以下の通りです.
Install-Package OwinInstall-Package Microsoft.AspNet.WebApi.OwinInstall-Package Microsoft.Owin.Host.HttpListenerInstall-Package Microsoft.Owin.HostingInstall-Package AutofacInstall-Package Autofac.WebApi2
参照先:
ASP.NET Web API Unit Testing
Net 4.5でのHttpClient試用
ダウンロード
public class ArticlesController : ApiController
{
private IArticleService _articleService;
public ArticlesController(IArticleService articleService)
{
_articleService = articleService;
}
// GET: api/Articles
public IEnumerable<Article> GetArticles()
{
return _articleService.GetArticles();
}
// GET: api/Articles/5
[ResponseType(typeof(Article))]
public IHttpActionResult GetArticle(int id)
{
Article article = _articleService.GetArticle(id);
if (article == null)
{
return NotFound();
}
return Ok(article);
}
// PUT: api/Articles/5
[ResponseType(typeof(void))]
public IHttpActionResult PutArticle(int id, Article article)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != article.ID)
{
return BadRequest();
}
try
{
_articleService.UpdateArticle(article);
}
catch (DbUpdateConcurrencyException)
{
if (!ArticleExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/Articles
[ResponseType(typeof(Article))]
public IHttpActionResult PostArticle(Article article)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_articleService.CreateArticle(article);
return CreatedAtRoute("DefaultApi", new { id = article.ID }, article);
}
// DELETE: api/Articles/5
[ResponseType(typeof(Article))]
public IHttpActionResult DeleteArticle(int id)
{
Article article = _articleService.GetArticle(id);
if (article == null)
{
return NotFound();
}
_articleService.DeleteArticle(article);
return Ok(article);
}
private bool ArticleExists(int id)
{
return _articleService.GetArticle(id) != null;
}
}
まず、コンストラクション関数にはIArticleServiceインスタンスが必要であり、Controllerのactionでこのサービスが必要になります.【ここのコントロールはとても簡単で、ユニットテストは不要だと言わないでください.実際のプロジェクトでは、コントロールラーのユニットテストをしないために、その中のロジックをすべて抽出してHelperの中に入れて、Helperにユニットテストをする人がいます】
IOCの実現:
ここではUnityを使っているので、まずInstall-Package Unityをインストールする必要があります.WebApi.5.1
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
//Install-Package Unity.WebApi.5.1
IUnityContainer container = new UnityContainer()
.RegisterType<IArticleService, ArticleService>()
.RegisterType<IBlogService, BlogService>();
//GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new UnityHttpControllerActivator(container));
}
}
public class UnityHttpControllerActivator : IHttpControllerActivator
{
public IUnityContainer UnityContainer { get; private set; }
public UnityHttpControllerActivator(IUnityContainer unityContainer)
{
this.UnityContainer = unityContainer;
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
return (IHttpController)this.UnityContainer.Resolve(controllerType);
}
}
一般的に我々のコントローラはDependencyResolverとIHttpControllerActivvatorのみで制御反転を実現している.
ユニットテストは普通Moqとnunitを使うので、Install-Package MoqとInstall-Package NUnitが必要です.
ユニットテストのcodeは以下の通りです.
public void TestPostArticle()
{
var article = new Article
{
Title = "Web API Unit Testing",
URL = "https://chsakell.com/web-api-unit-testing",
Author = "Chris Sakellarios",
DateCreated = DateTime.Now,
Contents = "Unit testing Web API.."
};
var _articlesController = new ArticlesController(_articleService)
{
Configuration = new HttpConfiguration(),
Request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("http://localhost/api/articles")
}
};
var result = _articlesController.PostArticle(article) as CreatedAtRouteNegotiatedContentResult<Article>;
Assert.That(result.RouteName, Is.EqualTo("DefaultApi"));
Assert.That(result.Content.ID, Is.EqualTo(result.RouteValues["id"]));
Assert.That(result.Content.ID, Is.EqualTo(_randomArticles.Max(a => a.ID)));
}
コントローラのインスタンスの場合はIArticleServiceインスタンスを直接渡します.
public static IArticleService GetIArticleService()
{
var _article = new Mock<IArticleService>();
_article.Setup(x => x.GetArticles(It.IsAny<string>())).Returns(new Func<string, List<Article>>(name =>
{
if (string.IsNullOrEmpty(name))
{
return _randomArticles;
}
else
{
return _randomArticles.FindAll(x => x.Title.Contains(name));
}
}));
_article.Setup(x => x.GetArticle(It.IsAny<int>())).Returns(new Func<int, Article>(id =>
{
return _randomArticles.Find(x => x.ID == id);
}));
_article.Setup(x => x.GetArticle(It.IsAny<string>())).Returns(new Func<string, Article>(name =>
{
return _randomArticles.Find(x => x.Title == name);
}));
_article.Setup(r => r.CreateArticle(It.IsAny<Article>()))
.Callback(new Action<Article>(newArticle =>
{
newArticle.DateCreated = DateTime.Now;
newArticle.ID = _randomArticles.Last().ID + 1;
_randomArticles.Add(newArticle);
}));
_article.Setup(r => r.UpdateArticle(It.IsAny<Article>()))
.Callback(new Action<Article>(x =>
{
var oldArticle = _randomArticles.Find(a => a.ID == x.ID);
oldArticle.DateEdited = DateTime.Now;
oldArticle.URL = x.URL;
oldArticle.Title = x.Title;
oldArticle.Contents = x.Contents;
oldArticle.BlogID = x.BlogID;
}));
return _article.Object;
}
WebApiは一般的にインタフェースを提供するために使用されるため、使用者はhttpリクエストを送信してデータを取得することが多い.このような方法でユニットテストを行うこともできます.
public void TestPostArticle2()
{
var article = new Article
{
Title = "Web API Unit Testing2",
URL = "https://chsakell.com/web-api-unit-testing",
Author = "Chris Sakellarios",
DateCreated = DateTime.Now,
Contents = "Unit testing Web API.."
};
var address = "http://localhost:9000/";
using (WebApp.Start<Startup>(address))
{
HttpClient _client = new HttpClient();
var response = _client.PostAsJsonAsync<Article>(address + "api/articles/", article).Result;
var result = response.Content.ReadAsAsync<Article>().Result;
Assert.That(result.Title, Is.EqualTo(article.Title));
Assert.That(_randomArticles.Last().Title, Is.EqualTo(article.Title));
}
}
では、このウェブプログラムをどのように構築し、IOCをどのように使用するか、
ここでIOCはAutofacを使用しており、そのパッケージは以下の通りです.
Install-Package OwinInstall-Package Microsoft.AspNet.WebApi.OwinInstall-Package Microsoft.Owin.Host.HttpListenerInstall-Package Microsoft.Owin.HostingInstall-Package AutofacInstall-Package Autofac.WebApi2
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
// config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var builder = new ContainerBuilder();
builder.RegisterApiControllers(typeof(ArticlesController).Assembly);
var _articleService = Helper.GetIArticleService();
builder.RegisterInstance(_articleService).As<IArticleService>();
IContainer container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
appBuilder.UseWebApi(config);
}
}
参照先:
ASP.NET Web API Unit Testing
Net 4.5でのHttpClient試用
ダウンロード