I'm in the process of building an ASP.NET Core WebAPI and I'm attempting to write unit tests for the controllers. Most examples I've found are from the older WebAPI/WebAPI2 platforms and don't seem to correlate with the new Core controllers.
My controller methods are returning IActionResults. However, the IActionResult object only has a ExecuteResultAsync() method which requires a controller context. I'm instantiating the controller manually, so the controller context in this instance is null, which causes an exception when calling ExecuteResultAsync. Essentially this is leading me down a very hacky path to get these unit tests to successfully complete and is very messy. I'm left wondering that there must be a more simple/correct way of testing API controllers.
Also, my controllers are NOT using async/await if that makes a difference.
Simple example of what I'm trying to achieve:
Controller method:
[HttpGet(Name = "GetOrdersRoute")]
public IActionResult GetOrders([FromQuery]int page = 0)
{
try
{
var query = _repository.GetAll().ToList();
int totalCount = query.Count;
int totalPages = (int)Math.Ceiling((double)totalCount / pageSize) - 1;
var orders = query.Skip(pageSize * page).Take(pageSize);
return Ok(new
{
TotalCount = totalCount,
TotalPages = totalPages,
Orders = orders
});
}
catch (Exception ex)
{
return BadRequest(ex);
}
}
Unit test:
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk()
{
// arrange
var controller = new OrdersController(new MockRepository());
// act
IActionResult result = controller.GetOrders();
// assert
Assert.Equal(HttpStatusCode.OK, ????);
}
Assuming something like the
public IActionResult GetOrders() {
var orders = repository.All();
return Ok(orders);
}
the controller in this case is returning an OkObjectResult class.
Cast the result to the type of what you are returning in the method and perform your assert on that
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk() {
// arrange
var controller = new OrdersController(new MockRepository());
// act
var result = controller.GetOrders();
var okResult = result as OkObjectResult;
// assert
Assert.IsNotNull(okResult);
Assert.AreEqual(200, okResult.StatusCode);
}
You can also do cool things like:
var result = await controller.GetOrders();//
var okResult = result as ObjectResult;
// assert
Assert.NotNull(okResult);
Assert.True(okResult is OkObjectResult);
Assert.IsType<TheTypeYouAreExpecting>(okResult.Value);
Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode);
Thanks
Other answers adviced to cast to ObjectResult, but its work only if you return OkObjectResult \ NotFoundObjectResult \ etc. But server could return NotFound\ OkResult which derived from StatusCodeResult.
For example:
public class SampleController : ControllerBase
{
public async Task<IActionResult> FooAsync(int? id)
{
if (id == 0)
{
// returned "NotFoundResult" base type "StatusCodeResult"
return NotFound();
}
if (id == 1)
{
// returned "StatusCodeResult" base type "StatusCodeResult"
return StatusCode(StatusCodes.Status415UnsupportedMediaType);
}
// returned "OkObjectResult" base type "ObjectResult"
return new OkObjectResult("some message");
}
}
I looked at the implementation of all these methods and found that they are all inherited from the IStatusCodeActionResult interface. It seems like this is the most base type that contains StatusCode:
private SampleController _sampleController = new SampleController();
[Theory]
[InlineData(0, StatusCodes.Status404NotFound)]
[InlineData(1, StatusCodes.Status415UnsupportedMediaType)]
[InlineData(2, StatusCodes.Status200OK)]
public async Task Foo_ResponseTest(int id, int expectedCode)
{
var actionResult = await _sampleController.FooAsync(id);
var statusCodeResult = (IStatusCodeActionResult)actionResult;
Assert.Equal(expectedCode, statusCodeResult.StatusCode);
}
A good way to do that is like this:
[Fact]
public void GetOrders_WithOrdersInRepo_ReturnsOk() {
// arrange
var controller = new OrdersController(new MockRepository());
// act
var result = controller.GetOrders();
// assert
var okResult = Assert.IsType<OkObjectResult>(result);
Assert.IsNotNull(okResult);
Assert.AreEqual(200, okResult.StatusCode);
}
You also can use ActionResult class as a controller result (assuming you have type Orders).
In that case you can use something like this:
[ProducesResponseType(typeof(Orders), StatusCodes.Status200OK)]
public ActionResult<Orders> GetOrders()
{
return service.GetOrders();
}
and now in unit tests you have:
Assert.IsInstanceOf<Orders>(result.Value);
Besides, this is the recommendation of Microsoft - https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-2.2#actionresultt-type
Unfortunately, I don't know why using Ok method
return Ok(service.GetOrders());
doesn't map it properly.
public async Task CallRxData_ReturnsHttpNotFound_ForInvalidJobNum_ReturnsStoredRxOrder()
{
var scanInController = new ScanInController(_logger, _scanInService);
var okResult = await scanInController.CallRxData(rxOrderRequest);
var notFoundResult = await scanInController.CallRxData(invalidRxOrderRequest);
var okResultWithScanInCheckFalse = await scanInController.CallRxData(rxOrderRequest);
var okResultWithEmptyAelAntiFakeDatas = await scanInController.CallRxData(rxOrderRequest);
// Assert
Assert.That(okResult, Is.TypeOf<OkObjectResult>());
Assert.That(notFoundResult, Is.TypeOf<NotFoundObjectResult>());
Assert.IsFalse(((okResultWithScanInCheckFalse as ObjectResult).Value as RxOrder).IsSecurity);`enter code here`
}
Related
In my test project i am using xUnit with Moq,
in my controller test class i also need an object of type Mock<UserManager<ApplicationUser>> to get the current user informations.
That's why i am using Moq to Setup the async method FindByIdAsync the setup is configured like this :
public async Task EntrsAdd_ShouldReturnSuccessJsonResult_WhenModelIdIsNull()
{
//Arrange
var id = _fixture.Create<string>();
ApplicationUser applicationUserMock = _fixture.Create<ApplicationUser>();
var viewModelMock = _fixture.Create<AddOrUpdateEntrViewModel>();
viewModelMock.Id = null;
_userManager.Setup(u => u.FindByIdAsync(id))
.Returns(Task.FromResult<ApplicationUser>(applicationUserMock));
//Act
var result = await _sutEntrsController.Add(viewModelMock).ConfigureAwait(false);
int test = 0;
//Assert
}
the method i am testing in the controller is:
public async Task<IActionResult> Add(AddOrUpdateEntrViewModel model)
{
var user = await _userManager.FindByIdAsync(model.UsrId);
var entr = _mapper.Map<Entr>(model);
entr.Usr = $"{user.FirstName} {user.LastName}";
if (model.Id is null)
{
await _entrService.AddAsync(entr);
}
else
{
await _entrService.UpdateAsync(entr);
}
return new JsonResult("success.");
}
The problem is that the setup uses the id variable and the corresponding property in the viewmodel is not set.
Either set the correct id for UsrId on the ViewModelMock or relax the setup by using It.IsAny<string>():
var viewModelMock = _fixture.Create<AddOrUpdateEntrViewModel>();
viewModelMock.Id = null;
viewModelMock.UsrId = id;
or
_userManager.Setup(u => u.FindByIdAsync(It.IsAny<string>()))
.Returns(Task.FromResult<ApplicationUser>(applicationUserMock));
I'm trying to write a Unit test case for the delete method of the below controller
public class AssetProxyController : Controller
{
private IRiskAssetProxyService _assetProxyService;
public AssetProxyController( IRiskAssetProxyService assetProxyService)
{
_assetProxyService = assetProxyService;
}
[HttpDelete("{assetId}")]
public ActionResult Delete(string assetId)
{
if (_assetProxyService.DeleteAssetProxyMapping(assetId))
{
return Ok("AssetProxy Deleted");
}
else
{
return BadRequest("Unable to delete AssetProxy");
}
}
}
Test
[TestMethod]
public void Delete_ShouldReturnDeleteAssetProxy()
{
//Mock
var mockContext = new Mock<MainDBContext>();
var faker = AutoFaker.Create();
var assetProxyMappings = faker.Generate<List<AssetProxyMapping>>();
var mockAssetProxyDbSet = GetQueryableMockDbSet<AssetProxyMapping>(assetProxyMappings);
mockContext.Setup(c => c.AssetProxyMapping).Returns(mockAssetProxyDbSet);
//Test
var mocklogger = new Mock<ILogger<RiskDataControllerDbAccess>>();
var positiondbaccess = new Mock<RiskAssetProxyDbAccess>(mockContext,mocklogger);
var mockserviceLogger = new Mock<ILogger<RiskAssetProxyService>>();
var positionService = new Mock<RiskAssetProxyService>(positiondbaccess, mockserviceLogger);
var positionController = new AssetProxyController(positionService.Object);
Assert.AreEqual(true, true);
}
But i keep getting the exception
Test method Risk.DataService.UnitTests.API.AssetProxyControllerTests.Delete_ShouldReturnDeleteAssetProxy threw exception:
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: Can not instantiate proxy of class: Risk.DataServices.RiskAssetProxyService.
Could not find a constructor that would match given arguments:
Moq.Mock`1[Risk.DataServices.RiskAssetProxyDbAccess]
Moq.Mock`1[Microsoft.Extensions.Logging.ILogger`1[Risk.DataServices.RiskAssetProxyService]]
The currently shown test is completely over-engineered based on the shown subject under test.
Given the shown Delete controller action, the test case should be simplified
[TestMethod]
public void AssetProxy_Delete_Should_Return_Ok() {
//Arrange
string assetId = "assetId";
var serviceMock = new Mock<IRiskAssetProxyService>();
serviceMock.Setup(_ => _.DeleteAssetProxyMapping(assetId))
.Returns(true);
AssetProxyController controller = new AssetProxyController(serviceMock.Object);
//Act
ActionResult result = controller.Delete(assetId);
//Assert - using fluent assertions
result.Should().NotBeNull();
string expected = "AssetProxy Deleted";
OkObjectResult okResult = result as OkObjectResult;
okResult.Should().NotBeNull();
string actual = okResult.Value as string;
actual.Should().Be(expected); //same as Assert.AreEqual(expected, actual);
}
Another test case can be done to assert bad requests and would be similar to the case shown above except that the mocked service will be arranged to return false in order to cause the expected behavior and the assertion updated accordingly
I have async method in controller that calling the method from client. I want to iterate through this method in foreach loop but getting error:
*Cannot convert from 'Microsoft.AspNetCore.Mvc.ActionResult' to 'System.Collections.Generic.IEnumerable<string>'*
UnitTest.cs
[TestFixture]
public class ClientUnitTests
{
private MessagesController Controller = new MessagesController(null, null, null);
[Test]
public async Task Check_API_Links()
{
// Arrange
List<IEnumerable<string>> reporteeList = new List<IEnumerable<string>>();
var links = await Controller.GetLinks();
reporteeList.Add(links); //Cannot convert from 'Microsoft.AspNetCore.Mvc.ActionResult' to 'System.Collections.Generic.IEnumerable<string>'
foreach (var itemLink in reporteeList)
{
reporteeList.Add(itemLink);
}
// Assert
Assert.IsNotNull(reporteeList);
Assert.GreaterOrEqual(0, reporteeList.Count);
Assert.IsInstanceOf<IEnumerable<string>>(reporteeList);
}
}
Controller.cs
[HttpGet]
public async Task<ActionResult> GetLinks()
{
var result = await _client.GetLinks();
return Ok(result);
}
Client.cs
public async Task<ActionResult<IEnumerable<string>>> GetLinks()
{
// Some logic here,
}
How can I resolve this to do iterate trought GetLinks method in unit test ?
Thanks.
Assuming .NET Core 2.1
public async Task<ActionResult<IEnumerable<string>>> GetLinks()
or the less preferred
public async Task<IActionResult> GetLinks()
For your issue, you embed ActionResult<IEnumerable<string>> with Ok and List<IEnumerable<string>> with IEnumerable<string>.
For your current design, try code below:
List<IEnumerable<string>> reporteeList = new List<IEnumerable<string>>();
var result = await controller.GetLinks();
var links = ((ObjectResult)((ActionResult<IEnumerable<string>>)((ObjectResult)result).Value).Result).Value as IEnumerable<string>;
reporteeList.Add(links);
But, in my option, you should not use embed object. Please check whether below design meets your requirement.
Client.cs return IEnumerable<string> instead of ActionResult<IEnumerable<string>>
public class Client
{
public async Task<ActionResult<IEnumerable<string>>> GetLinks()
{
IEnumerable<string> links = new List<string>() {
"L1","L2"
};
return new ObjectResult(links);
// Some logic here,
}
public async Task<IEnumerable<string>> GetLinks1()
{
IEnumerable<string> links = new List<string>() {
"L1","L2"
};
return links;
// Some logic here,
}
}
Controller
public async Task<ActionResult> GetLinks1()
{
var result = await new Client().GetLinks1();
return Ok(result);
}
Test
List<string> reporteeList1 = new List<string>();
var result1 = await controller.GetLinks1();
var links1 = result1 as ObjectResult;
reporteeList1.AddRange((IEnumerable<string>)links1.Value);
Test
[Fact]
public async Task Get_WhenCalled_ReturnsAllItemsCount()
{
// Act
var result = await _controller.GetLinks1();
var okResult = result.Result as OkObjectResult;
// Assert
var items = Assert.IsType<List<string>>(okResult.Value);
Assert.Equal(2, items.Count);
}
I'm using VisualStudio 2015, .NET 4.6, Moq 4.5.2, Nunit 3.4.1 to test a WebApi2 Controller. However, I am getting a null response object when mocking the intended controller method:
var response = actionResult as NegotiatedContentResult;
I am guessing I must be setting up my mock of the UserService incorrectly?
My suspicion is that this part is the culprit:
userServiceMock.Setup(service => service.InsertOrUpdateUser(
It.IsAny())).Returns(1);
As I am getting the following in the output window:
'((System.Web.Http.Results.OkNegotiatedContentResult)actionResult).Request'
threw an exception of type 'System.InvalidOperationException'
Is the problem that I am telling Moq to expect a return value of 1, but the Put method returns OkNegotiatedContentResult?
My questions are (possibly the same question):
1) Am I setting up my Moq correctly and
2) how do I resolve the problem so my response object is populated?
Thanks much.
Here is the Test method:
[Test]
public void Put_ShouldUpdate_User()
{
// Arrange
var userServiceMock = new Mock<IUserService>();
userServiceMock.Setup(service => service.InsertOrUpdateUser(
It.IsAny<User>())).Returns(1);
var controller = new UsersController(userServiceMock.Object);
// Act
IHttpActionResult actionResult = controller.Put(
new User()
{
Id = 1,
Name = "Joe"
});
var response = actionResult as NegotiatedContentResult<User>;
// Assert:
Assert.IsNotNull(response);
var newUser = response.Content;
Assert.AreEqual(1, newUser.Id);
Assert.AreEqual("Joe", newUser.Name);
}
Here is the UserController method:
// PUT api/users/1
public IHttpActionResult Put(User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Ok(_userService.InsertOrUpdateUser(user));
}
Finally, the method for the UserService:
public int InsertOrUpdateUser(User user)
{
return _userRepository.InsertOrUpdateUser(user);
}
According to your code IUserService has
public interface IUserService {
int InsertOrUpdateUser(User user);
}
which returns an int.
If you do the following in your controller
return Ok(_userService.InsertOrUpdateUser(user));
then based on the interface and your setup mock, that will return a response type of OkNegotiatedContentResult<int>. But in your test you do this
var response = actionResult as NegotiatedContentResult<User>;
where you cast your returned result as NegotiatedContentResult<User> this will cause Assert.IsNotNull(response); to fail as the cast will result in response being null.
Given the asserts of your test then you would have to update your controller's Put method to return the User user after the mocked action like so...
public IHttpActionResult Put(User user) {
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
var count = _userService.InsertOrUpdateUser(user);
if(count == 1)
return Ok(user);
else
return BadRequest(); // 500 (Internal Server Error) you choose.
}
and also update the test as follows
//...other code removed for brevity
var response = actionResult as OkNegotiatedContentResult<User>;
// Assert:
Assert.IsNotNull(response);
var newUser = response.Content;
Assert.AreEqual(1, newUser.Id);
Assert.AreEqual("Joe", newUser.Name);
I have a unit test project using Xunit and the method we are testing returns IActionResult.
I saw some people suggest using "NegotiatedContentResult" to get the content of the IActionResult but that doesn't work in Xunit.
So I wonder how to get the content value of an IActionResult in Xunit?
Test code example is provided below:
public void GetTest()
{
var getTest = new ResourcesController(mockDb);
var result = getTest.Get("1");
//Here I want to convert the result to my model called Resource and
//compare the attribute Description like below.
Resource r = ?? //to get the content value of the IActionResult
Assert.Equal("test", r.Description);
}
Does anyone know how to do this in XUnit?
Depends on what you expect returned. From previous example you used an action like this.
[HttpGet("{id}")]
public IActionResult Get(string id) {
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.Resource result = ConvertResourceFromCoreToApi(r);
if (result == null) {
return NotFound();
} else {
return Ok(result);
}
}
That method will either return a OkObjectResult or a NotFoundResult. If the expectation of the method under test is for it to return Ok() then you need to cast the result in the test to what you expect and then do your assertions on that
public void GetTest_Given_Id_Should_Return_OkObjectResult_With_Resource() {
//Arrange
var expected = "test";
var controller = new ResourcesController(mockDb);
//Act
var actionResult = controller.Get("1");
//Assert
var okObjectResult = actionResult as OkObjectResult;
Assert.NotNull(okObjectResult);
var model = okObjectResult.Value as Models.Resource;
Assert.NotNull(model);
var actual = model.Description;
Assert.Equal(expected, actual);
}