I'm new to testing with Moq, so I'm confused a bit regarding ReturnsAsync.
Here is my test method, where I'm expecting ReturnsAsync should receive type that will be returned by method defined in Setup. Am I right?
But seemed ReturnsAsync should have the other signature - it's looking for Func delegate.
[TestClass]
public class TourObjectControllerTest
{
[TestMethod]
public async Task GetTourObjects()
{
var mockService = new Mock<ITourObjectService>(MockBehavior.Default);
mockService.Setup(x => x.GetAll()).ReturnsAsync(new Task<IEnumerable<TourObjectDTO>>);
var controller = new TourObjectController(mockService.Object);
var result = await controller.Get();
Assert.IsNotNull(result);
Assert.AreSame(typeof(TourObjectViewModel), result);
}
}
Method under test:
public async Task<IEnumerable<TourObjectViewModel>> Get()
{
IEnumerable<TourObjectViewModel> viewmodel = null;
try
{
var tourobjects = await _tos.GetAll();
viewmodel = Mapper.Map<IEnumerable<TourObjectViewModel>>(tourobjects);
}
catch (Exception ex)
{
Log.ErrorFormat("Method:{0} <br/> Error: {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
}
return viewmodel;
}
Pass an actual result.
Assuming ITourObjectService.GetAll() returns Task<IEnumerable<TourObjectDTO>>
eg
[TestClass]
public class TourObjectControllerTest
{
[TestMethod]
public async Task GetTourObjects()
{
var fakeData = new List<TourObjectDTO>() {
new TourObjectDTO { ... }
};
var mockService = new Mock<ITourObjectService>(MockBehavior.Default);
mockService.Setup(x => x.GetAll()).ReturnsAsync(fakeData);
var controller = new TourObjectController(mockService.Object);
var result = await controller.Get();
Assert.IsNotNull(result);
Assert.IsTry(typeof(IEnumerable<TourObjectViewModel>).IsAssignableFrom(result.GetType());
}
}
ReturnsAsync() should take a return value as the paramter instead of Task<TReurnValue>. For example, next 2 lines of code are equal (by behavior):
mockService.Setup(x => x.GetAll()).ReturnsAsync(new List<TourObjectDTO>());
mockService.Setup(x => x.GetAll()).Returns(Task.FromResult(new List<TourObjectDTO>()));
You need to replace new List<TourObjectDTO>() with your test data which you want to retrieve in the test from the mock. For example, you can create just few test values:
var testData = new [] { new TourObjectDTO(1, "test1"), new TourObjectDTO(2, "test2") };
mockService.Setup(x => x.GetAll()).ReturnsAsync(testData);
or create fake data generator if needed.
Note that ReturnsAsync() is only available for methods that return a Task<T>. For methods that return only a Task, .Returns(Task.FromResult(default(object))) can be used.
Related
I have a method in a repository that receives an Expression as a parameter and the result of the method are supposed to filter by that expression. The thing is that when I try to test this method, it doesn't give me the result filtered:
Service method I'm testing
public async Task<Response<IEnumerable<AirlineDto>>> GetAllAsync()
{
try
{
var airlines = await _airlineRepository.GetAsync(x => x.Status); // call to the real repo
... // some logic
}
... // exception handling
}
Method Setup, MockAirlineRepository is a mock of AirlineRepository
public MockAirlineRepository MockGetAll()
{
Setup(x => x.GetAsync(It.IsAny<Expression<Func<Airline, bool>>>()))
.ReturnsAsync(GetTestAirlines);
return this;
}
private static IEnumerable<Airline> GetTestAirlines()
{
var airlines = new List<Airline>
{
new()
{
Id = Guid.NewGuid(),
Name = "Test One",
Status = true
},
new()
{
Id = Guid.NewGuid(),
Name = "Test Two",
Status = true
},
new()
{
Id = Guid.NewGuid(),
Name = "Test Three",
Status = false // notice that this Airline Status is false, so,
// the count of the retrieved values should be 2
}
};
return airlines;
}
Test
[Fact]
public void AirlineService_GetAll_ReturnsAirlines()
{
//Arrange
var mockAirlineRepo = new MockAirlineRepository().MockGetAll();
var airlineService = new AirlineService(mockAirlineRepo.Object, _airlineMapper);
//Act
var result = airlineService.GetAllAsync().Result.Data;
//Assert
var airlineDtoList = result.ToList();
Assert.NotEmpty(airlineDtoList);
Assert.Equal(2, airlineDtoList.Count); // assert fails, because airlineDtoList.Count is 3
mockAirlineRepo.VerifyGetAllAirlines(Times.Once());
}
Let's start with the basics:
Dummy: simple code that returns bogus data
Fake: a working alternative which can take shortcuts
Stub: custom logic with predefined data
Mock: custom logic with expectations (interactive stub)
Shim: custom logic at run-time (replace static with a delegate)
Spy: interceptors to record calls
So, your mock is an interactive stub which means it can return different outputs based on the received input. In your Setup call you have made the following statement: whatever I receive as a filter expression I should always return with the entire list
If you want to apply the filter then change your setup
.Setup(x => x.GetAsync(It.IsAny<Expression<Func<Airline, bool>>>()))
.ReturnsAsync(filter => GetTestAirlines.Where(filter));
Now if you assert on airlineDtoList.Count then it will return 2. But in this case you are not testing the GetAllAsync function rather than your mock.
So, I would rather spend more time testing the // some logic
You can also take advantage of async-await in your test
[Fact]
public Task AirlineService_GetAll_ReturnsAirlines()
{
//Arrange
var mockAirlineRepo = new MockAirlineRepository().MockGetAll();
var airlineService = new AirlineService(mockAirlineRepo.Object, _airlineMapper);
//Act
var response = await airlineService.GetAllAsync();
var result = response.Data;
//Assert
...
}
My controller for the delete method :
[HttpDelete("{toDoListId}")]
public async Task<ActionResult> DeleteToDoList(int toDoListId)
{
var toDoListEntity = await _toDoListRepository.GetSpecificTodoAsync(toDoListId);
if (toDoListEntity == null)
{
return NotFound();
}
_toDoListRepository.DeleteToDoList(toDoListEntity);
await _toDoListRepository.SaveChangesAsync();
return NoContent();
}
My repository :
public async Task<ToDoList?> GetSpecificTodoAsync(int taskId)
{
return await _context.ToDoLists.Where(c => c.Id == taskId).FirstOrDefaultAsync();
}
public void DeleteToDoList(ToDoList toDoListDto)
{
_context.ToDoLists.Remove(toDoListDto);
}
My testcase for checking if the item got deleted and if it returns no content after being deleted. But both of my test cases are failing. Any help on how to write test cases for the delete part, I would be really grateful. I am also trying to test other methods but I am unfortunately stuck here. Please kindly help me
public class UnitTest1
{
private readonly Mock<IToDoListRepository> repositoryStub = new ();
private readonly Mock<IMapper> mapper = new Mock<IMapper>();
private readonly Random Rand = new();
private ToDoList GenerateRandomItem()
{
return new()
{
Id = Rand.Next(),
Description= Guid.NewGuid().ToString(),
Title = Guid.NewGuid().ToString(),
StartDate = DateTime.Now,
EndDate = DateTime.Now,
Done = false
};
}
[Fact]
public void Delete_removesEntry()
{
//arrange
var existingItem = GenerateRandomItem();
var controller = new ToDoController(repositoryStub.Object, mapper.Object);
var itemID = existingItem.Id;
//act
controller.DeleteToDoList(itemID);
//assert
Assert.Null(repositoryStub.Object.GetSpecificTodoAsync(itemID));
}
[Fact]
public async Task DeleteItemAsync_WithExistingItem_ReturnNoContent()
{
//Arrange
var existingItem = GenerateRandomItem();
repositoryStub.Setup(repo => repo.GetSpecificTodoAsync(existingItem.Id)).ReturnsAsync((existingItem));
var itemID = existingItem.Id;
var controller = new ToDoController(repositoryStub.Object, mapper.Object);
//Act
var result = await controller.DeleteToDoList(itemID);
//assert
result.Should().BeOfType<NoContentResult>();
}
Both test cases are failing because the mock has not been setup to behave as expected for each test case.
There is also a potential race condition in the shown tests since they are sharing the same mock instance. This will cause issues when setting up the mock as one test case could potentially override the setup of another case.
Update the tests so that they are isolated from each other.
In the first test, the expected behavior can be verified by checking the mock to see if the expected member was invoked.
[Fact]
public async Task DeleteToDoList_Should_RemoveEntry() {
//arrange
ToDoList existingItem = GenerateRandomItem();
var itemID = existingItem.Id;
Mock<IToDoListRepository> repositoryStub = new ();
//Setup expected behavior of mock
repositoryStub
.Setup(_ => _.GetSpecificTodoAsync(itemID))
.ReturnsAsync(existingItem);
var controller = new ToDoController(repositoryStub.Object, mapper.Object);
//act
await controller.DeleteToDoList(itemID);
//assert
repositoryStub.Verify(_ => _.DeleteToDoList(existingItem));
}
In the other test, the mock needs be setup to make sure the subject executes to completion.
[Fact]
public async Task DeleteToDoList_WithExistingItem_Should_ReturnNoContent() {
//Arrange
ToDoList existingItem = GenerateRandomItem();
var itemID = existingItem.Id;
Mock<IToDoListRepository> repositoryStub = new ();
//Setup expected behavior of mock
repositoryStub
.Setup(_ => _.GetSpecificTodoAsync(itemID))
.ReturnsAsync(existingItem);
repositoryStub.Setup(_ => _.SaveChangesAsync()).ReturnsAsynt(true);
var controller = new ToDoController(repositoryStub.Object, mapper.Object);
//Act
ActionResult result = await controller.DeleteToDoList(itemID);
//assert
result.Should().BeOfType<NoContentResult>();
}
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 want to test GetMoviesAsync of my Controller. I don't know where I am doing wrong in my Moq setup. I am getting 0 item from GetMoviesAsync.
What am I doing wrong?
// Api-Controller:
public interface ICommand
{
Task<IEnumerable<Movie>> GetMoviesAsync();
}
public class SampleController : ControllerBase
{
private readonly ICommand movieCommand;
public SampleController(ICommand command)
{
movieCommand = command;
}
[HttpGet]
public async Task<IActionResult> GetMoviesAsync()
{
var movies = await movieCommand.GetMoviesAsync();
return Ok(movies);
}
}
// Unit-Test:
public class SampleControllerTest
{
private IEnumerable<Movie> MovieList()
{
IList<Movie> movies = new List<Movie>()
{
new Movie()
{
ID =1,
Title = "Test",
ReleaseDate = DateTime.Now,
RunningTimeInMinutes = 100
}
};
return movies;
}
private SampleController GetSampleController()
{
var command = new Mock<ICommand>();
return new SampleController(command.Object);
}
[Fact]
public async Task GetMovies_Test()
{
// Arrange
var controller = GetSampleController();
var commadMock = new Mock<ICommand>();
// How to setup moq here?
commadMock.Setup(s => s.GetMoviesAsync()).Returns(Task.FromResult<IEnumerable<Movie>>(MovieList())).Verifiable();
// Act
var response = await controller.GetMoviesAsync() as OkObjectResult;
// Problem is here,
var li=response.Value as IEnumerable<Movie>;
}
}
What am I doing wrong?
Two completely different mocks are being used.
One is used to create the controller
private SampleController GetSampleController()
{
var command = new Mock<ICommand>();
return new SampleController(command.Object);
}
and another is being created and setup in the test.
var controller = GetSampleController();
var commadMock = new Mock<ICommand>();
// How to setup moq here?
commadMock.Setup(s => s.GetMoviesAsync()).Returns(Task.FromResult<IEnumerable<Movie>>(MovieList())).Verifiable();
To solve this, use the same mock to get the desired behavior
[Fact]
public async Task GetMovies_Test() {
// Arrange
var commadMock = new Mock<ICommand>();
var controller = new SampleController(commadMock.Object); //<---
commadMock
.Setup(_ => _.GetMoviesAsync())
.ReturnsAsync(MovieList())
.Verifiable();
// Act
var response = await controller.GetMoviesAsync() as OkObjectResult;
//Assert
var list = response.Value as IEnumerable<Movie>;
//...
}
Note the use of ReturnsAsync to setup the returned Task
It seems that you are not using the correct mock on the Controller. The one that you are using does not have any setup on top of the method GetMoviesAsync
For me helped almost the solution offered by Nkosi but with little difference
[Fact]
public async Task GetMovies_Test() {
// Arrange
var commadMock = new Mock<ICommand>();
var controller = new SampleController(commadMock.Object); //<---
commadMock
.Setup(_ => _.GetMoviesAsync())
.ReturnsAsync(MovieList());
// Act
var response = await controller.GetMoviesAsync();
//Assert
var returnValue = Assert.IsType<ViewResult>(response);
var model = returnValue.Model as IEnumerable<Movie>;
//...
}
I have a test class that checks the put on a controller:
[TestMethod, TestCategory(Unit)]
public void DocumentController_Put_Returns_NoContent()
{
// arrange
var scope = new DefaultScope();
var expectedId = scope.TestId;
var model = Builder<Api.Document>
.CreateNew()
.Build();
var entityToUpdate = scope.TestDocument;
scope.DocumentProviderMock
.Setup(x => x.GetAsync(expectedId))
.ReturnsAsync(entityToUpdate);
// act & assert
scope.InstanceUnderTest.Put(scope.TestId, model)
.AssertNoContent();
}
AssertNoContent is a method defined here:
public static StatusCodeResult AssertNoContent(this Task<IHttpActionResult> task)
{
var httpActionResult = task.GetAwaiter().GetResult();
var actionResult = httpActionResult as StatusCodeResult;
Assert.IsNotNull(actionResult, "IHttpActionResult is not the correct type. Expected type {0}, but {1} was found.", typeof(StatusCodeResult).FullName, httpActionResult.GetType().FullName);
return actionResult;
}
My issue is that the task is returning being passed in as a parameter is returning NegotiatedContentResult in the variable httpActionResult instead of the one I want (StatusCodeResult).
I am not allowed to change the AssertNoContent method. Why am I getting NegotiatedContentResult and how do I get it to return a StatusCodeResult?
Edit (object not showing tooltip):