Asp.net WebAPI 单元测试
现在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 WebAPI 单元测试的更多相关文章
- Asp.net WebApi + EF 单元测试架构 DbContext一站到底
其实关于webapi和Ef service的单元测试我以前已经写过相关文章,大家可以参考: Asp.net WebAPI 单元测试 单元测试 mock EF 中DbContext 和DbSet Inc ...
- Asp.Net WebApi 调试利器“单元测试”
当我们编辑好一个WebApi应用程序后,需要对该Api接口进行调试,传统的调试办法是在方法内设置断点,然后用PostMan等http工具模拟访问进行查看WebAPI的运行情况,但这种除了效率较低还进行 ...
- ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- ASP.NET 5 单元测试中使用依赖注入
相关博文:<ASP.NET 5 使用 TestServer 进行单元测试> 在上一篇博文中,主要说的是,使用 TestServer 对 ASP.NET 5 WebApi 进行单元测试,依赖 ...
- 重温ASP.NET WebAPI(二)进阶
重温ASP.NET WebAPI(二)进阶 介绍 本文为个人对WebApi的回顾无参考价值. 本文内容: Rest和UnitOfWork 创建WebAPi的流程 IOC-Unity的使用 MEF ...
- 【转】.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 ...
- [转]ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- Asp.Net WebAPI的简介及创建
一.WebAPI简介 ASP.NET Web API 是一种框架,用于轻松构建可以由多种客户端(包括浏览器和移动设备)访问的 HTTP 服务.ASP.NET Web API 是一种用于在 .NET F ...
- Asp.Net WebApi核心对象解析(下篇)
在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...
随机推荐
- MySQL的binlog数据如何查看
binlog介绍 binlog,即二进制日志,它记录了数据库上的所有改变. 改变数据库的SQL语句执行结束时,将在binlog的末尾写入一条记录,同时通知语句解析器,语句执行完毕. binlog格式 ...
- 用sql从一张表更新数据到另外一张表(多表数据迁移)
update TBL_1 A, TBL_2 B, TBL_3 Cset a.email=c.email_addrwhere a.user_id=b.user_id and b.un_id=c.un_i ...
- 数字电路-异同步 复位 线与 oc门
1.同步电路和异步电路的区别是什么? 异步电路:主要是组合逻辑电路,用于产生地址译码器.FIFO或RAM的读写控制信号脉冲,但它同时也用在时序电路中,此时它没有统一的时钟,状态变化的时刻是不稳定的,通 ...
- perl 对源文件内容修改 方法整理
1, 利用Tie::File模块来直接对文件内容进行修改. #!/usr/bin/perl -w my $std="F160"; my $fast="FAST" ...
- 四色定理+dfs(poj 1129)
题目:Channel Allocation 题意:要求A:BCD,A与B,C,D都不相同,求不同的值,典型的四色定理: #include <iostream> #include <a ...
- centos下安装iftop
CentOS 下iftop 的安装 安装方法一:编译安装(安装成功) wget http://ex-parrot.com/~pdw/iftop/download/iftop-0.17pre3.tar. ...
- search in rotated sorted array leetcode
原题链接 题意:给你一个目标值,或者返回其在数组中的下标位置,或者返回-1(表示不存在,查找失败). 例如 0 1 2 4 5 6 7 可能成为 4 5 6 7 0 1 2. 思路分析: 用二分搜索来 ...
- 用scala实现一个sql执行引擎-(下)
执行 上一篇讲述了如何通过scala提供的内置DSL支持,实现一个可以解析sql的解析器,这篇讲如何拿到了解析结果-AST以后,如何在数据上进行操作,得到我们想要的结果.之前说到,为什么选择scala ...
- Modifiers: virtual, override, new, abstract, sealed, internal
internal 声明类.类成员.接口或接口成员具有内部可见性. internal 修饰符使类.接口或成员仅在当前包中可见. 当前包之外的代码不能访问 internal 成员.只有在同一程序集的文件中 ...
- linux tcp协议定时器
1 连接建立定时器:75秒 2 保活定时器:2小时又10分钟 3 重传定时器:根据RTT计算 4 2MSL定时器:最大报文段存活时间 5 持续定时器