How to unit test MVC controller action that is using HttpClient - c#

I am trying to test the following MVC controller Action, which is calling out to Web API for a List of Products:
public ActionResult Index()
{
var model = new List<Product>();
using(HttpClient client = new HttpClient())
{
model = client.GetAsync(uri).Result.Content.ReadAsAsync<List<Product>>().Result;
}
return View(model);
}
I am trying to unit test this, have tried using Telerik JustMock to test, for example:
[TestMethod]
public void IndexDisplaysAllProducts()
{ // Not sure how to call here
var repo = Mock.Arrange(() => (new List<Product>())).Returns(
new List<Product> {
new Product(),
new Product(),
new Product()
});
var controller = new ProductsController();
var result = (ViewResult)controller.Index();
var model = (List<Product>)result.Model;
Assert.AreEqual(3, model.Count);
}
Just wondered how you would go about testing this?

Mobile atm, so excuse brevity. Implement an httpclientfactory class and Ihttpclientfactory interface, inject that to the ctor using ioc then mock during test to create a mocked instance of the http client.
Alternatively, and even simpler to test, you could use this approach to implement a factory class for something that does all of the stuff you're using the http client for (GetAsync(uri).Result.Content.ReadAsAsync>() etc)
Hth

Related

I am Making Mock of request But it showing Null?

