410 likes | 575 Views
Web Service Testing. Unit Testing. Doncho Minkov. Telerik Software Academy. http://academy.telerik.com. Senior Technical Trainer. http://minkov.it. Table of Contents. Ways of web service testing Unit Testing Integration Testing Complete Testing of Web Services
E N D
Web Service Testing Unit Testing Doncho Minkov Telerik Software Academy http://academy.telerik.com Senior Technical Trainer http://minkov.it
Table of Contents • 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
Web Service Testing • 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…
Web Service Testing (2) • 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
Unit Testing Testing the Work of the Controllers
Unit Testing • 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
Unit Testing the Data Layer • 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 storewith a ready-to-use framework • EntityFramework, NHibernate, OpenAccess • They are already tested enough • No point of testing dbContext.Set<T>.Add(), right?
Unit Testing the Repositories • 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
Unit Testing the Repositories (2) • 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 100ktest records in the database
Ways to Unit Test a Data Store • 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
Unit Testing with Transactions • 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 TransactionScopeinstance • Initialize the transaction in TestInitialize() • Dispose the transaction in TestCleanup()
Unit Testing with Transactions Live Demo
How should be tested the repositories? • 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
Unit Testing the Repositories Live Demo
Unit Testing the Services • 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
Unit Testing of the Controllers • 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
Unit Testing Controllers with Fake Repositories • To test the controllers, repositories should be faked • i.e. create a in-memory repository that implements the IRepository<T> interface class FakeRepository<T> : IRepository<T> where T:class { IList<T> entities = new List<T>(); public T Add(T entity) { this.entities.Add(entity); return entity; } public T Get(int id) { return this.entities[id]; } … }
Unit Testing Controllers with Fake Repositories (2) • 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<Category>(); var categoryToAdd = new Category(){ Name = "Test category" }; repository.entities.Add(categoryToAdd); var controller = new CategoriesController(repository); //act var categoriesModels = controller.GetAll(); //assert Assert.IsTrue(categoriesModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, categoriesModels.First().Name); }
Unit Testing with Fake Repositories Live Demo
Unit Testing with JustMock • Creating fake repository for each and every unit test is kind of boring • Here comes the mocking • Provide objects that can mimic some functionality • JustMock supports mocking of interfaces • Creates a fake instance of an interface and implement only the functionality needed
Unit Testing with JustMock (2) [TestMethod] public void GetAll_OneCategoryInRepository_ReturnsOneCategory() { //arrange var repository = Mock.Create<IRepository<Category>>(); var categoryToAdd = new Category() { Name = "Test category" }; IList<Category> categoryEntities = new List<Category>(); categoryEntities.Add(categoryToAdd); Mock.Arrange(() => repository.All()) .Returns(() => categoryEntities.AsQueryable()); var controller = new CategoriesController(repository); //act var categoryModels = controller.GetAll(); //assert Assert.IsTrue(categoryModels.Count() == 1); Assert.AreEqual(categoryToAdd.Name, categoryModels.First().Name); }
Unit Testing With JustMock Live Demo
More About Controllers Unit Testing • GET actions are easy to test • They return a C# objects • How to test POST actions? • They return HttpResponseMessage • Unfortunately POST actions require additional configuration due to the Request object they use
Configuring POST Actions • A simple POST action: Run in unit test has a value of null public HttpResponseMessage Post(CategoryModel model) { var entity = this.categoriesRepository.Add(model); var response = Request.CreateResponse<CategoryModel>( HttpStatusCode.Created, entity); var resourceLink = Url.Link("DefaultApi", new { id = entity.Id }); response.Headers.Location= new Uri(resourceLink); return response; } • If a controller is invoked outside of WebAPI environment, the Request object is not set
Configuring POST Actions (2) • The whole configuration needed for the POST to work in Unit Tests private void SetupController(ApiController controller) { var config = new HttpConfiguration(); var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/categories"); var route = config.Routes.MapHttpRoute(name: "DefaultApi", routeTemplate: "api/{controller}/{id}"); var routeData = new HttpRouteData(route); routeData.Values.Add("id", RouteParameter.Optional); routeData.Values.Add("controller", "categories"); controller.ControllerContext= new HttpControllerContext(config, routeData, request); controller.Request= request; controller.Request.Properties[ HttpPropertyKeys.HttpConfigurationKey] = config; controller.Request.Properties[ HttpPropertyKeys.HttpRouteDataKey] = routeData; }
Unit Testing POST Actions Live Demo
Integration Testing • 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
Integration Testing WebAPI • When integration testing WebAPI, controllers and their actions are thought 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
Integration Testing WebAPI (2) • Integration testing a GET request: [TestMethod] public void GetAll_OneCategory_StatusCodeOkAndNotNullContent() { var mockRepository = Mock.Create<IRepository<Category>>(); var models = new List<Category>(); models.Add(new Category() { Name = "Test Cat" }); Mock.Arrange(() => mockRepository.All()) .Returns(() => models.AsQueryable()); var server = new InMemoryHttpServer<Category>( "http://localhost/", mockRepository); var response = server.CreateGetRequest("api/categories"); Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); Assert.IsNotNull(response.Content); }
Integration Testing Live Demo
Unit Testing Web Services http://academy.Telerik.com
Homework • Students Database • Develop an application that holds students in schools • Students have first and last names, age, grade, a set of marks and a school • Each student can be only in a single school • Marks have subject and value • Schools have a name, location and a set of students
Homework (2) • Create a database for the students database • Using MS SQL server • Create a data layer that uses the above database • Use either database-first or code-first approach • Create a repositories layer that communicates with the data layer
Homework (3) • Create a services layer using the repositories and: • Use WebAPI • Introduce a REST API that allows adding and getting of students to/from the Students database • The following endpoints should be present: • GET api/students, GET api/students/{id}, • GET api/schools, GET api/schools/{id} • POST api/schools, POST api/students • GET api/students?subject=math&value=5.00 • Returns all students that have a mark for MATH and it is above 5.00
Homework (4) • Write unit tests for: • The StudentsRepository • The StudentsController • Write integration tests for the StudentsController endpoints