今天來到了最后的壓軸章節(jié):單元測試
十年的安溪網(wǎng)站建設經(jīng)驗,針對設計、前端、開發(fā)、售后、文案、推廣等六對一服務,響應快,48小時及時工作處理。成都營銷網(wǎng)站建設的優(yōu)勢是能夠根據(jù)用戶設備顯示端的尺寸不同,自動調(diào)整安溪建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)建站從事“安溪網(wǎng)站設計”,“安溪網(wǎng)站推廣”以來,每個客戶項目都認真落實執(zhí)行。
我們已經(jīng)有了完整的程序結(jié)構(gòu),現(xiàn)在是時候來對我們的組件做單元測試了。
在UnitTestingWebAPI.Tests類庫上添加UnitTestingWebAPI.Domain, UnitTestingWebAPI.Data, UnitTestingWebAPI.Service和UnitTestingWebAPI.API.Core 同樣要安裝下列的Nuget 包:
Entity Framework
Microsoft.AspNet.WebApi.Core
Microsoft.AspNet.WebApi.Client
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.WebApi.SelfHost
Micoroft.Owin
Owin
Micoroft.Owin.Hosting
Micoroft.Owin.Host.HttpListener
Autofac.WebApi2
NUnit
NUnitTestAdapter
從清單中可知,我們將用NUnit 來寫單元測試
Services 單元測試
寫單元測試的第一件事是需要去設置或初始化一些單元測試中要用到的變量,NUnit框架則給要測試的方法添加Setup特性,在任何其他的NUnit測試開始之前,這一方法會先執(zhí)行,把Services層注入到Controller的構(gòu)造函數(shù)之后的第一件事就是進行單元測試。因此在對WebAPI進行單元測試之前需要仿造Repositories和Service。
在這個例子中會看到如何仿造ArticleService, 并在這個Service的構(gòu)造函數(shù)中注入IArticleRepository和IUnitOfWork,所以我們需要創(chuàng)建兩個"特別的"實例來注入。
ArticleService Constructor
private readonly IArticleRepository articlesRepository;
private readonly IUnitOfWork unitOfWork;
public ArticleService(IArticleRepository articlesRepository, IUnitOfWork unitOfWork)
{
this.articlesRepository = articlesRepository;
this.unitOfWork = unitOfWork;
}這里的"特別的",是因為這些實例不是真正訪問數(shù)據(jù)庫的實例.
注意
單元測試必須運行在內(nèi)存中并且不應該訪問數(shù)據(jù)庫. 所有核心的方法必須通過像我們的例子中用Mock這樣的框架仿造。這個方式自動的測試會更快些。單元測試最基本的目的是更多的測試組件的行為,而不是真正的結(jié)果.
開始測試ArticleService,創(chuàng)建一個ServiceTests的文件并添加下列代碼:
[TestFixture]
public class ServicesTests
{
#region Variables
IArticleService _articleService;
IArticleRepository _articleRepository;
IUnitOfWork _unitOfWork;
List<Article> _randomArticles;
#endregion
#region Setup
[SetUp]
public void Setup()
{
_randomArticles = SetupArticles();
_articleRepository = SetupArticleRepository();
_unitOfWork = new Mock<IUnitOfWork>().Object;
_articleService = new ArticleService(_articleRepository, _unitOfWork);
}
public List<Article> SetupArticles()
{
int _counter = new int();
List<Article> _articles = BloggerInitializer.GetAllArticles();
foreach (Article _article in _articles)
_article.ID = ++_counter;
return _articles;
}
public IArticleRepository SetupArticleRepository()
{
// Init repository
var repo = new Mock<IArticleRepository>();
// Setup mocking behavior
repo.Setup(r => r.GetAll()).Returns(_randomArticles);
repo.Setup(r => r.GetById(It.IsAny<int>()))
.Returns(new Func<int, Article>(
id => _randomArticles.Find(a => a.ID.Equals(id))));
repo.Setup(r => r.Add(It.IsAny<Article>()))
.Callback(new Action<Article>(newArticle =>
{
dynamic maxArticleID = _randomArticles.Last().ID;
newArticle.ID = maxArticleID + 1;
newArticle.DateCreated = DateTime.Now;
_randomArticles.Add(newArticle);
}));
repo.Setup(r => r.Update(It.IsAny<Article>()))
.Callback(new Action<Article>(x =>
{
var oldArticle = _randomArticles.Find(a => a.ID == x.ID);
oldArticle.DateEdited = DateTime.Now;
oldArticle = x;
}));
repo.Setup(r => r.Delete(It.IsAny<Article>()))
.Callback(new Action<Article>(x =>
{
var _articleToRemove = _randomArticles.Find(a => a.ID == x.ID);
if (_articleToRemove != null)
_randomArticles.Remove(_articleToRemove);
}));
// Return mock implementation
return repo.Object;
}
#endregion
}如果你直接copy代碼可能會報錯:
One or more types required to compile a dynaic expression ....
解決辦法:
在Assembiles中添加Microsoft.CSharp.dll