I am Making Mock of Register Agency User But It showing some of its attribute Null.
I am new and I don't understand this.
I am trying to make Mock of Register Agency User that is the model
[TestMethod]
public async Task ABCreateActionResult_ReturnsBadRequest_Badrequest()
{
RegisterAgencyUserRequest mock = new Mock<RegisterAgencyUserRequest>().Object;
var controller = _accountController.RegisterAgencyUser(mock);
JsonResult viewResult = (JsonResult)await _accountController.RegisterAgencyUser(mock);
}
Here is what I have to Test
public async Task<IActionResult> RegisterAgencyUser([FromBody] Models.Request.RegisterAgencyUserRequest request)
{
JsonContentResult jsonContentResult = new JsonContentResult();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (string.IsNullOrEmpty(request.InvitationCode) || string.IsNullOrWhiteSpace(request.InvitationCode))
return Json(new SharedResponse(AppConstants.ServiceReponses.Error, "A user can't be registered without invitation"));
var invitationDetails = _inviteRepository.GetInvitaionCodeDetails(request.InvitationCode);
if (invitationDetails.Type != (int)InviteType.Agency)
{
return Json(new SharedResponse(AppConstants.ServiceReponses.Error, "Invalid invitation code"));
}
//...
You only mock dependencies to your class that you are testing. So you don't need to mock a POCO. Just set it up with dummy data like below:
var model = new RegisterAgencyUserRequest
{
Address = "Value1",
AgencyName = "Value2",
//...
};
and use it like below:
var controller = _accountController.RegisterAgencyUser(model);
Your dependency for the method is:
_inviteRepository.GetInvitaionCodeDetails()
so you need to mock the _inviteRepository and inject it into the subject controller under test when initializing.
You need to setup something on the mock, your code just creates an empty object.
var mock = new Mock<RegisterAgencyUserRequest>();
mock.Setup(x => x.Address).Returns(() => "some address");
RegisterAgencyUserRequest request = mock.Object;
Edit: The answer from Azhar Khorasany explains how it should be done, by not mocking the POCO, but the service/repository that you use in your test.

Web API - Issues with HttpContext.Current in owin for integration testing using moq

I am building a Web API application which will be hosted in an IIS environment. In order to perform end to end integration testing of my service(no mocking), I am using OWIN.
The problem is deep down in my service architecture, at the repository layer I am making use of HttpContext.Current to retrieve values from the header(say UserId). See this answer
If you look into the above code, I am making use GetUserInfo method throughout my application to fetch current user information. Another way to do is pass it as a parameter in all method(which I don't personally want to do).
I went through this great answer about including IOwinContext into the repository. I have tried it and it worked for self-hosting, but my end goal is to deploy the application on IIS.
My Questions:
Is there any way my code can handle both the use cases of OWIN self-hosting for integration testing & actual service deployment on IIS?
Is there any issue with my architecture? Something like I shouldn't be using OWIN at all, and use other tools like POSTMAN for testing.
I can post some code if it's required.
Edit:
As suggested by #Nkosi I might have to mock my HeaderService in order to perform integration testing with owin. I am not sure how can I mock one certain method using moq. Here is my code. Its strip down version in order to make as simple as possible.
Code:
public class CreditController : ApiController
{
private readonly ICreditService _creditService;
public CreditController(ICreditService creditService)
{
_creditService = creditService;
}
public IHttpActionResult CreditSummary([FromUri]string requestId)
{
var response = _creditService.GetCreditSummary(requestId);
return Ok(response);
}
}
public class CreditService : ICreditService
{
private readonly IHeaderService _headerService;
private readonly ICreditRepository _creditRepository;
public CreditService(ICreditRepository creditRepository, IHeaderService headerService)
{
_headerService = headerService;
_creditRepository = creditRepository;
}
public CreditObj GetCreditSummary(string req)
{
var userId = _headerService.GetHeaderFromHttpRequest();//Get User
var response = _creditRepository.GetDataFromDatabase(req, userId);
return response;
}
}
public interface IHeaderService
{
string GetHeaderFromHttpRequest();
}
public class HeaderService : IHeaderService
{
public string GetHeaderFromHttpRequest()
{
return HttpContext.Current.Request.Headers["USERID"];
}
}
Below is my code for integration testing: I am using OWIN for self-host. So i want to call the controller method but my GetHeaderFromHttpRequest method should return mock response.
[TestClass]
public class IntegrationTest
{
private static HttpClient _client;
private static IDisposable _webApp;
[ClassInitialize]
public static void Init(TestContext testContext)
{
_webApp = WebApp.Start<Startup>(url: Url);
_client = new HttpClient
{
BaseAddress = new Uri(Url)
};
}
[TestMethod]
public void TestDashboard()
{
var headerStub = new Mock<IHeaderService>();
headerStub.Setup(s => s.GetHeaderFromHttpRequest())
.Returns("MockUserId");
var builder = new UriBuilder(Url + "api/Credit/CreditSummary");
HttpResponseMessage responseMessage = _client.GetAsync(builder.ToString()).Result;
Assert.IsNotNull(responseMessage);
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
WebApiConfig.Register(config); //This method having all routing/dependancy configuration
app.UseWebApi(config);
}
}
Problem:
When I debug this test case, how do I make sure that _headerService.GetHeaderFromHttpRequest() return mock response. As of now I dont know how can i inject my mocking service to actual controller method call.
Any advise?
Based on #Nkosi's suggestion I was able to mock HeaderService for my integration testing.
Here is the code:
var container = new UnityContainer();
var mock = new Mock<IHeaderService>();
mock.Setup(x => x.GetHeaderFromHttpRequest()).Returns("MockId");
container.RegisterInstance(mock.Object);
I followed this topic and use HttpContextBase in my old project.
Moq: unit testing a method relying on HttpContext
HttpContextWrapper is a wrapper for the HttpContext class, can construct an HttpContextWrapper like this:
var wrapper = new HttpContextWrapper(HttpContext.Current);
You can mock an HttpContextBase and set up your expectations on it using Moq
var mockContext = new Mock<HttpContextBase>();

How to use Moq framework to unit test azure service fabrics?

I am planning to use Moq for unit testing my azure service fabric application. I saw some of the examples here https://github.com/Azure-Samples/service-fabric-dotnet-web-reference-app/blob/master/ReferenceApp/Inventory.UnitTests/InventoryServiceTests.cs. The test I saw seems like actually writing to reliable dictionary and not mocking. Is there way to mock the add/remove from reliable dictionary? How do I unit test something like below
public async Task<bool> AddItem(MyItem item)
{
var items = await StateManager.GetOrAddAsync<IReliableDictionary<int, MyItem>>("itemDict");
using (ITransaction tx = this.StateManager.CreateTransaction())
{
await items.AddAsync(tx, item.Id, item);
await tx.CommitAsync();
}
return true;
}
First set up your DI in your services so that you can inject a mock StateManager. You can do that using a constructor that takes an IReliableStateManagerReplica as a parameter
public class MyStatefulService : StatefulService
{
public MyStatefulService(StatefulServiceContext serviceContext, IReliableStateManagerReplica reliableStateManagerReplica)
: base(serviceContext, reliableStateManagerReplica)
{
}
}
Then in your tests, when you're creating your system under test (the service), use a mock IReliableStateManagerReplica
var reliableStateManagerReplica = new Mock<IReliableStateManagerReplica>();
var codePackageActivationContext = new Mock<ICodePackageActivationContext>();
var serviceContext = new StatefulServiceContext(new NodeContext("", new NodeId(8, 8), 8, "", ""), codePackageActivationContext.Object, string.Empty, new Uri("http://boo.net"), null, Guid.NewGuid(), 0L);
var myService = new MyService(serviceContext, reliableStateManagerReplica.Object);
And then set up the reliableStateManagerReplica to return a mock reliable dictionary.
var dictionary = new Mock<IReliableDictionary<int, MyItem>>();
reliableStateManagerReplica.Setup(m => m.GetOrAddAsync<IReliableDictionary<int, MyItem>>(name).Returns(Task.FromResult(dictionary.Object));
Finally, setup any mock behaviors on your mock dictionary.
Edit: Updated sample code to use Moq properly.

ASP.NET 5 - MVC 6 - Unit Test a Controller that uses Url.Action

I have an MVC6 controller with the following line of code:
string link = Url.Action("Profile", "Account");
When I unit test this controller, that line fails with an error:
Value cannot be null.
Parameter name: helper
I don't really want to mock the Url.Action response plus I don't think I can because it's an extension method (static method). I want it to run and return like it would in a web environment.
What do I need to do when instantiating the controller in my unit test so that I the line above will execute as expected?
I see that I can do something like this in my unit test:
controller.Url = new UrlHelper(new ActionContextAccessor(), new DefaultActionSelector(...));
But I can't figure out what is needed to setup the ActionContextAccessor and/or the DefaultActionSelector (which requires more types that I'm not sure where to obtain or how to instantiate).
Has anyone already done this?
Thanks,
Kevin
My wife always tells me to just "walk away" from a problem when I'm stuck. And she's almost always right.
After realizing the solution above wasn't going to work (because it's MVC5), I took another look and realized that controller.Url is just an instance of IUrlHelper. I mocked that and poof the tests started to work. Here's the gist of what I'm doing. This made the test able to execute. I'm sure I can add some more to the mocking to actually verify it's functioning as expected.
Mock<IUrlHelper> urlHelperMock = new Mock<IUrlHelper>();
var controller = new BooksController();
controller.Url = urlHelperMock.Object;
Yeah, it was that easy LOL.
Thanks,
Kevin
Given:
namespace SampleWebApplication.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
string link = Url.Action("Profile", "Account");
return View();
}
}
}
This test seems to run through OK:
using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SampleWebApplication.Controllers;
namespace SampleUnitTestProject
{
[TestClass]
public class HomeTests
{
private Mock<HttpRequestBase> RequestMock;
private Mock<HttpResponseBase> ResponseMock;
private Mock<HttpContextBase> ContextMock;
[TestInitialize]
public virtual void Setup()
{
this.RequestMock = new Mock<HttpRequestBase>();
this.ResponseMock = new Mock<HttpResponseBase>();
this.RequestMock.Setup(m => m.QueryString).Returns(new NameValueCollection());
this.RequestMock.Setup(m => m.Url).Returns(new Uri("http://www.somedomain.com"));
this.ContextMock = new Mock<HttpContextBase>();
this.ContextMock.Setup(m => m.Request).Returns(this.RequestMock.Object);
this.ContextMock.Setup(m => m.Response).Returns(this.ResponseMock.Object);
}
[TestMethod]
public void Test_Index()
{
// Arrange
using (var controller = new HomeController())
{
this.RequestMock.Setup(c => c.ApplicationPath).Returns("/tmp/testpath");
this.ResponseMock.Setup(c => c.ApplyAppPathModifier(It.IsAny<string>())).Returns("/mynewVirtualPath/");
var requestContext = new RequestContext(this.ContextMock.Object, new RouteData());
controller.Url = new UrlHelper(requestContext, new RouteCollection());
// Act
var result = controller.Index();
// Assert
}
}
}
}

