现在Asp.net webapi 运用的越来越多,其单元而是也越来越重要。一般软件开发都是多层结构,上层调用下层的接口,而各层的实现人员不同,一般大家都只写自己对应单元测试。对下层的依赖我们通过IOC来做。首先看我们的Controller定义及实现

 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中将需要用到该service。【请千万不要告诉我说这里的controller非常简单,不需要做单元测试!在实际项目中有人为了不做Controller的单元测试,把里面的逻辑全部提取出来放在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);
}
}

一般我们的controller都只用DependencyResolver和IHttpControllerActivator 来实现控制反转。

单元测试我一般用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)));
}

在controller的实例的时候直接传递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 + ;
_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));
} }

那么我们如何托起这个web 程序,有如何 使用IOC,

这里IOC 用Autofac,其所用包如下:

Install-Package Owin
Install-Package Microsoft.AspNet.WebApi.Owin
Install-Package Microsoft.Owin.Host.HttpListener
Install-Package Microsoft.Owin.Hosting
Install-Package Autofac
Install-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试用

下载

Asp.net WebAPI 单元测试的更多相关文章

  1. Asp.net WebApi + EF 单元测试架构 DbContext一站到底

    其实关于webapi和Ef service的单元测试我以前已经写过相关文章,大家可以参考: Asp.net WebAPI 单元测试 单元测试 mock EF 中DbContext 和DbSet Inc ...

  2. Asp.Net WebApi 调试利器“单元测试”

    当我们编辑好一个WebApi应用程序后,需要对该Api接口进行调试,传统的调试办法是在方法内设置断点,然后用PostMan等http工具模拟访问进行查看WebAPI的运行情况,但这种除了效率较低还进行 ...

  3. ASP.NET WebApi OWIN 实现 OAuth 2.0

    OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...

  4. ASP.NET 5 单元测试中使用依赖注入

    相关博文:<ASP.NET 5 使用 TestServer 进行单元测试> 在上一篇博文中,主要说的是,使用 TestServer 对 ASP.NET 5 WebApi 进行单元测试,依赖 ...

  5. 重温ASP.NET WebAPI(二)进阶

    重温ASP.NET WebAPI(二)进阶   介绍 本文为个人对WebApi的回顾无参考价值. 本文内容: Rest和UnitOfWork 创建WebAPi的流程 IOC-Unity的使用 MEF ...

  6. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  7. [转]ASP.NET WebApi OWIN 实现 OAuth 2.0

    OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...

  8. Asp.Net WebAPI的简介及创建

    一.WebAPI简介 ASP.NET Web API 是一种框架,用于轻松构建可以由多种客户端(包括浏览器和移动设备)访问的 HTTP 服务.ASP.NET Web API 是一种用于在 .NET F ...

  9. Asp.Net WebApi核心对象解析(下篇)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

随机推荐

  1. JS倒计时网页自动跳转代码

    <title>JS倒计时网页自动跳转代码</title> <script language="JavaScript" type="text/ ...

  2. JS-定时器管理实例函数封装

    /** * Created by 12461 on 2016/11/6. */window.onload = function () { var oBtn1 = document.getElement ...

  3. UIView的剖析(转)

    转自:http://blog.csdn.net/mengtnt/article/details/6716289 前面说过UIViewController,但是UIView也是在MVC中非常重要的一层  ...

  4. IOS-synthesize和dynamic的异同(转)

    一,retain, copy, assign区别 1. 假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b.此时a ...

  5. 跟服务器交互的Web表单(form)

    使用HTML来构建可以跟服务器交互的Web表单(form),通过给你的form元素添加一个action属性来达到此目的. action属性的值指定了表单提交到服务器的地址. 例如: <form ...

  6. 5.1 Intent

    Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent用于启动Activity, Service, 以及BroadcastReceiver三种组件, 同时还是组件之间通信 ...

  7. 如果我用C#来输出99表

    题目:参见这个链接,简单点说就是在控制台输出一个99乘方表. 无聊想了个C#版本的解答: private static void Print(int n) { var s = Enumerable.R ...

  8. WPF快速入门系列(4)——深入解析WPF绑定

    一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信 ...

  9. github心得

    心得  : 1:安装:省略 2. 配置 Git 以及上传代码 安装 Git 成功后,如果是 Windows 下,选择 Git Bash ,在命令行中完成一切,可能开始有点麻 烦,不过就那几条命令行,用 ...

  10. Python模拟HTTP Post上传文件

    使用urllib2模块构造http post数据结构,提交有文件的表单(multipart/form-data),本示例提交的post表单带有两个参数及一张图片,代码如下: #buld post bo ...