Using the unit test below .. Iam trying to test my webapi.
[Test]
public void CheckControllerForCreate()
{
var config = new HttpConfiguration();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/product");
var route = config.Routes.MapHttpRoute("Foo", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });
var controller = new ProductController
{
ControllerContext = new HttpControllerContext(config, routeData, request),
Request = request,
Url = new UrlHelper(request)
};
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
var result = controller.Create(new Product {Id = 4, Name = "Tomato Soup", Category = "Groceries", Price = 1});
}
[HttpPost]
public HttpResponseMessage Create(Product product)
{
var url = Url;
if (product == null)
throw new HttpResponseException(new HttpResponseMessage{StatusCode = HttpStatusCode.BadRequest,ReasonPhrase = "Product is not specified"});
products.Add(product);
var response = Request.CreateResponse(HttpStatusCode.Created, product);
string uri = Url.Link("Foo", product.Id);
response.Headers.Location = new Uri(uri);
return response;
}
The Create Action throws an exception because uri is null. Now, the Url helper is correctly picking up the RouteName , otherwise there would be a RouteName not found exception. I am assuming that somethign is wrong with my configuration.
I referred to http://www.peterprovost.org/blog/2012/06/16/unit-testing-asp-dot-net-web-api and several other posts for unit testing the controllers.
The WebAPI method is here on codeplex
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/1acb241299a8#src/System.Web.Http/Routing/UrlHelper.cs
Edit
I have narrowed it down to vpd being null in ( UrlHelper)
IHttpVirtualPathData vpd = configuration.Routes.GetVirtualPath(
request: request,
name: routeName,
values: routeValues);
Can't seem to figure out why ?
You need to set the routeData into the request, the same way you did with the configuration:
controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
Also you are incorrectly using the Url.Link helper. You haven't specified a controller nor you have indicated the id.
The code in your controller should look like this:
string uri = Url.Link("Foo", new { id = product.Id, controller = "product" });
UPDATE:
Here's a full example.
Controller:
public class ProductController : ApiController
{
public HttpResponseMessage Create(int id)
{
var uri = Url.Link("Foo", new { id = id, controller = "product" });
return Request.CreateResponse(HttpStatusCode.OK, uri);
}
}
Test:
// arrange
var config = new HttpConfiguration();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/product");
var route = config.Routes.MapHttpRoute("Foo", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary(new { controller = "product" }));
var controller = new ProductController
{
ControllerContext = new HttpControllerContext(config, routeData, request),
Request = request,
Url = new UrlHelper(request)
};
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
// act
var result = controller.Create(4);
// assert
...
Related
I'm trying to write test cases for my web api methods.
[Fact]
public async Task Get_Message_By_Id()
{
var messages = new Messages()
{
MessageId = 1,
Recipient = "1;2;3",
Subject = "Test Subject",
MessageText = "test subject"
};
var mesBL = new Mock<IMessagesBusinessLogic>();
mesBL
.Setup(repo => repo.GetRecivedMessageById(It.IsAny<IWebToken>() ,messages.MessageId))
.Returns(Task.FromResult(messages));
var messageController = new MessagesController(mesBL.Object);
var contentResult = await messageController.GetRecivedMessageById(messages.MessageId) as OkObjectResult;
Assert.NotNull(contentResult);
}
I get an error of null reference while GetRecivedMessageById method call.
Controller method;
[HttpGet]
[Route("{id:int}")]
public async Task<IActionResult> GetRecivedMessageById(int id)
{
return Ok(await _messagesBusinessLogic.GetRecivedMessageById(User.GetWebToken(), id));
}
Here, issue is because, user identity passing NULL.
How can we pass it from Test?
User is null because it was not setup in the test before exercising the method under test.
//Create test user
var displayName = "User name";
var role = "SomeRole";
var token = new Mock<IWebToken>();
token.Setup(m => m.DisplayName).Returns(displayName);
token.Setup(m => m.Role).Returns(role);
var identity = new UserIdentity(token.Object);
var principal = new GenericPrincipal(identity, roles: new string[] { });
var user = new ClaimsPrincipal(principal);
// Set the User on the controller via the ControllerContext
var messageController = new MessagesController(mesBL.Object) {
ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = user }
}
};
inside unit test I'm faking http context using
var fakeHttpContext = new Mock<HttpContextBase>();
var controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(t => t.HttpContext).Returns(fakeHttpContext.Object);
this.controller.ControllerContext = controllerContext.Object;
now inside my code I'm using following
UrlHelper helper = new UrlHelper(this.ControllerContext.RequestContext);
string url = helper.Action("Details", "Pers", new { id = person.Id });
what should I mock inside unit test in order to use UrlHelper code?
Currently UrlHelper helper is null.
p.s. Because of clarity I did not show whole initialization of controller code inside test, it's working, but I'm struggling now with this request context and UrlHelper.
Controller already has a UrlHelper Url property that you can pass an instance or mock. no need to new one up in the code.
Take a look at this example test with a controller that uses the UrlHelper.
[TestClass]
public class UrlHelperTest {
[TestMethod]
public void MockUrlHelper() {
//Arrange
var requestUrl = new Uri("http://myrequesturl");
var request = Mock.Of<HttpRequestBase>();
var requestMock = Mock.Get(request);
requestMock.Setup(m => m.Url).Returns(requestUrl);
var httpcontext = Mock.Of<HttpContextBase>();
var httpcontextSetup = Mock.Get(httpcontext);
httpcontextSetup.Setup(m => m.Request).Returns(request);
var actionName = "MyTargetActionName";
var expectedUrl = "http://myfakeactionurl.com";
var mockUrlHelper = new Mock<UrlHelper>();
mockUrlHelper
.Setup(m => m.Action(actionName, "Register", It.IsAny<object>(), It.IsAny<string>()))
.Returns(expectedUrl)
.Verifiable();
var sut = new MyController();
sut.Url = mockUrlHelper.Object;
sut.ControllerContext = new ControllerContext {
Controller = sut,
HttpContext = httpcontext,
};
//Act
var result = sut.MyAction();
//Assert
mockUrlHelper.Verify();
}
public class MyController : Controller {
[HttpPost]
public ActionResult MyAction() {
var link = GenerateActionLink("MyTargetActionName", string.Empty, string.Empty);
return View((object)link);
}
private string GenerateActionLink(string actionName, string token, string username) {
string validationLink = null;
if (Request.Url != null) {
var encodedToken = EncodedUrlParameter(token);
var url = Url.Action(actionName, "Register", new { Token = encodedToken, Username = username }, Request.Url.Scheme);
validationLink = url;
}
return validationLink;
}
private string EncodedUrlParameter(string token) {
return "Fake encoding";
}
}
}
I would like to unit test a async controller. Ive done this many times before but never called the Url.Link()method in order to generate the Location Header.
Heres my demo code.
Controller
public class DemoController : ApiController
{
[HttpPost]
public async Task<IHttpActionResult> DemoRequestPost(string someRequest)
{
// do something await ...
var id = 1;
// generate url for location header (here the problem occurrs)
var url = Url.Link("DemoRequestGet", new {id = id});
return Created(url, id);
}
[HttpGet]
[Route("demo/{id}", Name = "DemoRequestGet")]
public async Task<IHttpActionResult> DemoRequestGet(int id)
{
// return something
return Ok();
}
}
Test
[TestFixture]
public class DemoControllerTests
{
[Test]
public async Task CreateFromDraftShouldSucceed()
{
// Arrange
var request = "Hello World";
var controller = new DemoController();
var httpConfiguration = new HttpConfiguration();
// ensure attribte routing is setup
httpConfiguration.MapHttpAttributeRoutes();
httpConfiguration.EnsureInitialized();
controller.Configuration = httpConfiguration;
// Act
var result = await controller.DemoRequestPost(request);
// Assert
Assert.AreEqual(result, 1);
}
}
I am receiving
at NUnit.Framework.Internal.ExceptionHelper.Rethrow(Exception exception)
at NUnit.Framework.Internal.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult)
at NUnit.Framework.Internal.Commands.TestMethodCommand.RunAsyncTestMethod(TestExecutionContext context)
Check the Testing Link Generation in Unit Testing Controllers in ASP.NET Web API 2
The UrlHelper class needs the request URL and route data, so the test
has to set values for these.
You are not setting those in your example hence your error.
Here is one of their examples
[TestMethod]
public void PostSetsLocationHeader()
{
// Arrange
ProductsController controller = new ProductsController(repository);
controller.Request = new HttpRequestMessage {
RequestUri = new Uri("http://localhost/api/products")
};
controller.Configuration = new HttpConfiguration();
controller.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
controller.RequestContext.RouteData = new HttpRouteData(
route: new HttpRoute(),
values: new HttpRouteValueDictionary { { "controller", "products" } });
// Act
Product product = new Product() { Id = 42, Name = "Product1" };
var response = controller.Post(product);
// Assert
Assert.AreEqual("http://localhost/api/products/42", response.Headers.Location.AbsoluteUri);
}
based on that article you can do something like this which is mocking the UrlHelper using Moq.
[TestClass]
public class DemoControllerTests {
[TestMethod]
public async Task CreateFromDraftShouldSucceed() {
// This version uses a mock UrlHelper.
// Arrange
var controller = new DemoController();
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
string locationUrl = "http://localhost/api/demo/1";
// Create the mock and set up the Link method, which is used to create the Location header.
// The mock version returns a fixed string.
var mockUrlHelper = new Mock<UrlHelper>();
mockUrlHelper.Setup(x => x.Link(It.IsAny<string>(), It.IsAny<object>())).Returns(locationUrl);
controller.Url = mockUrlHelper.Object;
// Act
var request = "Hello World";
var result = await controller.DemoRequestPost(request);
var response = await result.ExecuteAsync(System.Threading.CancellationToken.None);
// Assert
Assert.AreEqual(locationUrl, response.Headers.Location.AbsoluteUri);
}
}
#dknaack For your controller test, you probably don't need this line of code:
var httpConfiguration = new HttpConfiguration();
httpConfiguration.MapHttpAttributeRoutes();
httpConfiguration.EnsureInitialized();
controller.Configuration = httpConfiguration;
I am trying to unit test an ApiController and set the current HttpContext, but every time I try and set the request information for the controller HttpContext.Current still seems to be null.
Here is the code:
[TestMethod]
public void MakePayment()
{
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/payment");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "payment" } });
var mockUserStore = new Mock<IUserStore<ApplicationUser>>();
var mockUserManager = new Mock<ApplicationUserManager>(mockUserStore.Object);
mockUserManager.Setup(x => x.GetUser(It.IsAny<string>())).Returns(new ApplicationUser());
IPayPalService payPalService = new PayPalService(mockUserManager.Object);
var paymentController = new PaymentController(payPalService);
paymentController.ControllerContext = new HttpControllerContext(config, routeData, request);
paymentController.Request = request;
paymentController.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
Payment payment = paymentController.DepositMoney(50.00);
Assert.AreEqual(payment.intent, "sale");
}
Is there anything that I am missing here?
The helper:
public static MvcHtmlString RouteLink(this HtmlHelper helper, String linkText, String routeName, Object routeValues, String status)
{
if (status.ToLower() == "post-game" || status.ToLower() == "mid-game")
{
return helper.RouteLink(linkText, routeName, routeValues);
}
return MvcHtmlString.Create(" ");
}
The unit test:
[TestMethod]
public void RouteLinkTest()
{
var httpContext = new Mock<HttpContextBase>();
var routeData = new Mock<RouteData>();
var viewContext = new ViewContext { HttpContext = httpContext.Object, RouteData = routeData.Object };
var helper = new HtmlHelper(viewContext, new Mock<IViewDataContainer>().Object);
var target01 = helper.RouteLink("Linking Text", "route-name", new { id = "id" }, "status");
Assert.IsNotNull(target01);
}
The error:
Test method Web.Tests.Extensions.HtmlHelpersTest.RouteLinkTest threw exception:
System.ArgumentException: A route named 'route-name' could not be found in the route collection.
Parameter name: name
The question:
How do I mock the route to have the proper route name?
Instead of mockin the routevalues tell MvcApplication to register them
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
I use it when I have to test some redirectToAction in my controller with something like that
var controller = GetController();
var httpContext = Utilities.MockControllerContext(true, false).Object;
controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
controller.Url = new UrlHelper(new RequestContext(httpContext, new RouteData()), routes);
var result = controller.DoMyStuff();
Assert.IsInstanceOfType(typeof(RedirectResult), result);
var actual = (RedirectResult)result;
Assert.AreEqual("/myurl", actual.Url.ToString());
I never tested an helper like yours but I think that should works