Ostatnio postanowiłem także bliżej zapoznać się z frameworkiem Nsubstitute. Jest to framework do tworzenia mock'ów, dużo bardziej intuicyjny niż prezentowany w książce Moq.
Do testowania używam frameworka xUnit oraz pluginu do VisualStudio pozwalającego na uruchamianie w nim xUnitowych testów.
Do sprawnego testowania potrzebne będą 2 metody pomocnicze:
- metoda udająca zapytanie, odpowiedź oraz treść zapytania Http
- metoda porównająca dane ścieżki z nazwą kontrolera, akcji oraz dodatkowych parametrów
Pierwsze 2 metody wyglądają następująco:
using System; using System.Web; using Xunit; using NSubstitute; using System.Web.Routing; using System.Reflection; using Routing1; namespace UnitTests { public class Tests { private HttpContextBase CreateHttpContext(string targetUrl = null, string httpMethod = "Get") { //create the mock request var mockRequest = Substitute.For<httprequestbase>(); mockRequest.AppRelativeCurrentExecutionFilePath.Returns(targetUrl); mockRequest.HttpMethod.Returns(httpMethod); //create the mock response var mockResponse = Substitute.For<httpresponsebase>(); mockResponse.ApplyAppPathModifier(Arg.Any<string>()) .Returns(s=>s[0]); //create the mock context, using the request and response var mockContext = Substitute.For<httpcontextbase>(); mockContext.Request.Returns(mockRequest); mockContext.Response.Returns(mockResponse); return mockContext; } private bool TestIncomingRouteResult(RouteData routeResult, string controller, string action, object propertySet = null) { Func<object, bool, object> valCompare = (v1, v2) => { return StringComparer.InvariantCultureIgnoreCase .Compare(v1, v2) == 0; }; bool result = valCompare(routeResult.Values["controller"], controller) && valCompare(routeResult.Values["action"], action); if (propertySet != null) { PropertyInfo[] propInfo = propertySet.GetType().GetProperties(); foreach (var p in propInfo) { if (!(routeResult.Values.ContainsKey(p.Name) && valCompare(routeResult.Values[p.Name], p.GetValue(propertySet, null)))) { result = false; break; } } } return result; } } }
Pierwsza metoda pozwala na przekazanie URL'a poprzez właściwość AppRelativeCurrentExecutionFilePath klasy HttpRequestBase.
Druga metoda pozwala nam przetestować samą ścieżkę.
Następnie dopisane zostały kolejne 3 metody pozwalające na sprawdzenie poprawności ścieżki, sprawdzenie błędnej ścieżki oraz metody testującej kilka przykładowych ścieżek.
private void TestRouteMatch(string url, string controller, string action, object routeProperties = null, string httpMethod = "GET") { //Arrange RouteCollection routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); //Act RouteData result = routes.GetRouteData(CreateHttpContext(url, httpMethod)); //Assert Assert.NotNull(result); Assert.True(TestIncomingRouteResult(result, controller, action, routeProperties)); } private void TestRouteFail(string url) { RouteCollection routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); RouteData result = routes.GetRouteData(CreateHttpContext(url)); Assert.True(result == null || result.Route == null); } [Fact] public void TestIncomingRoutes() { //check for the URL that we hope to receive TestRouteMatch("~/Admin/Index","Admin","Index"); //check that the values are baing obtained from the segments TestRouteMatch("~/One/Two", "One", "Two"); TestRouteFail("~/Admin/Index/Segment"); TestRouteFail("~/Admin"); }
Należy pamiętać, aby do projektu z testami dodać referencję do naszego prawdziwego projektu. W nim zawiera się kalsa RouteConfig wykorzystywana w metodach pomocniczych zawierająca metodę RegisterRoutes, gdzie domyślnie powinniśmy mieć zdefiniowane wszystkie ścieżki w naszej aplikacji.
Tak przygotowane testy mogą posłużyć do bardziej skomplikowanego testowania, którego przykłady zostaną zaprezentowane w kolejnej części.