Presentation is loading. Please wait.

Presentation is loading. Please wait.

Unit testing of the Services Telerik Software Academy Web Services and Cloud.

Similar presentations


Presentation on theme: "Unit testing of the Services Telerik Software Academy Web Services and Cloud."— Presentation transcript:

1 Unit testing of the Services Telerik Software Academy http://academy.telerik.com Web Services and Cloud

2  Ways of web service testing  Unit Testing  Integration Testing  Complete Testing of Web Services  Unit testing the data layer  Unit testing the repositories layer  Unit testing the controllers  Integration testing the web services

3

4  Web service unit testing is much like regular unit testing  Writing test methods to test methods etc..  Yet a service is build from many more components than POCO objects  There are the objects, service routing, media types, HTTP Status codes, etc…

5  When a web service is ready for test, the testing itself is performed in the following steps:  Write Unit Tests to test the C# objects  Test all objects, their constructors, their methods  Write the Integration Testing  Test the application as if a user tests it

6 Testing the Work of the Controllers

7  The core idea of Unit testing is to test small components of an application  Test a single behavior (a method, property, constructor, etc…)  Unit tests cover all C# components of the app  Models and data layer  Like repositories and DB/XML read/write  Business layer  Controllers and their actions

8

9  The data layer is the one thing that most of the time does not need testing  The idea of the data layer is to reference a data store with a ready-to-use framework  EntityFramework, NHibernate, OpenAccess  They are already tested enough  No point of testing dbContext.Set.Add(), right?

10

11  It is essential to test the implementations of our repositories  The repositories contain the data store logic  All custom (developer) logic must be tested  A missing dbContext.SaveChanges() can cause a lot of pain

12  How to test the data store logic?  Writing and deleting the original (production) database is not quite a good option  Imagine a failed test that leaves 100k test records in the database

13  A few ways exist to unit test a data store  Manually create a copy of the data store and work on the copy  Backup the original data store, work on the original, and restore the backup when the tests are over  Use transactions, to prevent commit to the data store

14  When testing with transactions, the changes done are not really applied to the data store  Whatever commited, if tran.Complete() is not called, the transaction logic is rolled back  How to use transactions in unit tests?  Create a static TransactionScope instance  Initialize the transaction in TestInitialize()  Dispose the transaction in TestCleanup()

15 Live Demo

16  What parts of the repositories should our tests cover?  Test for correct behavior  Add, Delete, Get, All, etc…  Test for incorrect behavior  Test Add with Category that has NULL name

17 Live Demo

18

19  Testing the services layers actually means testing the controllers and the REST API  Test if controllers work correctly as C# objects  Using mocking or fake repositories  Test if the endpoints of the REST services work correctly  Check the StatusCode and Content

20  The Unit testing of the controllers is not much of a big deal  Test them as regular C# classes  Instantiate an object, and test its methods (actions)  The repositories can be mocked/faked for easier testing  If not mocked, the transaction technique should be used again