在SetupArticleRepository()方法中我們模仿了_articleRepository的行為,換句話說,當一個特定的方法使用了這個Reporistory的實例,就會得到我們所期待的結(jié)果。然后我們在_articleService的構(gòu)造函數(shù)中注入這個實例。我們用下面代碼測試_articleService.GetArticles()的行為是否是我們所期待的.
ServiceShouldReturnAllArticles Test
[Test]
public void ServiceShouldReturnAllArticles()
{
var articles = _articleService.GetArticles();
NUnit.Framework.Assert.That(articles, Is.EqualTo(_randomArticles));
}編譯項目,運行測試,要確保這個測試變?yōu)榫G色通過狀態(tài),用同樣的方式創(chuàng)建下面的測試:
Services Test
[Test]
public void ServiceShouldReturnRightArticle()
{
var wcfSecurityArticle = _articleService.GetArticle(2);
NUnit.Framework.Assert.That(wcfSecurityArticle,
Is.EqualTo(_randomArticles.Find(a => a.Title.Contains("Secure WCF Services"))));
}
[Test]
public void ServiceShouldAddNewArticle()
{
var _newArticle = new Article()
{
Author = "Chris Sakellarios",
Contents = "If you are an ASP.NET MVC developer, you will certainly..",
Title = "URL Rooting in ASP.NET (Web Forms)",
URL = "https://chsakell.com/2013/12/15/url-rooting-in-asp-net-web-forms/"
};
int _maxArticleIDBeforeAdd = _randomArticles.Max(a => a.ID);
_articleService.CreateArticle(_newArticle);
NUnit.Framework.Assert.That(_newArticle, Is.EqualTo(_randomArticles.Last()));
NUnit.Framework.Assert.That(_maxArticleIDBeforeAdd + 1, Is.EqualTo(_randomArticles.Last().ID));
}
[Test]
public void ServiceShouldUpdateArticle()
{
var _firstArticle = _randomArticles.First();
_firstArticle.Title = "OData feat. ASP.NET Web API"; // reversed<img draggable="false" class="emoji" alt="" src="https://s.w.org/p_w_picpaths/core/emoji/2/svg/1f642.svg">
_firstArticle.URL = "http://t.co/fuIbNoc7Zh"; // short link
_articleService.UpdateArticle(_firstArticle);
NUnit.Framework.Assert.That(_firstArticle.DateEdited, Is.Not.EqualTo(DateTime.MinValue));
NUnit.Framework.Assert.That(_firstArticle.URL, Is.EqualTo("http://t.co/fuIbNoc7Zh"));
NUnit.Framework.Assert.That(_firstArticle.ID, Is.EqualTo(1)); // hasn't changed
}
[Test]
public void ServiceShouldDeleteArticle()
{
int maxID = _randomArticles.Max(a => a.ID); // Before removal
var _lastArticle = _randomArticles.Last();
// Remove last article
_articleService.DeleteArticle(_lastArticle);
NUnit.Framework.Assert.That(maxID, Is.GreaterThan(_randomArticles.Max(a => a.ID))); // Max reduced by 1
}WebAPI 控制器單元測試
在熟悉了偽造Services行為測試的基礎(chǔ)上,來進行WebAPI控制器的單元測試。
第一件事:設置在測試中需要的變量。
用下面的代碼創(chuàng)建用于測試的控制器:
[TestFixture]
public class ControllerTests
{
#region Variables
IArticleService _articleService;
IArticleRepository _articleRepository;
IUnitOfWork _unitOfWork;
List<Article> _randomArticles;
#endregion
#region Setup
[SetUp]
public void Setup()
{
_randomArticles = SetupArticles();
_articleRepository = SetupArticleRepository();
_unitOfWork = new Mock<IUnitOfWork>().Object;
_articleService = new ArticleService(_articleRepository, _unitOfWork);
}
/// <summary>
/// Setup Articles
/// </summary>
/// <returns></returns>
public List<Article> SetupArticles()
{
int _counter = new int();
List<Article> _articles = BloggerInitializer.GetAllArticles();
foreach (Article _article in _articles)
_article.ID = ++_counter;
return _articles;
}
/// <summary>
/// Emulate _articleRepository behavior
/// </summary>
/// <returns></returns>
public IArticleRepository SetupArticleRepository()
{
// Init repository
var repo = new Mock<IArticleRepository>();
// Get all articles
repo.Setup(r => r.GetAll()).Returns(_randomArticles);
// Get Article by id
repo.Setup(r => r.GetById(It.IsAny<int>()))
.Returns(new Func<int, Article>(
id => _randomArticles.Find(a => a.ID.Equals(id))));
// Add Article
repo.Setup(r => r.Add(It.IsAny<Article>()))
.Callback(new Action<Article>(newArticle =>
{
dynamic maxArticleID = _randomArticles.Last().ID;
newArticle.ID = maxArticleID + 1;
newArticle.DateCreated = DateTime.Now;
_randomArticles.Add(newArticle);
}));
// Update Article
repo.Setup(r => r.Update(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;
}));
// Delete Article
repo.Setup(r => r.Delete(It.IsAny<Article>()))
.Callback(new Action<Article>(x =>
{
var _articleToRemove = _randomArticles.Find(a => a.ID == x.ID);
if (_articleToRemove != null)
_randomArticles.Remove(_articleToRemove);
}));
// Return mock implementation
return repo.Object;
}
#endregion
}控制器的類和其它的類一樣,所以我們可以分開各自測試。下面測試_articlesController.GetArticles(),看看是否能返回所有的文章。
[Test]
public void ControlerShouldReturnAllArticles()
{
var _articlesController = new ArticlesController(_articleService);
var result = _articlesController.GetArticles();
CollectionAssert.AreEqual(result, _randomArticles);
}請確保測試已綠色通過,我們初始化了3條數(shù)據(jù),用_articlesController.GetArticle(3)測試看看能否返回最后一條。
[Test]
public void ControlerShouldReturnLastArticle()
{
var _articlesController = new ArticlesController(_articleService);
var result = _articlesController.GetArticle(3) as OkNegotiatedContentResult<Article>;
Assert.IsNotNull(result);
Assert.AreEqual(result.Content.Title, _randomArticles.Last().Title);
}測試一個無效的Update操作,必須失敗并且返回一個BadRequestResult, 重新調(diào)用設置在_articleRepository上的Update操作。
repo.Setup(r => r.Update(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;
}));所以,當我們測試一個不存在的文章就應該返回失敗信息。
[Test]
public void ControlerShouldPutReturnBadRequestResult()
{
var _articlesController = new ArticlesController(_articleService)
{
Configuration = new HttpConfiguration(),
Request = new HttpRequestMessage
{
Method = HttpMethod.Put,
RequestUri = new Uri("http://localhost/api/articles/-1")
}
};
var badresult = _articlesController.PutArticle(-1, new Article() { Title = "Unknown Article" });
Assert.That(badresult, Is.TypeOf<BadRequestResult>());
}通過分別成功更新第一篇文章、發(fā)表一篇新文章、發(fā)布失敗一篇文章來完成我們的單元測試。
Controller 單元測試
[Test]
public void ControlerShouldPutUpdateFirstArticle()
{
var _articlesController = new ArticlesController(_articleService)
{
Configuration = new HttpConfiguration(),
Request = new HttpRequestMessage
{
Method = HttpMethod.Put,
RequestUri = new Uri("http://localhost/api/articles/1")
}
};
IHttpActionResult updateResult = _articlesController.PutArticle(1, new Article()
{
ID = 1,
Title = "ASP.NET Web API feat. OData",
URL = "http://t.co/fuIbNoc7Zh",
Contents = @"OData is an open standard protocol.."
}) as IHttpActionResult;
Assert.That(updateResult, Is.TypeOf<StatusCodeResult>());
StatusCodeResult statusCodeResult = updateResult as StatusCodeResult;
Assert.That(statusCodeResult.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
Assert.That(_randomArticles.First().URL, Is.EqualTo("http://t.co/fuIbNoc7Zh"));
}
[Test]
public void ControlerShouldPostNewArticle()
{
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")
}
};
_articlesController.Configuration.MapHttpAttributeRoutes();
_articlesController.Configuration.EnsureInitialized();
_articlesController.RequestContext.RouteData = new HttpRouteData(
new HttpRoute(), new HttpRouteValueDictionary { { "_articlesController", "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)));
}
[Test]
public void ControlerShouldNotPostNewArticle()
{
var article = new Article
{
Title = "Web API Unit Testing",
URL = "https://chsakell.com/web-api-unit-testing",
Author = "Chris Sakellarios",
DateCreated = DateTime.Now,
Contents = null
};
var _articlesController = new ArticlesController(_articleService)
{
Configuration = new HttpConfiguration(),
Request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("http://localhost/api/articles")
}
};
_articlesController.Configuration.MapHttpAttributeRoutes();
_articlesController.Configuration.EnsureInitialized();
_articlesController.RequestContext.RouteData = new HttpRouteData(
new HttpRoute(), new HttpRouteValueDictionary { { "Controller", "Articles" } });
_articlesController.ModelState.AddModelError("Contents", "Contents is required field");
var result = _articlesController.PostArticle(article) as InvalidModelStateResult;
Assert.That(result.ModelState.Count, Is.EqualTo(1));
Assert.That(result.ModelState.IsValid, Is.EqualTo(false));
}上面測試的重點,我們請求的幾個方面:返回碼或路由屬性。
管理 Handler單元測試
你可以通過創(chuàng)建HttpMessageInvoker的實例來測試Message Handler, 解析你要測試的Handler實例并調(diào)用SendAsync 方法。創(chuàng)建一個MessageHandlerTest.cs文件,并貼上下面的啟動設置代碼
#region Variables
private EndRequestHandler _endRequestHandler;
private HeaderAppenderHandler _headerAppenderHandler;
#endregion
#region Setup
[SetUp]
public void Setup()
{
// Direct MessageHandler test
_endRequestHandler = new EndRequestHandler();
_headerAppenderHandler = new HeaderAppenderHandler()
{
InnerHandler = _endRequestHandler
};
}
#endregion我們在HeaderAppenderHandler的內(nèi)部設置另外一個可以終止請求的Hanlder.只要Uri中包含一個測試字符,從新調(diào)用EndRequestHandler將會終止請求.現(xiàn)在來測試.
[Test]
public async void ShouldAppendCustomHeader()
{
var invoker = new HttpMessageInvoker(_headerAppenderHandler);
var result = await invoker.SendAsync(new HttpRequestMessage(HttpMethod.Get,
new Uri("http://localhost/api/test/")), CancellationToken.None);
Assert.That(result.Headers.Contains("X-WebAPI-Header"), Is.True);
Assert.That(result.Content.ReadAsStringAsync().Result,
Is.EqualTo("Unit testing message handlers!"));
}假如要做一個集成測試:當一個請求被消息管道分配到Controller的Action的真實behavior。
這將需要運行WebApi,然后運行單元測試。怎么做呢?必須是 通過Self host的模式運行API,然后設置恰當?shù)呐渲谩?/p>
在UnitTestingWebAPI.Tests的項目中添加Startup.cs文件:
Hosting/Startup.cs
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
config.MessageHandlers.Add(new HeaderAppenderHandler());
config.MessageHandlers.Add(new EndRequestHandler());
config.Filters.Add(new ArticlesReversedFilter());
config.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.MapHttpAttributeRoutes();
// Autofac configuration
var builder = new ContainerBuilder();
builder.RegisterApiControllers(typeof(ArticlesController).Assembly);
// Unit of Work
var _unitOfWork = new Mock<IUnitOfWork>();
builder.RegisterInstance(_unitOfWork.Object).As<IUnitOfWork>();
//Repositories
var _articlesRepository = new Mock<IArticleRepository>();
_articlesRepository.Setup(x => x.GetAll()).Returns(
BloggerInitializer.GetAllArticles()
);
builder.RegisterInstance(_articlesRepository.Object).As<IArticleRepository>();
var _blogsRepository = new Mock<IBlogRepository>();
_blogsRepository.Setup(x => x.GetAll()).Returns(
BloggerInitializer.GetBlogs
);
builder.RegisterInstance(_blogsRepository.Object).As<IBlogRepository>();
// Services
builder.RegisterAssemblyTypes(typeof(ArticleService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerRequest();
builder.RegisterInstance(new ArticleService(_articlesRepository.Object, _unitOfWork.Object));
builder.RegisterInstance(new BlogService(_blogsRepository.Object, _unitOfWork.Object));
IContainer container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
appBuilder.UseWebApi(config);
}
}可能注意到和UnitTestingWebAPI.API里的WebSetup類的不同之處在與,這里我們用了假的Repositories和Services。
返回到ControllerTests.cs中。
[Test]
public void ShouldCallToControllerActionAppendCustomHeader()
{
//Arrange
var address = "http://localhost:9000/";
using (WebApp.Start<Startup>(address))
{
HttpClient _client = new HttpClient();
var response = _client.GetAsync(address + "api/articles").Result;
Assert.That(response.Headers.Contains("X-WebAPI-Header"), Is.True);
var _returnedArticles = response.Content.ReadAsAsync<List<Article>>().Result;
Assert.That(_returnedArticles.Count, Is.EqualTo(BloggerInitializer.GetAllArticles().Count));
}
}媒體類型格式化器 測試
我們在UnitTestingWebAPI.API.Core中創(chuàng)建了ArticleFormatter,現(xiàn)在測試一下,應該返回用逗號分割的文章字符串。它只能是寫文章的實例,但不能讀或者明白其它類型的類。為了應用這個格式化器需要設置請求頭信息的Accept為application/article
[TestFixture]
public class MediaTypeFormatterTests
{
#region Variables
Blog _blog;
Article _article;
ArticleFormatter _formatter;
#endregion
#region Setup
[SetUp]
public void Setup()
{
_blog = BloggerInitializer.GetBlogs().First();
_article = BloggerInitializer.GetChsakellsArticles().First();
_formatter = new ArticleFormatter();
}
#endregion
}我們可以創(chuàng)建一個ObjectContent來測試MediaTypeFormatter,傳遞一個對象來檢查是否能被被格式化,如果格式化器不能讀和寫傳遞過去的對象則會拋出異常,例如,文章的格式化器不能識別Blog對象:
[Test]
public void FormatterShouldThrowExceptionWhenUnsupportedType()
{
Assert.Throws<InvalidOperationException>(() => new ObjectContent<Blog>(_blog, _formatter));
}換句話說,傳一個Article對象就一定會通過測試
[Test]
public void FormatterShouldNotThrowExceptionWhenArticle()
{
Assert.DoesNotThrow(() => new ObjectContent<Article>(_article, _formatter));
}用下面的代碼測試不符合MediaType formatter的Media type
Media Type Formatters Unit tests
[Test]
public void FormatterShouldHeaderBeSetCorrectly()
{
var content = new ObjectContent<Article>(_article, new ArticleFormatter());
Assert.That(content.Headers.ContentType.MediaType, Is.EqualTo("application/article"));
}
[Test]
public async void FormatterShouldBeAbleToDeserializeArticle()
{
var content = new ObjectContent<Article>(_article, _formatter);
var deserializedItem = await content.ReadAsAsync<Article>(new[] { _formatter });
Assert.That(_article, Is.SameAs(deserializedItem));
}
[Test]
public void FormatterShouldNotBeAbleToWriteUnsupportedType()
{
var canWriteBlog = _formatter.CanWriteType(typeof(Blog));
Assert.That(canWriteBlog, Is.False);
}
[Test]
public void FormatterShouldBeAbleToWriteArticle()
{
var canWriteArticle = _formatter.CanWriteType(typeof(Article));
Assert.That(canWriteArticle, Is.True);
}路由測試
在不Host Web API的情況下,測試路由配置。為了這個目的,需要一個可以從HttpControllerContext的實例中返回Controllerl類型或Controller中Action的幫助類,在測試之前,先創(chuàng)建一個路由配置的HttpConfiguration
Helpers/ControllerActionSelector.cs
public class ControllerActionSelector
{
#region Variables
HttpConfiguration config;
HttpRequestMessage request;
IHttpRouteData routeData;
IHttpControllerSelector controllerSelector;
HttpControllerContext controllerContext;
#endregion
#region Constructor
public ControllerActionSelector(HttpConfiguration conf, HttpRequestMessage req)
{
config = conf;
request = req;
routeData = config.Routes.GetRouteData(request);
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
controllerSelector = new DefaultHttpControllerSelector(config);
controllerContext = new HttpControllerContext(config, routeData, request);
}
#endregion
#region Methods
public string GetActionName()
{
if (controllerContext.ControllerDescriptor == null)
GetControllerType();
var actionSelector = new ApiControllerActionSelector();
var descriptor = actionSelector.SelectAction(controllerContext);
return descriptor.ActionName;
}
public Type GetControllerType()
{
var descriptor = controllerSelector.SelectController(request);
controllerContext.ControllerDescriptor = descriptor;
return descriptor.ControllerType;
}
#endregion
}下面是路由測試:
[TestFixture]
public class RouteTests
{
#region Variables
HttpConfiguration _config;
#endregion
#region Setup
[SetUp]
public void Setup()
{
_config = new HttpConfiguration();
_config.Routes.MapHttpRoute(name: "DefaultWebAPI", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional });
}
#endregion
#region Helper methods
public static string GetMethodName<T, U>(Expression<Func<T, U>> expression)
{
var method = expression.Body as MethodCallExpression;
if (method != null)
return method.Method.Name;
throw new ArgumentException("Expression is wrong");
}
#endregion
}測試一個請求api/articles/5到ArticleController的action GetArticle(int id)
[Test]
public void RouteShouldControllerGetArticleIsInvoked()
{
var request = new HttpRequestMessage(HttpMethod.Get, "http://www.chsakell.com/api/articles/5");
var _actionSelector = new ControllerActionSelector(_config, request);
Assert.That(typeof(ArticlesController), Is.EqualTo(_actionSelector.GetControllerType()));
Assert.That(GetMethodName((ArticlesController c) => c.GetArticle(5)),
Is.EqualTo(_actionSelector.GetActionName()));
}我們用反射得到controller的action名稱,用同樣的方法來測試post提交的action
[Test]
public void RouteShouldPostArticleActionIsInvoked()
{
var request = new HttpRequestMessage(HttpMethod.Post, "http://www.chsakell.com/api/articles/");
var _actionSelector = new ControllerActionSelector(_config, request);
Assert.That(GetMethodName((ArticlesController c) =>
c.PostArticle(new Article())), Is.EqualTo(_actionSelector.GetActionName()));
}下面這個測試,路由會發(fā)生異常.
[Test]
public void RouteShouldInvalidRouteThrowException()
{
var request = new HttpRequestMessage(HttpMethod.Post, "http://www.chsakell.com/api/InvalidController/");
var _actionSelector = new ControllerActionSelector(_config, request);
Assert.Throws<HttpResponseException>(() => _actionSelector.GetActionName());
}結(jié)論
我們看到了Web API棧很多方面的單元測試,例如: mocking 服務層,單元測試控制器,消息管道,過濾器,定制媒體類型和路由配置。
嘗試在你的程序中總是寫單元測試,你不會后悔的。從里面會得到很多的好處,例如:在repository中一個簡單的修改可能破壞很多方面,如果寫一個合適的測試,則可能破壞你程序的問題會立即出現(xiàn).
原文:chsakell's Blog
網(wǎng)站標題:ASP.NETWebAPI單元測試-單元測試
URL網(wǎng)址:http://www.chinadenli.net/article4/gegioe.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設、全網(wǎng)營銷推廣、網(wǎng)站導航、動態(tài)網(wǎng)站、網(wǎng)站制作、小程序開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)