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 + 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));
            }

        }

那么我们如何托起这个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试用

下载

时间: 2024-08-23 23:07:49

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

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

其实关于webapi和Ef service的单元测试我以前已经写过相关文章,大家可以参考: Asp.net WebAPI 单元测试 单元测试 mock EF 中DbContext 和DbSet Include 先看一下项目结构图: 这个demo非常简单,UTWebApi.Data 是纯粹的数据定义,UTWebApi.Service是我们的业务服务逻辑层,UTWebApi 是我们webapi的实现,UTWebApi.Tests就是测试项目. 数据层: BloggerDbContext的构造函数一般

ASP.NET WebApi OWIN 实现 OAuth 2.0

原文:http://www.cnblogs.com/xishuai/p/aspnet-webapi-owin-oauth2.html OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据.每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的 2 小时内)内访问特定的资源(

重温ASP.NET WebAPI(二)进阶

介绍 本文为个人对WebApi的回顾无参考价值. 本文内容: Rest和UnitOfWork 创建WebAPi的流程 IOC-Unity的使用 MEF 自定义URL Base认证和Token自定义权限 日志NLog OData的使用 Owin自宿主的使用 代码地址:https://github.com/OtherRuan/Review-Serials WebApi的几点特性 WebApi 提供了几点特性: 1. 自动匹配HTTP方法 GetMethod(), 惯例上会直接匹配Get的http方法

ASP.NET WebAPI 08 Message,HttpConfiguration,DependencyResolver

Message WebAPI作为通信架构必定包含包含请求与响应两个方法上的报文,在WebAPI它们分别是HttpRequestMessage,HttpResponseMessage.对于HttpResponseMessage之前在WebAPI返回结果中有应用. HttpRequestMessage 请求报文包含请求地址(RequestUri),请求方法(Method),头信息(Headers),报文信息(Content)以及Http版本(Versions) public class HttpRe

asp.net webapi 使用小结

一星期前公司用webapi处理一些事情,自己总结一下用法. 1.创建一个空的webapi会默认有一下几个方法. public class ValueController : ApiController { // GET api/value public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } // GET api/value/5 public st

使用ASP.Net WebAPI构建REST服务(六)——Self-Host

Asp.Net WebAPI生成的是一个程序集,并不是独立的进程,因此,要运行的时候必须将其承载在相应的宿主上,一般比较常见的是IIS承载.很多时候,我们为了简化部署或者功能集成,需要将其承载到独立的进程上,这种方式一般称之为Self-Host,本文就简单的介绍一下WebAPI的SefHost方法. 首先在Nuget上安装Microsoft.AspNet.WebApi.SelfHost库. 附上我们的WebAPI控制器 publicclassValuesController : ApiContr

使用ASP.Net WebAPI构建REST服务(四)——参数绑定

默认绑定方式 WebAPI把参数分成了简单类型和复杂类型: 简单类型主要包括CLR的primitive types,(int.double.bool等),系统内置的几个strcut类型(TimeSpan.Guid等)以及string.对于简单类型的参数,默认从URI中获取. 复杂类型的数据也可以直接作为参数传入进来,系统使用media-type formatter进行解析后传给服务函数.对于复杂类型,默认从正文中获取, 例如,对于如下函数 HttpResponseMessage Put(int

使用ASP.Net WebAPI构建REST-ful 服务(一)——简单的示例

由于给予REST的Web服务非常简单易用,它越来越成为企业后端服务集成的首选方法.本文这里介绍一下如何通过微软的Asp.Net WebAPI快速构建REST-ful 服务. 首先创建一个Asp.Net Web应用程序(我这里用的是Visual Studio 2013,它已经内置了Web API2). 在出来的模板中选择Empty(空项目),并勾选WebAPI.点击确定后,就创建了一个空的WebAPI服务. 此时只有一个空项目,还没有任何功能,在进行下一步之前,首先我们来看一下REST的基本操作模

[原创] ASP.NET WEBAPI 接入微信公众平台 总结,Token验证失败解决办法

首先,请允许我说一句:shit! 因为这个问题不难,但是网上有关 ASP.NET WEBAPI的资料太少.都是PHP等等的. 我也是在看了某位大神的博客后有启发,一点点研究出来的. 来看正题! 1.微信公众平台的接入方法,无非4个参数(signature, timestamp, nonce, echostr)加1个Token(两边对应) 2.Token, timestamp, nonce字典排序后,按照字符串“SHA1”加密.得出一串字符串 tmpStr(转化为小写) 3.然后比对 tmpStr