I write web application using ASP.NET MVC WebAPI and I want to transform current synchronous code to asynchronous for optimization. Problem is that I fill ViewModel with multiple objects taken from repository. These calls from repository should be async.
Let's asume I have signature for repository calls respecting this interface
public interface ICompanyRepository
{
IEnumerable<Company> GetCompanies();
IEnumerable<Address> GetAddresses();
}
ViewModels definition
public class CompaniesFullViewModel
{
public IEnumerable<Company> Companies { get; set; }
public IEnumerable<Address> Addresses { get; set; }
}
And controller:
public class CompanyController
{
public readonly ICompanyRepository Repository { get; private set; }
public CompanyController(IRepository repository)
{
Repository = repository;
}
[ResponseType(typeof(CompaniesFullViewModel))]
public HttpResponseMessage Get()
{
var companies = Repository.GetCompanies();
var addresses = Repository.GetAddresses();
HttpStatusCode statusCode = companies.Any()
? HttpStatusCode.OK
: HttpStatusCode.PartialContent;
return
Request.CreateResponse(
statusCode,
new CompaniesFullViewModel
{
Companies = companies,
Addresses = addresses
});
}
}
Furthermore I have tests implemented to the controller:
[TestClass]
public sealed class CompanyTestController : BaseTestController
{
#region Fields
private static Mock<ICompanyRepository> _repositoryMock;
private static CompanyController _controller;
#endregion
[ClassInitialize]
public static void Initialize(TestContext testContext)
{
// Mock repository
_repositoryMock = new Mock<ICompanyRepository>();
DependencyResolver.Default.Container.RegisterInstance(_repositoryMock.Object);
// Create controller
_controller =
DependencyResolver.Default.Container.Resolve<CompanyController>();
// Init request
_controller.Request = new HttpRequestMessage();
_controller.Request.SetConfiguration(new HttpConfiguration());
}
[ClassCleanup]
public static void Cleanup()
{
_controller.Dispose();
}
[TestMethod]
public void Get_ActionExecutes_ReturnsEmptyCompaniesViewModel()
{
var companies = new List<Company>();
var addresses = new List<Address>();
// Setup fake method
_repositoryMock
.Setup(c => c.GetCompanies())
.Returns(companies);
_repositoryMock
.Setup(c => c.GetAddresses())
.Returns(addresses);
// Execute action
var response = _controller.Get();
// Check the response
Assert.AreEqual(HttpStatusCode.PartialContent, response.StatusCode);
}
}
How can I convert the controller to async, if the repository is async and the signature looks like this:
public interface ICompanyRepository
{
Task<IEnumerable<Company>> GetCompaniesAsync();
Task<IEnumerable<Address>> GetAddressesAsync();
}
What you need to do is change the Controller action to be async as well, and change the return type to Task<>. You can then await your asynchronous repository calls:
[ResponseType(typeof(CompaniesFullViewModel))]
public async Task<HttpResponseMessage> Get() // async keyword.
{
var companies = await Repository.GetCompaniesAsync(); // await
var addresses = await Repository.GetAddressesAsync(); // await
HttpStatusCode statusCode = companies.Any()
? HttpStatusCode.OK
: HttpStatusCode.PartialContent;
return
Request.CreateResponse(
statusCode,
new CompaniesFullViewModel
{
Companies = companies,
Addresses = addresses
});
}
By convention, you can also change the name of the controller action to end in Async as well, although if you are using RESTful conventions and / or Routing attributes, the actual name of the controller action isn't really important.
Testing
I use XUnit and NUnit, but it seems MSTest also supports testing of asynchronous methods, and Moq also provides Async versions of the setups:
[Test]
public async Task Get_ActionExecutes_ReturnsEmptyCompaniesViewModel() // async Task
{
var companies = new List<Company>();
var addresses = new List<Address>();
// Setup fake method
_repositoryMock
.Setup(c => c.GetCompaniesAsync())
.ReturnsAsync(companies); // Async
_repositoryMock
.Setup(c => c.GetAddressesAsync())
.ReturnsAsync(addresses); // Async
// Execute action
var response = await _controller.Get(); // Await
// Check the response
Assert.AreEqual(HttpStatusCode.PartialContent, response.StatusCode);
_repositoryMock.Verify(m => m.GetAddressesAsync(), Times.Once);
_repositoryMock.Verify(m => m.GetCompaniesAsync(), Times.Once);
}
As an aside, it seems you are using Setter Dependency injection. An alternative is to use Constructor injection, which has the benefit of ensuring that the class is always in a valid state (i.e. there is no transient state while it is waiting for the dependencies to be set). This also allows the dependencies (your repository in this case) to be made readonly.
Related
I am new to Integration test, I have a controller which has IMediator and I am using Moq framework for the integration test.
The issue I am having is that I keep getting null in the response when trying to mock MediatR. Before trying to mock MediatR, I tried to mock a service (in this case IUserService) and it worked perfectly (the return type for the Delete controller and other methods was bool).
Since I am now using IMediator in the controller so trying to change the integration test to mock MediatR, I can do integration test for the handler which has IUserService but I am trying to test the whole pipeline. Below is what I have in terms of code starting from the controller to the integration test.
//Controller
private IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
[HttpDelete("{id}")]
public async Task<ActionResult<Result<Unit>>> DeleteUser(int id)
{
return await _mediator.Send(new DeleteUserCommand { UserId = id });
}
public class DeleteUserCommand : IRequest<Result<Unit>> {public int UserId { get; set; }}
//Command handler
public class DeleteUserCommandHandler : IRequestHandler<DeleteUserCommand, Result<Unit>>
{
private readonly IUserService _userService;
public DeleteUserCommandHandler(IUserService userService)
{
_userService = userService;
}
public async Task<Result<Unit>> Handle(DeleteUserCommand request, CancellationToken cancellationToken)
{
return await _userService.DeleteUser(request.UserId);
}
}
//Service layer
public async Task<Result<Unit>> DeleteUser(int userId)
{
var userExist = await _context.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (userExist == null) return Result<Unit>.Failure("User Id doesn't exsist");
_context.Remove(userExist);
var result = await _context.SaveChangesAsync() > 0;
return Result<Unit>.Success(Unit.Value);
}
//Integration test
[TestFixture]
public class UserControllerTests
{
private readonly Mock<IMediator> _mockMediator;
private UserController _userController;
public UserControllerTests()
{
_mockMediator = new Mock<IMediator>();
}
[Test]
public async Task DeleteUser_NotSuccessful_NoIdDeleted()
{
Result<Unit> expected = new Result<Unit>();
_mockMediator.Setup(x => x.Send(It.IsAny<DeleteUserCommand>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(expected));
_userController = new UserController(_mockMediator.Object);
var result = await _userController.DeleteUser(6);
Assert.AreEqual("User Id doesn't exsist", result?.Value?.Error);
}
}
//Response in the integration test
I have two UserIds 6 and 7
UserId 6: doesn't exsist so I am expecting a message saying id doesn't exsist but the current response I am getting is null.
UserId 7: does exsist and expecting something like IsSuccess: true which is a custom code I added
Note: the attached code for the test is just for user Id 6.
You might notice in the code above starting from the controller, part of the return type is Result which is a custom class I added and below is the code.
public class Result<T>
{
public bool IsSuccess { get; set; }
public T Value { get; set; }
public string Error { get; set; }
public static Result<T> Success(T value) => new Result<T> { IsSuccess = true, Value = value };
public static Result<T> Failure(string error) => new Result<T> { IsSuccess = false, Error = error };
}
`
I am trying to find out what I have done wrong with mocking MediatR and why it keep returning null.
Thanks for the help in advance.
First thing I tried to do integration test mock the IUserService then switched to IMediator then started to get null in the response when doing integration test. I tried to google the issue but no luck.
So I have a controller that is using HttpClient to call a webservice like so:
public class DemoController : Controller
{
HttpClient client;
string baseUrl = "http://localhost:90/webservice";
public DemoController()
{
client = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
}
// GET: DemoInfo
public async Task<ActionResult> Index()
{
HttpResponseMessage response = await client.GetAsync(baseUrl + "vehicle/menu/year");
string content = "";
MenuItems result = null;
if (response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
result = (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(new StringReader(content));
}
return View("Index", result);
}
}
My unit test for this action is as follows:
[TestMethod]
public async Task Test_Index()
{
// Arrange
DemoController controller = new DemoController();
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult) result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
So obviously I would like to avoid making the web service call every time the test is run. Would I be on the right track in opting for an IoC container like Unity so that HttpClient would be injected into the controller? Is that overkill for what I'm trying to achieve? I'm aware that there is a lot of history with people struggling with properly mocking httpclient in there unit tests through this github issue. Any help would be greatly appreciated in giving some insight into how to write the controller to make a service call while still being testable.
All dependencies which makes tests slow should be abstracted.
Wrap HttpClient with an abstraction, which you can mock in your tests.
public interface IMyClient
{
Task<string> GetRawDataFrom(string url);
}
Then your controller will depend on that abstraction
public class DemoController : Controller
{
private readonly IMyClient _client;
private string _baseUrl = "http://localhost:90/webservice";
public DemoController(IMyClient client)
{
_client = client;
}
public async Task<ActionResult> Index()
{
var rawData = _client.GetRawDataFrom($"{_baseUrl}vehicle/menu/year");
using (var reader = new StringReader(rawData))
{
var result =
(MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(reader);
return View("Index", result);
}
}
}
Then in tests you can mock your abstraction to return expected data
public class FakeClient : IMyClient
{
public string RawData { get; set; }
public Task<string> GetRawDataFrom(string url)
{
return Task.FromResult(RawData);
}
}
[TestMethod]
public async Task Test_Index()
{
// Arrange
var fakeClient = new FakeClient
{
RawData = #"[
{ Name: "One", Path: "/one" },
{ Name: "Two", Path: "/two" }
]"
};
DemoController controller = new DemoController(fakeClient);
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult)result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
Actual implementation will use HttpClient
public class MyHttpClient : IMyClient
{
public Task<string> GetRawDataFrom(string url)
{
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
}
}
An alternative approach to testing HttpClient calls without service wrappers, mocks, or IoC containers is to use Flurl, a small wrapper library around HttpClient that provides (among other things) some robust testing features. [Disclaimer: I'm the author]
Here's what your controller would look like. There's a few ways to do this, but this approach uses string extension methods that abstract away the client entirely. (A single HttpClient instance per host is managed for you to prevent trouble.)
using Flurl.Http;
public class DemoController : Controller
{
string baseUrl = "http://localhost:90/webservice";
// GET: DemoInfo
public async Task<ActionResult> Index()
{
var content = await baseUrl
.AppendPathSegment("vehicle/menu/year")
.GetStringAsync();
var result = (MenuItems)new XmlSerializer(typeof(MenuItems)).Deserialize(new StringReader(content));
return View("Index", result);
}
}
And the test:
using Flurl.Http;
[TestMethod]
public async Task Test_Index()
{
// fake & record all HTTP calls in the test subject
using (var httpTest = new HttpTest())
{
// Arrange
httpTest.RespondWith(200, "<xml>some fake response xml...</xml>");
DemoController controller = new DemoController();
// Act
var result = await controller.Index();
ViewResult viewResult = (ViewResult) result;
// Assert
Assert.AreEqual("Index", viewResult.ViewName);
Assert.IsNotNull(viewResult.Model);
}
}
Flurl.Http is available on NuGet.
I'm trying to unit test an action in an MVC controller but the mocked object is returning null.
I have a ClientProxy interface
public interface IClientProxy
{
Task<T> Get<T>(string uri);
}
With the following concrete implementation
public async Task<T> Get<T>(string uri)
{
using (HttpClient httpClient = new HttpClient())
{
ConfigureClient(httpClient);
var response = await httpClient.GetAsync(uri);
response.EnsureSuccessStatusCode();
return await Task.Run(() => GetResultFromResponse<T>(response));
}
}
This is my action
public async Task<ActionResult> Index()
{
var categories = await _proxy.Get<PagedResults<Category>>("/category/get");
return View("index", null, JsonConvert.SerializeObject(categories));
}
And this is the unit test class
public class CategoryControllerTests
{
private readonly CategoryController _controller;
private readonly Mock<IClientProxy> _mockProxy;
public CategoryControllerTests()
{
_mockProxy = new Mock<IClientProxy>();
_controller = new CategoryController(_mockProxy.Object);
}
[Fact]
public async Task IndexPageRenders()
{
// Arrange
var fakeResult = new PagedResults<Category>
{
Paging = new Paging
{
Page = 1,
PageSize = 10,
TotalRecords = 2
},
Results = new List<Category>
{
new Category { CategoryId = 1, Name = "Category One" },
new Category { CategoryId = 2, Name = "Category Two" }
}
};
var result = JsonConvert.SerializeObject(fakeResult);
_mockProxy.Setup(p => p.Get<PagedResults<Category>>(It.IsAny<string>())).ReturnsAsync(fakeResult);
// Act
var action = await _controller.Index() as ViewResult;
// Assert
_mockProxy.Verify(p => p.Get<PagedResults<Category>>(It.IsAny<string>()), Times.Exactly(1));
}
}
Currently the mocked object setup for the get method is not getting hit, and categories are null (the Verify is failing, implying that the mocked method is never called). However, if I remove the await keyword from the action then the categories are returned. I'd rather not remove the await just to pass the test as it's there for a good reason, so any help would be appreciated.
Thanks in advance.
Currently the mocked object setup for the get method is not getting hit, and categories are null (the Verify is failing, implying that the mocked method is never called).
That would mean that the mocked object was not passed as a dependency to the controller being tested and as such is not being invoked when the test is being exercised.
Here is a Minimal, Complete, and Verifiable example that demonstrates how one would setup and exercise a test in such a scenario.
The follow were used
public class MyModel {
public int MyIntProperty { get; set; }
public string MyStringProperty { get; set; }
}
public interface IClientProxy {
Task<T> Get<T>(string uri);
}
public class MyController : Controller {
IClientProxy _proxy;
public MyController(IClientProxy _proxy) {
this._proxy = _proxy;
}
public async Task<ActionResult> Index() {
var categories = await _proxy.Get<MyModel>("/category/get");
return View(categories);
}
}
And following test was used with MSTest, Moq and Fluent Assertions where is flowed to completion and passed.
[TestClass]
public class MyController_Should {
[TestMethod]
public async Task _Render_Index_Page() {
// Arrange
var fakeResult = new MyModel {
MyIntProperty = 0,
MyStringProperty = "Hello World."
};
var _mockProxy = new Mock<IClientProxy>();
_mockProxy
.Setup(_ => _.Get<MyModel>(It.IsAny<string>()))
.ReturnsAsync(fakeResult);
var _controller = new MyController(_mockProxy.Object);
// Act
var action = await _controller.Index() as ViewResult;
// Assert
_mockProxy.Verify(_ => _.Get<MyModel>(It.IsAny<string>()), Times.Exactly(1));
action.Should().NotBeNull();
var model = action.Model;
model.Should().NotBeNull()
.And.BeOfType<MyModel>()
.And.Be(fakeResult);
}
}
I would suggest reviewing this example and then comparing it to how you designed your test to see if you can identify what may be causing your problem.
I got two console applications which calls my webapi the same time and I get back in the console application the follow response from my api:
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
So they call at the same time my webapi and then something inside the webapi cannot handle those 2 async calls so this error is returned.
I checked all my code on the webapi project and all methods are async and got await so I cannot see why I get this.
Here is the code of the webapi.
Controller:
public class FederationsController : ApiController
{
private readonly IFederationRepository _federationRepository;
public FederationsController(IFederationRepository federationRepository)
{
_federationRepository = federationRepository;
}
[HttpGet]
[Route("federations", Name = "GetFederations")]
public async Task<IHttpActionResult> GetFederations()
{
var federations = await _federationRepository.GetAllAsync();
return Ok(federations.ToModel());
}
}
Repository
public class FederationRepository : IFederationRepository, IDisposable
{
private Models.DataAccessLayer.CompetitionContext _db = new CompetitionContext();
#region IQueryable
private IQueryable<Models.Entities.Federation> FederationWithEntities()
{
return _db.Federations.Include(x => x.Clubs)
.Where(x => !x.DeletedAt.HasValue && x.Clubs.Any(y => !y.DeletedAt.HasValue));
}
#endregion IQueryable
public async Task<IEnumerable<Models.Entities.Federation>> GetAllAsync()
{
return await FederationWithEntities().ToListAsync();
}
}
Mapper
public static class FederationMapper
{
public static List<Federation> ToModel(this IEnumerable<Models.Entities.Federation> federations)
{
if (federations == null) return new List<Federation>();
return federations.Select(federation => federation.ToModel()).ToList();
}
public static Federation ToModel(this Models.Entities.Federation federation)
{
return new Federation()
{
Name = federation.Name,
FederationCode = federation.FederationCode,
CreatedAt = federation.CreatedAt,
UpdatedAt = federation.UpdatedAt
};
}
}
DbContext
public class CompetitionContext : DbContext
{
public CompetitionContext() : base("ContextName")
{
}
public DbSet<Federation> Federations { get; set; }
}
UnityConfig
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IFederationRepository, FederationRepository>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
Thank you for all the advices/help.
In your repository you are creating a single CompetitionContext and reusing it. I'm assuming that IoC setup is registring the repository as some kind of single instance, so the same repository is getting used every time. If that's the case you should create a new CompetitionContext for each method call.
Also, probably should make sure it's closed with a using statement.
I'm also not clear from your code snippets why you are returning an IQueryable from that FederationWithEntities, method, do you have other things that are using it?
Anyway, I'd probably change that GetAllMethod to be something like this:
public async Task<IEnumerable<Models.Entities.Federation>> GetAllAsync()
{
using (Models.DataAccessLayer.CompetitionContext _db = new CompetitionContext())
{
return _db.Federations.Include(x => x.Clubs)
.Where(x => !x.DeletedAt.HasValue && x.Clubs.Any(y => !y.DeletedAt.HasValue))
.ToListAsync();
}
}
I'm new to unit testing, so my problem is probably with my code and not the Moq framework, but here goes.
I'm using .Net Core with xUnit and the Moq framework, and I'm more or less following instructions from their documentation. I'm trying to test route api/user to get all users, and the issue was on asserting that the response was an ObjectResult containing <IEnumerable<User>>. No matter what I tried, result.Value was always null. The first assertion passes fine.
I set up a console project to debug this, and found something interesting. that value of the controller in the test method in Visual Studio is null. In VS Code, the value in the debugger shows Unknown Error: 0x00000....
Below is the test:
public class UserControllerTests {
[Fact]
public void GetAll_ReturnsObjectResult_WithAListOfUsers() {
// Arrange
var mockService = new Mock<IUserService>();
var mockRequest = new Mock<IServiceRequest>();
mockService.Setup(svc => svc.GetUsers(mockRequest.Object))
.Returns(new ServiceRequest(new List<User> { new User() }));
var controller = new UserController(mockService.Object);
// Act
var result = controller.GetAll();
// Assert
Assert.IsType<ObjectResult>(result);
Assert.IsAssignableFrom<IEnumerable<User>>(((ObjectResult)result).Value);
}
}
And here is the controller:
public class UserController : Controller {
private IUserService service;
public UserController(IUserService service) {
this.service = service;
}
[HttpGet]
public IActionResult GetAll() {
var req = new ServiceRequest();
service.GetUsers(req);
if(req.Errors != null) return new BadRequestObjectResult(req.Errors);
return new ObjectResult(req.EntityCollection);
}
}
And the Service Layer:
public interface IUserService {
IServiceRequest GetUsers(IServiceRequest req);
}
public class UserService : IUserService {
private IUserRepository repo;
public IServiceRequest GetUsers(IServiceRequest req) {
IEnumerable<User> users = null;
try {
users = repo.GetAll();
}
catch(MySqlException ex) {
req.AddError(new Error { Code = (int)ex.Number, Message = ex.Message });
}
finally {
req.EntityCollection = users;
}
return req;
}
}
public interface IServiceRequest {
IEnumerable<Object> EntityCollection { get; set; }
List<Error> Errors { get; }
void AddError(Error error);
}
public class ServiceRequest : IServiceRequest {
public IEnumerable<Object> EntityCollection { get; set; }
public virtual List<Error> Errors { get; private set; }
public ServiceRequest () { }
public void AddError(Error error) {
if(this.Errors == null) this.Errors = new List<Error>();
this.Errors.Add(error);
}
}
Like I said, it's probably something I'm doing wrong, I'm thinking in the mockService.Setup() but I'm not sure where. Help please?
From the use of service.GetUsers(req) it looks like service is suppose to populate the service request but in your setup you have it returning a service request. A result which is also not used according to your code.
You need a Callback to populate whatever parameter is given to the service in order to mock/replicate when it is invoked. Since the parameter is being created inside of the method you will use Moq's It.IsAny<> to allow the mock to accept any parameter that is passed.
var mockService = new Mock<IUserService>();
mockService.Setup(svc => svc.GetUsers(It.IsAny<IServiceRequest>()))
.Callback((IServiceRequest arg) => {
arg.EntityCollection = new List<User> { new User() };
});
This should allow the method under test to flow through it's invocation and allow you to assert the outcome.