21  To test the controllers, repositories should be faked  i.e. create a in-memory repository that implements the IRepository interface class FakeRepository : IRepository where T:class { IList entities = new List (); IList entities = new List (); public T Add(T entity) { public T Add(T entity) { this.entities.Add(entity); this.entities.Add(entity); return entity; return entity; } public T Get(int id) public T Get(int id) { return this.entities[id]; return this.entities[id]; } …}

22  With the Fake Repository present, controllers can be tested by passing their constructor a fake rep public void GetAll_OneCategoryInRepository_ReturnOneCategory() { //arrange var repository = new FakeRepository (); var repository = new FakeRepository (); var categoryToAdd = new Category(){ Name = "Test category" }; var categoryToAdd = new Category(){ Name = "Test category" }; repository.Add(categoryToAdd); repository.Add(categoryToAdd); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoriesModels = controller.GetAll(); var categoriesModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoriesModels.Count() == 1); Assert.IsTrue(categoriesModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoriesModels.First().Name); categoriesModels.First().Name);}

23  With the Fake Repository present, controllers can be tested by passing their constructor a fake rep public void GetAll_OneCategoryInRepository_ReturnOneCategory() { //arrange var repository = new FakeRepository (); var repository = new FakeRepository (); var categoryToAdd = new Category(){ Name = "Test category" }; var categoryToAdd = new Category(){ Name = "Test category" }; repository.Add(categoryToAdd); repository.Add(categoryToAdd); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoriesModels = controller.GetAll(); var categoriesModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoriesModels.Count() == 1); Assert.IsTrue(categoriesModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoriesModels.First().Name); categoriesModels.First().Name);} Prepare the repository

24  With the Fake Repository present, controllers can be tested by passing their constructor a fake rep public void GetAll_OneCategoryInRepository_ReturnOneCategory() { //arrange var repository = new FakeRepository (); var repository = new FakeRepository (); var categoryToAdd = new Category(){ Name = "Test category" }; var categoryToAdd = new Category(){ Name = "Test category" }; repository.Add(categoryToAdd); repository.Add(categoryToAdd); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoriesModels = controller.GetAll(); var categoriesModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoriesModels.Count() == 1); Assert.IsTrue(categoriesModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoriesModels.First().Name); categoriesModels.First().Name);} Prepare the repository Pass it to the controller

25  With the Fake Repository present, controllers can be tested by passing their constructor a fake rep public void GetAll_OneCategoryInRepository_ReturnOneCategory() { //arrange var repository = new FakeRepository (); var repository = new FakeRepository (); var categoryToAdd = new Category(){ Name = "Test category" }; var categoryToAdd = new Category(){ Name = "Test category" }; repository.Add(categoryToAdd); repository.Add(categoryToAdd); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoriesModels = controller.GetAll(); var categoriesModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoriesModels.Count() == 1); Assert.IsTrue(categoriesModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoriesModels.First().Name); categoriesModels.First().Name);} Prepare the repository Pass it to the controller Act on the controller

26  With the Fake Repository present, controllers can be tested by passing their constructor a fake rep public void GetAll_OneCategoryInRepository_ReturnOneCategory() { //arrange var repository = new FakeRepository (); var repository = new FakeRepository (); var categoryToAdd = new Category(){ Name = "Test category" }; var categoryToAdd = new Category(){ Name = "Test category" }; repository.Add(categoryToAdd); repository.Add(categoryToAdd); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoriesModels = controller.GetAll(); var categoriesModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoriesModels.Count() == 1); Assert.IsTrue(categoriesModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoriesModels.First().Name); categoriesModels.First().Name);} Prepare the repository Pass it to the controller Act on the controller Assert the result

27 Live Demo

28  Creating fake repository for each and every unit test is kind of boring  Here comes the mocking  Provide objects that mimic some functionality  JustMock/Moq provide mocking functionality  Creates a fake instance of an interface and implement only the functionality needed

29 [TestMethod] public void GetAll_SingleCategoryInRepository_ReturnsTheCategory() { //arrange //arrange var repository = Mock.Create >(); var repository = Mock.Create >(); var categoryToAdd = GetTestCategory(); var categoryToAdd = GetTestCategory(); IList entities = new List (){ categoryToAdd }; IList entities = new List (){ categoryToAdd }; Mock.Arrange(() => repository.All()) Mock.Arrange(() => repository.All()).Returns(() => entities.AsQueryable()); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoryModels = controller.GetAll(); var categoryModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoryModels.Count() == 1); Assert.IsTrue(categoryModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoryModels.First().Name); categoryModels.First().Name);}

30 [TestMethod] public void GetAll_SingleCategoryInRepository_ReturnsTheCategory() { //arrange //arrange var repository = Mock.Create >(); var repository = Mock.Create >(); var categoryToAdd = GetTestCategory(); var categoryToAdd = GetTestCategory(); IList entities = new List (){ categoryToAdd }; IList entities = new List (){ categoryToAdd }; Mock.Arrange(() => repository.All()) Mock.Arrange(() => repository.All()).Returns(() => entities.AsQueryable()); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoryModels = controller.GetAll(); var categoryModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoryModels.Count() == 1); Assert.IsTrue(categoryModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoryModels.First().Name); categoryModels.First().Name);} Create the mock object

31 [TestMethod] public void GetAll_SingleCategoryInRepository_ReturnsTheCategory() { //arrange //arrange var repository = Mock.Create >(); var repository = Mock.Create >(); var categoryToAdd = GetTestCategory(); var categoryToAdd = GetTestCategory(); IList entities = new List (){ categoryToAdd }; IList entities = new List (){ categoryToAdd }; Mock.Arrange(() => repository.All()) Mock.Arrange(() => repository.All()).Returns(() => entities.AsQueryable()); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoryModels = controller.GetAll(); var categoryModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoryModels.Count() == 1); Assert.IsTrue(categoryModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoryModels.First().Name); categoryModels.First().Name);} Create the mock object Mock the All() behavior

32 [TestMethod] public void GetAll_SingleCategoryInRepository_ReturnsTheCategory() { //arrange //arrange var repository = Mock.Create >(); var repository = Mock.Create >(); var categoryToAdd = GetTestCategory(); var categoryToAdd = GetTestCategory(); IList entities = new List (){ categoryToAdd }; IList entities = new List (){ categoryToAdd }; Mock.Arrange(() => repository.All()) Mock.Arrange(() => repository.All()).Returns(() => entities.AsQueryable()); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoryModels = controller.GetAll(); var categoryModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoryModels.Count() == 1); Assert.IsTrue(categoryModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoryModels.First().Name); categoryModels.First().Name);} Create the mock object Mock the All() behavior Act on the controller

33 [TestMethod] public void GetAll_SingleCategoryInRepository_ReturnsTheCategory() { //arrange //arrange var repository = Mock.Create >(); var repository = Mock.Create >(); var categoryToAdd = GetTestCategory(); var categoryToAdd = GetTestCategory(); IList entities = new List (){ categoryToAdd }; IList entities = new List (){ categoryToAdd }; Mock.Arrange(() => repository.All()) Mock.Arrange(() => repository.All()).Returns(() => entities.AsQueryable()); var controller = new CategoriesController(repository); var controller = new CategoriesController(repository); //act //act var categoryModels = controller.GetAll(); var categoryModels = controller.GetAll(); //assert //assert Assert.IsTrue(categoryModels.Count() == 1); Assert.IsTrue(categoryModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, Assert.AreEqual(categoryToAdd.Name, categoryModels.First().Name); categoryModels.First().Name);} Create the mock object Mock the All() behavior Act on the controller Assert the result

34 Live Demo

35  GET actions are easy to test  They return POCO objects  How to test POST actions?  They return HttpResponseMessage  Unfortunately POST actions require additional configuration due to the Request object they use

36  A simple POST action: public HttpResponseMessage Post(CategoryModel model) { var entity = this.categoriesRepository.Add(model); var entity = this.categoriesRepository.Add(model); var response = Request.CreateResponse ( var response = Request.CreateResponse ( HttpStatusCode.Created, HttpStatusCode.Created, entity); entity); var resourceLink = Url.Link("DefaultApi", var resourceLink = Url.Link("DefaultApi", new { id = entity.Id }); new { id = entity.Id }); response.Headers.Location = new Uri(resourceLink); response.Headers.Location = new Uri(resourceLink); return response; return response;}

37  A simple POST action: public HttpResponseMessage Post(CategoryModel model) { var entity = this.categoriesRepository.Add(model); var entity = this.categoriesRepository.Add(model); var response = Request.CreateResponse ( var response = Request.CreateResponse ( HttpStatusCode.Created, HttpStatusCode.Created, entity); entity); var resourceLink = Url.Link("DefaultApi", var resourceLink = Url.Link("DefaultApi", new { id = entity.Id }); new { id = entity.Id }); response.Headers.Location = new Uri(resourceLink); response.Headers.Location = new Uri(resourceLink); return response; return response;} Run in unit test, Request has a value of null  If a controller is invoked outside of WebAPI environment, the Request object is not set

38  To have a non-null value of the Request object, it must be set up manually private void SetupController(ApiController controller) { var request = new HttpRequestMessage() var request = new HttpRequestMessage() { RequestUri = new Uri("http://test-url.com")}; { RequestUri = new Uri("http://test-url.com")}; controller.Request = request; controller.Request = request; var config = new HttpConfiguration(); var config = new HttpConfiguration(); config.Routes.MapHttpRoute( config.Routes.MapHttpRoute( name: "DefaultApi", name: "DefaultApi", routeTemplate: "api/{controller}/{id}", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }); defaults: new { id = RouteParameter.Optional }); controller.Configuration = config; controller.Configuration = config; controller.RequestContext.RouteData = new HttpRouteData( controller.RequestContext.RouteData = new HttpRouteData( route: new HttpRoute(), route: new HttpRoute(), values: new HttpRouteValueDictionary { values: new HttpRouteValueDictionary { { "controller", "categories" } { "controller", "categories" } }); });}

39  To have a non-null value of the Request object, it must be set up manually private void SetupController(ApiController controller) { var request = new HttpRequestMessage() var request = new HttpRequestMessage() { RequestUri = new Uri("http://test-url.com")}; { RequestUri = new Uri("http://test-url.com")}; controller.Request = request; controller.Request = request; var config = new HttpConfiguration(); var config = new HttpConfiguration(); config.Routes.MapHttpRoute( config.Routes.MapHttpRoute( name: "DefaultApi", name: "DefaultApi", routeTemplate: "api/{controller}/{id}", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }); defaults: new { id = RouteParameter.Optional }); controller.Configuration = config; controller.Configuration = config; controller.RequestContext.RouteData = new HttpRouteData( controller.RequestContext.RouteData = new HttpRouteData( route: new HttpRoute(), route: new HttpRoute(), values: new HttpRouteValueDictionary { values: new HttpRouteValueDictionary { { "controller", "categories" } { "controller", "categories" } }); });} Create a Request object

40  To have a non-null value of the Request object, it must be set up manually private void SetupController(ApiController controller) { var request = new HttpRequestMessage() var request = new HttpRequestMessage() { RequestUri = new Uri("http://test-url.com")}; { RequestUri = new Uri("http://test-url.com")}; controller.Request = request; controller.Request = request; var config = new HttpConfiguration(); var config = new HttpConfiguration(); config.Routes.MapHttpRoute( config.Routes.MapHttpRoute( name: "DefaultApi", name: "DefaultApi", routeTemplate: "api/{controller}/{id}", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional }); defaults: new { id = RouteParameter.Optional }); controller.Configuration = config; controller.Configuration = config; controller.RequestContext.RouteData = new HttpRouteData( controller.RequestContext.RouteData = new HttpRouteData( route: new HttpRoute(), route: new HttpRoute(), values: new HttpRouteValueDictionary { values: new HttpRouteValueDictionary { { "controller", "categories" } { "controller", "categories" } }); });} Create a config Create a Request object

41 Live Demo

42

43  Integration testing aims to cover the work of the whole application  Not small components like unit testing  Integration tests should work like a user  Test what a user sees in combination of all application components mixed together

44  When integration testing WebAPI, controllers and their actions are assumed to be working correctly  In WebAPI, integration tests should cover:  The endpoints of the RESTful services  Test if the endpoint reaches the correct action  Test the serialization of the data  Does it work with JSON/XML  Is the data serialized correctly

45  Integration testing a GET request: [TestMethod] public void GetAll_SingleCategory_StatusCodeOkAndNotNullContent() { var mockRepository = Mock.Create >(); var mockRepository = Mock.Create >(); var models = new List (); var models = new List (); models.Add(new Category() { Name = "Test Cat" }); models.Add(new Category() { Name = "Test Cat" }); Mock.Arrange(() => mockRepository.All()) Mock.Arrange(() => mockRepository.All()).Returns(() => models.AsQueryable());.Returns(() => models.AsQueryable()); var server = new InMemoryHttpServer ( var server = new InMemoryHttpServer ( "http://localhost/", "http://localhost/", mockRepository); mockRepository); var response = server.CreateGetRequest("api/categories"); var response = server.CreateGetRequest("api/categories"); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); Assert.IsNotNull(response.Content); Assert.IsNotNull(response.Content);}

46  Integration testing a GET request: [TestMethod] public void GetAll_SingleCategory_StatusCodeOkAndNotNullContent() { var mockRepository = Mock.Create >(); var mockRepository = Mock.Create >(); var models = new List (); var models = new List (); models.Add(new Category() { Name = "Test Cat" }); models.Add(new Category() { Name = "Test Cat" }); Mock.Arrange(() => mockRepository.All()) Mock.Arrange(() => mockRepository.All()).Returns(() => models.AsQueryable());.Returns(() => models.AsQueryable()); var server = new InMemoryHttpServer ( var server = new InMemoryHttpServer ( "http://localhost/", "http://localhost/", mockRepository); mockRepository); var response = server.CreateGetRequest("api/categories"); var response = server.CreateGetRequest("api/categories"); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); Assert.IsNotNull(response.Content); Assert.IsNotNull(response.Content);} Fake in-memory server, that hosts the WebAPI controllers

47 Live Demo

48 форум програмиране, форум уеб дизайн курсове и уроци по програмиране, уеб дизайн – безплатно програмиране за деца – безплатни курсове и уроци безплатен SEO курс - оптимизация за търсачки уроци по уеб дизайн, HTML, CSS, JavaScript, Photoshop уроци по програмиране и уеб дизайн за ученици ASP.NET MVC курс – HTML, SQL, C#,.NET, ASP.NET MVC безплатен курс "Разработка на софтуер в cloud среда" BG Coder - онлайн състезателна система - online judge курсове и уроци по програмиране, книги – безплатно от Наков безплатен курс "Качествен програмен код" алго академия – състезателно програмиране, състезания ASP.NET курс - уеб програмиране, бази данни, C#,.NET, ASP.NET курсове и уроци по програмиране – Телерик академия курс мобилни приложения с iPhone, Android, WP7, PhoneGap free C# book, безплатна книга C#, книга Java, книга C# Николай Костов - блог за програмиране http://academy.Telerik.com

49  Develop a REST API for a BugLogger app  Bugs have status:  fixed, assigned, for-testing, pending  Bugs have text and logDate  Newly added bugs always have status "pending"  Bugs can be queried – get all bugs, get bugs after a date, get only pending bugs, etc… 1. Develop a database in MS SQL Server that keeps the data of the bugs 2. Create repositories to work with the bugs database

50 3. Provide a REST API to work with the bugs  Using WebAPI  Provide the following actions:  Log new bug  Get all bugs  Get bugs after a specific date:  Get bugs by status  Change bug status 4. Write unit tests to test the BugLogger  Use a mocking framework 5. Write integration tests to test the BugLogger POST …/bugs GET …/bugs GET …/bugs?date=22-06-2014 GET …/bugs?type=pending PUT …/bugs/{id}


Download ppt "Unit testing of the Services Telerik Software Academy Web Services and Cloud."

Similar presentations


Ads by Google