I have a controller method in my webapi project which calls a service method and converts the response of the service method into a Dto object for sending the response.
The controller looks something like this:
[HttpPost]
[Route(WebApiConfig.RootApiUri + "/v1/examplepost")]
public async Task<List<Html1Dto>> examplepost([FromBody] SomeInfoDto someInfoDto)
{
var someInfo = _mapper.Map<SomeInfoDto, SomeInfo>(someInfoDto);
return this._mapper.Map<List<Html1>, List<Html1Dto>>(await this._someService.SomeCall(someInfo));
}
and the mock test like this :
//Arrange
var mockMapper = new Mock<IMapper>();
var mockSomeService = new Mock<ISomeService<Html1>>();
mockSomeService.Setup(s => s.SomeCall(It.IsAny<SomeInfo>())).ReturnsAsync(
new List<Html1>() {new Html1() {....}});
SomeInfoDto mockSomeInfoDto = new SomeInfoDto()
{
..
};
SomeInfo mockSomeInfo = new SomeInfo();
mockMapper.Setup(m => m.Map<SomeInfoDto, SomeInfo>(mockSomeInfoDto))
.Returns(mockSomeInfo);
mockMapper.Setup(m => m.Map<List<Html1>, List<Html1Dto>>(It.IsAny<List<Html1>>())).Returns(It.IsAny<List<Html1Dto>>);
var someController = GetController(mockMapper.Object, mockSomeService.Object);
//Act
var result = await someController.examplePost(mockSomeInfoDto);
I am using automapper to map the objects with Dtos. When I debug this test, result comes as null. The mapping of incoming dto works fine. I suspect there is some issue with the service method setup. Anyhelp is appreciated.
Your mapper mock is the other way round
mockMapper.Setup(m => m.Map<List<Html1>, List<Html1Dto>>(It.IsAny<List<Html1>>())).Returns(It.IsAny<List<Html1Dto>>);
to the signature in the method
this._mapper.Map<List<Html1Dto>, List<Html1>>(await this._someService.SomeCall(someInfo));
Additionally, assuming that is correct in your actual code, then the other bit that could be causing you issue is that the return It.IsAny<List<Html1Dto>> which will be null as default(List<HtmlDto>) is null, return a concrete class there instead as below.
This call:
this._mapper.Map<List<Html1Dto>, List<Html1>>(await this._someService.SomeCall(someInfo));
Doesn't have a setup in the Unit Test, so will return null. You need to arrange that to, probably something like:
mockMapper.Setup(m => m.Map<List<Html1Dto>, List<Html1>>(It.IsAny<List<Html1>>()))
.ReturnsAsync(new List<Html1Dto> { ... });
Related
My xunit test is failing because the expected Id isn't updated by the database when unit testing.
How do I mock the id/primary key being created by the database?
[HttpPost]
public async Task<ActionResult<AccountReadDto>> CreateAccountAsync(AccountCreateDto createAccountDto)
{
var account = _mapper.Map<Account>(createAccountDto); // Id is int equal to 0
await _accountRepo.CreateAccountAsync(account); // repo calls db and updates Id with pk
_accountRepo.SaveChanges();
var accountReadDto = _mapper.Map<AccountReadDto>(account);
return CreatedAtRoute(nameof(GetAccountByIdAsync), new { AccountId = accountReadDto.AccountId }, accountReadDto);
}
[Fact]
public async Task CreateAccountAsync_NewAccount_ReturnsAccountReadDto()
{
var expectedAccount = CreateRandomAccount(); // has id
var createDto = _mapperStub.Map<AccountCreateDto>(expectedAccount); // doesn't have id
_repoStub
.Setup(repo => repo.CreateAccountAsync(expectedAccount))
.Returns(Task.CompletedTask);
var controller = new AccountController(_repoStub.Object, _mapperStub);
var actionResult = await controller.CreateAccountAsync(createDto);
var result = (actionResult.Result as CreatedAtRouteResult).Value as AccountReadDto;
result.Should().BeEquivalentTo(
expectedAccount,
options => options.ComparingByMembers<AccountReadDto>().ExcludingMissingMembers() // excluding login info
);
}
This code is not correct:
_repoStub
.Setup(repo => repo.CreateAccountAsync(expectedAccount))
It's setups CreateAccountAsync just for case when you will pass expectedAccount instance as argument. (After mapping it's not the same). Use It.IsAny<T>() to configure method to work with any argument. Then use Callback<T>(Action<T>) to access argument passed to mock call:
_repoStub
.Setup(repo => repo.CreateAccountAsync(It.IsAny<Account>()))
.Callback<Account>(a => a.Id = expectedAccount.Id)
.Returns(Task.CompletedTask);
In that code you configured CreateAccountAsync method to return Task.CompletedTask and modify incoming argument id to expectedAccount.Id.
Returns(Task.CompletedTask) can be ommited. (It's default behaviour)
Additional info about Callback<T>: link
I am trying to mock a service, and receiving the following error below in unit test. How can this be resolved? What does this error mean? Is it something maybe with await/async?
services.AddSingleton(a =>
{
var mock = new Mock<IUserResolverService>();
mock.Setup(b => b.GetUser()).Returns(5);
return mock.Object;
});
System.NotSupportedException : Can not apply commission concerns to component Common.Services.IUserResolverService_175a16cd-d798-4fbd-b343-1ba0eb5e0ad6 because it appears to be a target-less proxy. Currently those are not supported. DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy) IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy) ScopedWindsorServiceProvider.GetServiceInternal(Type serviceType, Boolean isOptional) line 55
More Code Data:
private async Task<IServiceProvider> GetProvider()
{
var services = new ServiceCollection();
services.AddSingleton(new Mock<ISharedServicesApiClient>().Object);
services.AddSingleton(new Mock<IAddressValidator>().Object);
services.AddSingleton(new Mock<IAddressValidationService>().Object);
services.RegisterMappingProfiles(
new PropertyManagementDataMappingProfile(),
new ApplicationServicesMappingProfile()
);
services.AddSingleton(a =>
{
var mock = new Mock<IUserResolverService>();
mock.Setup(b => b.GetUser()).Returns(5);
return mock.Object;
});
services.AddDbContext<PropertyContext>(
a => a.UseInMemoryDatabase("TestDb").UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll),
ServiceLifetime.Singleton);
services.AddDbContext<AuditablePropertyContext>(
a => a.UseInMemoryDatabase("TestDb").UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll),
ServiceLifetime.Singleton);
services.AddSingleton<DbContext, AuditablePropertyContext>();
services.AddSingleton<DbContext, PropertyContext>();
services.AddSingleton<IAddressAppService, AddressAppService>();
services.AddSingleton<IAddressRepository, AddressRepository>();
services.AddSingleton<IPartyContactRepository, PartyContactRepository>();
services.AddSingleton<IPartyMailingAddressRepository, PartyMailingAddressRepository>();
var provider = services.GetServiceProvider();
var db = provider.GetRequiredService<PropertyContext>();
await db.AddTestData();
return provider;
}
[Fact]
public async Task AddNewAddressAlternateAddTest()
{
var provider = await GetProvider();
var manager = provider.GetRequiredService<IAddressAppService>();
var request =
SitusAndPartyAddressDataSeed.GetSingleApnRequestForAdd(AddressType.Alternate);
var result = await manager.UpdatePartyAndSitusAddresses(request);
// new AddressId is 3
Assert.True(result.Body == NewAddressId);
Following up on the "hunch" with some reasoning, you're setting up a singleton for a mocked instance of IUserResolverService, but as written you're uniquely composing and returning that mocked object using a delegate rather than just creating a single instance of the completed mock and registering it instead like you are with all the other ones. Given the blend of synchronous and asynchronous operations, it looks like your test is causing Moq to struggle with the lifecycle/creation of the object.
If we change this:
services.AddSingleton(a =>
{
var mock = new Mock<IUserResolverService>();
mock.Setup(b => b.GetUser()).Returns(5);
return mock.Object;
});
To this:
var mock = new Mock<IResolverService();
mock.Setup(b => b.GetUser()).Returns(5);
services.AddSingleton(mock.Object);
You're then passing in the actual single instance of the mock instead, which might resolve whatever is causing the issue, be it the lifecycle or late binding (or something else, but hey).
I don't understand how you would test something like the following controller.
How would you mock Request? It seems to me that to mock it you would need to pass in a Request to the method but that is wrong. Or you would need to inject it into the Controller constructor but that seems wrong too.
I totally get how this would work with ISomethingService or ISomethingRepository but for intrinsic dependancies I just don't get it.
Thanks
public ActionResult Test()
{
return View(Request.Browser.Crawler ? "A" : "B");
}
you need to create a mock http context. there are multiple libraries to do so, but you basically need to do something like this:
var request = new HttpRequest("", "http://localhost/", "");
var writer = new StringWriter();
var response = new HttpResponse(writer);
var context = new HttpContext(request, response);
Goal: test that a given url returns a given controller function.
In the process, I've broken into the routing system and I can't figure out how to test routes (or, for that matter, find the controller corresponding to the route :-/).
Sample code, which doesn't work:
[Test]
public void kick_the_tires()
{
var rc = new RouteCollection();
Infrastructure.RouteRegistry.RegisterRoutes(rc);
// get the route corresponding to name.
var got = rc["name"];
var expected = //What? foo is an internal type that can't be instantiated.
Assert.AreEqual(foo, frob);
}
edit: Using the linked blog post from Simon for the stub class.
[TestCase("/", "~/", "Home", "Index")]
[TestCase("/", "api/command", "Other", "command")]
internal void stub_mocker(string apppath, string route, string expected_controller,\
string expected_action)
{
var rc = new RouteCollection();
Infrastructure.RouteRegistry.RegisterRoutes(rc);
var httpmock = new StubHttpContextForRouting(
appPath: apppath,
requestUrl: route);
// this always returns null for everything but the Index case.
var routeData = rc.GetRouteData(httpmock);
var controller = routeData.Values["controller"];
var action = routeData.Values["action"];
Assert.AreEqual(expected_controller, controller);
Assert.AreEqual(expected_action, action);
}
All you are testing right now is if the routes are added to the collection, by accessing it by the route name, and not if the expected route will return given a virtual path. You need to obtain the route data as returned by the RouteCollection with a HttpContext.
Best way would be to use a mock or a stub for the HttpContext (or HttpContextBase) and call the RouteCollection's GetRouteData(HttpContextBase) method and inspect the route data.
There is a good example of this in Brad Wilson's blog:
http://bradwilson.typepad.com/blog/2010/07/testing-routing-and-url-generation-in-aspnet-mvc.html
Edit:You cannot get a controller instance from the RouteData itself. However, RouteData should give you enough information to know which controller will be instantiated. For example, if you have a controller at MyProject.Controllers.HomeController with an action Home, this should hold true in your test (using xUnit and Moq):
// Prepare
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
context.SetupGet(c => c.Request).Returns(request.Object);
context.SetupGet(c => c.Response).Returns(response.Object);
context.SetupGet(c => c.Session).Returns(session.Object);
context.SetupGet(c => c.Server).Returns(server.Object);
request.SetupGet(r => r.HttpMethod).Returns("GET");
request.SetupGet(r => r.PathInfo).Returns(String.Empty);
request.SetupGet(r => r.AppRelativeCurrentExecutionFilePath).Returns("~/Home");
var expectedHandler = typeof (HomeController).GetMethod("Index", Type.EmptyTypes);
var data = RouteTable.Routes.GetRouteData(context.Object);
Assert.NotNull(data);
var handler = (MethodInfo) data.DataTokens["actionMethod"];
Assert.Equal(expectedHandler, handler);
I've had prety good experience with MVCContrib's Testhelper
Take a look at this test of testhelper.
Saves a lot of hassles around stubbing HttpContext etc.
Also if you are on MVC4, have a look at this Nuget package which is a fork for MVC4.
I'm creating a really simple ViewResult subclass called JavaScriptViewResult that, when executing, calls the base implementation and then sets the Content-Type of the response to text/javascript. In trying to unit test this class, I'm running across a slew of difficulties fulfilling all of the dependencies of the ASP.NET MVC stack.
Here is what my unit test, which uses Rhino, looks like so far:
[TestMethod]
public void TestExecuteAction()
{
var request = MockRepository.GenerateMock<HttpRequestBase>();
request.Expect(m => m.Url).Return(new Uri("/Test/JavaScript", UriKind.Relative));
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
httpContext.Expect(m => m.Request).Return(request);
var controller = MockRepository.GenerateMock<ControllerBase>();
var virtualPathProvider = MockRepository.GenerateMock<VirtualPathProvider>();
var routeCollection = new RouteCollection(virtualPathProvider);
routeCollection.MapRoute("FakeRoute", "Test/JavaScript", new { controller = "Test", action = "JavaScript" });
var routeData = routeCollection.GetRouteData(httpContext);
var context = new ControllerContext(httpContext, routeData, controller);
var viewResult = new JavaScriptViewResult();
viewResult.ExecuteResult(context);
Assert.AreEqual("text/javascript", context.HttpContext.Response.ContentType);
}
The latest exception when running the test is a NullReferenceException deep within the bowels of System.Web.Routing.Route.GetRouteData(HttpContextBase httpContext).
How do I set up all of the dependencies for executing a ViewResult? Are there any techniques for making this simpler? Alternately, is there a different way I can utilize the MVC view engine to generate JavaScript that will set the proper Content-Type for the response?
I figured out how to meet the minimum requirements of ViewResult. One problem I was encountering was mocking the process of finding the view. This was avoidable by ensuring that the View property of my object was populated. Here is my working test:
[TestMethod]
public void TestExecuteAction()
{
var response = MockRepository.GenerateStub<HttpResponseBase>();
response.Output = new StringWriter();
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
httpContext.Expect(m => m.Response).Return(response);
var routeData = new RouteData();
routeData.Values.Add("action", "FakeAction");
var context = new ControllerContext(httpContext, routeData, MockRepository.GenerateMock<ControllerBase>());
var viewResult = new JavaScriptViewResult();
viewResult.View = MockRepository.GenerateMock<IView>();
viewResult.ExecuteResult(context);
Assert.AreEqual("text/javascript", context.HttpContext.Response.ContentType);
}