Unit testing with Moq and EF6

I have built unit testing for my service layer. I have not used Mock as I think that since you are adding/deleting/querying a database, why query a mock as the results could be different, but that isn't what I am asking.
Now I am using Moq to test my web api layer. I think that this is fine, as if all my tests pass on the service layer, it is fine to mock the services to test the web api.
I have managed to write a test for my GetAsync method and it works all fine, like so
Here is the controller:
public async Task<IHttpActionResult> GetAsync(long id)
{
Content content = await _service.GetAsync(id);
ContentModel model = Mapper.Map<ContentModel>(content);
return Ok(model);
}
Here is the test:
[TestMethod]
public void Content_GetAsync()
{
// arrange
var mockService = new Mock<IContentService>();
mockService.Setup(x => x.GetAsync(4))
.ReturnsAsync(new Content
{
Id = 4
});
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.GetAsync(4).Result;
var contentResult = actionResult as OkNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual(4, contentResult.Content.Id);
}
I believe I wrote this correctly, and it seems to work. Now I would like to test my PostAsync method to add an item. The controller looks like this:
public async Task<IHttpActionResult> PostAsync(ContentModel model)
{
Content content = Mapper.Map<Content>(model);
await _service.AddAsync(content);
return Created<ContentModel>(Request.RequestUri, Mapper.Map<ContentModel>(content));
}
And here is the test:
[TestMethod]
public void Content_PostAsync()
{
var mockService = new Mock<IContentService>();
mockService.Setup(e => e.AddAsync(new Content()))
.ReturnsAsync(1);
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}).Result;
var contentResult = actionResult as CreatedAtRouteNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual("New Heading", contentResult.Content.Heading);
}
Now when I run this, I get an error:
null reference exception. "Request" from the Request.RequestUri is null.
So I changed my controller and tests to this, to try and mock it.
Test code:
public Task<IHttpActionResult> PostAsync(ContentModel model)
{
return PostAsync(model, Request);
}
/// Unit testable version of above. Cannot be accessed by users
[NonAction]
public async Task<IHttpActionResult> PostAsync(ContentModel model, System.Net.Http.HttpRequestMessage request)
{
Content content = Mapper.Map<Content>(model);
await _service.AddAsync(content);
return Created<ContentModel>(request.RequestUri, Mapper.Map<ContentModel>(content));
}
Controller code:
[TestMethod]
public void Content_PostAsync()
{
// arrange
var mockRequest = new Mock<System.Net.Http.HttpRequestMessage>();
mockRequest.Setup(e => e.RequestUri)
.Returns(new Uri("http://localhost/"));
var mockService = new Mock<IContentService>();
mockService.Setup(e => e.AddAsync(new Content()))
.ReturnsAsync(1);
// setup automapper
AutoMapperConfig.RegisterMappings();
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}, mockRequest.Object).Result;
var contentResult = actionResult as CreatedAtRouteNegotiatedContentResult<ContentModel>;
// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual("New Heading", contentResult.Content.Heading);
}
Now I get an error saying:
Invalid setup on a non-virtual (overridable in VB) member: e => e.RequestUri
Can someone please, please help me with this. I am sure I am using Mock correctly in all tests, but unit testing is new to me, so maybe I am just not doing something right.
With Moq you can only mock virtual/absrtact members. The RequestUri is not a virtual member of HttpRequestMessage, hence the error message.
You should be able to just new a HttpRequestMessage directly without mocking it and pass that in.
var request = System.Net.Http.HttpRequestMessage>();
request.RequestUri = new Uri("http://localhost/");
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}, request).Result;
Ned's answer is correct. Moq is a constrained mocking library, meaning that it generates dynamic subclasses of the classes you mock at runtime. These subclasses cannot override methods if they are not declared virtual in the mocked class. You can find more information on constrained vs. unconstrained mocking libraries in the art of unit testing.
That's why people that use a mockist style of unit testing prefer to mock against interfaces instead of concrete classes, as the generated mock subclasses can easily override (or rather, implement) the methods on the interface.

Categories

Resources