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試用

下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM