I am trying to develop an Xunit test to establish if my Controller under test is returning the correct number of objects.
The Controller's getAreas function is as follows:
[HttpGet()]
public IActionResult GetAreas()
{
_logger.LogTrace("AreasController.GetAreas called.");
try
{
// Create an IEnumerable of Area objects by calling the repository.
var areasFromRepo = _areaRepository.GetAreas();
var areas = _mapper.Map<IEnumerable<AreaDto>>(areasFromRepo);
// Return a code 200 'OK' along with an IEnumerable of AreaDto objects mapped from the Area entities.
return Ok(areas);
}
catch (Exception ex)
{
_logger.LogError($"Failed to get all Areas: {ex}");
return StatusCode(500, "An unexpected error occurred. Please try again later.");
}
}
My test class uses Moq to mock the Logger, Repository and AutoMapper. I have created a variable to hold a list of objects to be returned by my mock repository:
private List<Area> testAreas = new List<Area>()
{
new Area
{
Id = new Guid("87d8f755-ef60-4cfa-9a4a-c94cff9f8a22"),
Description = "Buffer Store",
SortIndex = 1
},
new Area
{
Id = new Guid("19952c5a-b762-4937-a613-6151c8cd9332"),
Description = "Fuelling Machine",
SortIndex = 2
},
new Area
{
Id = new Guid("87c7e1d8-1ce7-4d8b-965d-5c44338461dd"),
Description = "Ponds",
SortIndex = 3
}
};
I created my test as follows:
[Fact]
public void ReturnAreasForGetAreas()
{
//Arrange
var _mockAreaRepository = new Mock<IAreaRepository>();
_mockAreaRepository
.Setup(x => x.GetAreas())
.Returns(testAreas);
var _mockMapper = new Mock<IMapper>();
var _mockLogger = new Mock<ILogger<AreasController>>();
var _sut = new AreasController(_mockAreaRepository.Object, _mockLogger.Object, _mockMapper.Object);
// Act
var result = _sut.GetAreas();
// Assert
Assert.NotNull(result);
var objectResult = Assert.IsType<OkObjectResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<AreaDto>>(objectResult.Value);
var modelCount = model.Count();
Assert.Equal(3, modelCount);
}
The test fails on the final Assert, it gets 0 when expecting 3.
The result is not null. The objectResult is an OkObjectResult. The model is an IEnumerable<AreaDto>, however it contains 0 items in the collection.
I can't see where I am going wrong here. Do I have to configure the mocked Automapper mapping?
Do I have to configure the mocked Automapper mapping
Yes
Setup the mapper mock to return your desired result when invoked. Right now it is not setup so will default to empty collection.
Create a collection to represent the DTOs
private List<AreaDto> testAreaDTOs = new List<AreaDto>()
{
new AreaDto
{
Id = new Guid("87d8f755-ef60-4cfa-9a4a-c94cff9f8a22"),
Description = "Buffer Store",
SortIndex = 1
},
new AreaDto
{
Id = new Guid("19952c5a-b762-4937-a613-6151c8cd9332"),
Description = "Fuelling Machine",
SortIndex = 2
},
new AreaDto
{
Id = new Guid("87c7e1d8-1ce7-4d8b-965d-5c44338461dd"),
Description = "Ponds",
SortIndex = 3
}
};
Any update the test to use that collection when the mocked mapped is invoked.
[Fact]
public void ReturnAreasForGetAreas()
{
//Arrange
var _mockAreaRepository = new Mock<IAreaRepository>();
_mockAreaRepository
.Setup(x => x.GetAreas())
.Returns(testAreas);
var _mockMapper = new Mock<IMapper>();
//Fake the mapper
_mockMapper
.Setup(_ => _.Map<IEnumerable<AreaDto>>(It.IsAny<IEnumerable<Area>>()))
.Returns(testAreaDTOs);
var _mockLogger = new Mock<ILogger<AreasController>>();
var _sut = new AreasController(_mockAreaRepository.Object, _mockLogger.Object, _mockMapper.Object);
// Act
var result = _sut.GetAreas();
// Assert
Assert.NotNull(result);
var objectResult = Assert.IsType<OkObjectResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<AreaDto>>(objectResult.Value);
var modelCount = model.Count();
Assert.Equal(3, modelCount);
}
Related
I'm working on CRUD unit test cases with having configuration .Net 5, Automapper, xUnit etc.
The issue:
So right now I'm having issue specifically in post call and when I uses Dto.
I tried lots of ways to resole it and I was also able to resolve it if I don't use Dto in post call as input parameter and just use Entity it self. (But I don't want to expose entity so I want to make it working with Dto only)
Below is test which is not working and it's controller side implementation.
Failing Test case:
[Fact]
public void PostTest()
{
try
{
//Arrange
var surveyRequest = new SurveyRequest()
{
Id = 0,
Name = "Survey Request 1",
CreateDate = DateTime.Now,
CreatedBy = 1
};
var addedSurveyRequest = new SurveyRequest()
{
Id = 1,
Name = "Survey Request 1",
CreateDate = DateTime.Now,
CreatedBy = 1
};
//setup mock
Mock<IRepositoryWrapper> mockRepo = new Mock<IRepositoryWrapper>();
mockRepo.Setup(m => m.SurveyRequest.Add(addedSurveyRequest)).Returns(new Response<SurveyRequest>(true, addedSurveyRequest));
//auto mapper
var mockMapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
});
var mapper = mockMapper.CreateMapper();
SurveyRequestController controller = new SurveyRequestController(repositories: mockRepo.Object, mapper: mapper);
//Act
var model = mapper.Map<SurveyRequest, SurveyRequestDto>(source: addedSurveyRequest);
var result = controller.Post(model); // The issue with this post call here is that response remains null on repository level.
//Assert
var okResult = result as OkObjectResult;
Assert.NotNull(okResult);
//we will make sure that returned object is dto and not actual entity
var response = okResult.Value as SurveyRequestDtoWithId;
Assert.NotNull(response);
Assert.Equal(expected: response.Name, actual: model.Name);
}
catch (Exception ex)
{
//Assert
Assert.False(true, ex.Message);
}
}
Controller side post call:
[HttpPost("Insert")]
public IActionResult Post([FromBody] SurveyRequestDto model)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
//If I remove this mapping from here, test case will work. (see next working test case)
var entity = _mapper.Map<SurveyRequestDto, SurveyRequest>(source: model);
entity.CreateDate = System.DateTime.Now;
entity.CreatedBy = 1;
var response = _repositories.SurveyRequest.Add(entity: entity); //Response remains null here
_repositories.Save();
if (response.IsSuccess == true)
return new OkObjectResult(_mapper.Map<SurveyRequest, SurveyRequestDtoWithId>(source: response.Data));
else
return new ObjectResult(response.ErrorMessage) { StatusCode = 500 };
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
Working Test case:
[Fact]
public void PostTest2()
{
try
{
//Arrange
var surveyRequest = new SurveyRequest()
{
Id = 0,
Name = "Survey Request 1",
CreateDate = DateTime.Now,
CreatedBy = 1
};
var addedSurveyRequest = new SurveyRequest()
{
Id = 1,
Name = "Survey Request 1",
CreateDate = DateTime.Now,
CreatedBy = 1
};
//setup mock
Mock<IRepositoryWrapper> mockRepo = new Mock<IRepositoryWrapper>();
mockRepo.Setup(m => m.SurveyRequest.Add(surveyRequest)).Returns(value: new Response<SurveyRequest>(true, addedSurveyRequest));
//auto mapper
var mockMapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
});
var mapper = mockMapper.CreateMapper();
//setup controlller
SurveyRequestController controller = new SurveyRequestController(repositories: mockRepo.Object, mapper: mapper);
//Act
//var model = mapper.Map<SurveyRequest, SurveyRequestDto>(source: surveyRequest);
var result = controller.Post2(entity: surveyRequest);
//Assert
var okResult = result as OkObjectResult;
Assert.NotNull(okResult);
///we will make sure that returned object is dto and not actual entity
var response = okResult.Value as SurveyRequestDtoWithId;
Assert.NotNull(response);
Assert.Equal(expected: response.Id, actual: addedSurveyRequest.Id);
Assert.Equal(expected: response.Name, actual: addedSurveyRequest.Name);
}
catch (Exception ex)
{
//Assert
Assert.False(true, ex.Message);
}
}
Controller side Post call for working test case:
[HttpPost("Insert")]
public IActionResult Post2([FromBody] SurveyRequest entity)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
//var entity = _mapper.Map<SurveyRequestDto, SurveyRequest>(source: model);
//entity.CreateDate = System.DateTime.Now;
//entity.CreatedBy = 1;
var response = _repositories.SurveyRequest.Add(entity: entity); //This returns proper response with saved data and ID
_repositories.Save();
if (response.IsSuccess == true)
return new OkObjectResult(_mapper.Map<SurveyRequest, SurveyRequestDtoWithId>(source: response.Data));
else
return new ObjectResult(response.ErrorMessage) { StatusCode = 500 };
}
catch (Exception ex)
{
return new ObjectResult(ex.Message) { StatusCode = 500 };
}
}
I'm not sure whether my test case setup for mapper is wrong or any other issue. I also tried lots of ways but no luck so far. So posting here if someone can look and help, will be much appreciated.
If you are using IMapper then you can do something like this:
var mapperMock = new Mock<IMapper>();
mapperMock
.Setup(mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.IsAny< SurveyRequestDto>()))
.Returns(surveyRequest);
This solution does not utilize the AutoMapperProfile, but because you have only a single mapping that's why I think it not really a problem.
If you want to call Verify on the mapperMock then I would suggest to extract the Map selector delegate like this:
private static Expression<Func<IMapper, SurveyRequestDto>> MapServiceModelFromRequestModelIsAny =>
mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.IsAny< SurveyRequestDto>());
Usage
//Arrange
mapperMock
.Setup(MapServiceModelFromRequestModelIsAny)
.Returns(surveyRequest);
...
//Assert
mapperMock
.Verify(MapServiceModelFromRequestModelIsAny, Times.Once);
UPDATE #1
It might also make sense to be as explicit as possible when you make assertion. If you want to you can do deep equality check to make sure that controller's parameter is not amended before the Map call:
private static Expression<Func<IMapper, SurveyRequestDto>> MapServiceModelFromRequestModel(SurveyRequestDto input) =>
mapper => mapper.Map<SurveyRequestDto, SurveyRequest>(It.Is< SurveyRequestDto>(dto => dto.deepEquals(input)));
//Assert
mapperMock
.Verify(MapServiceModelFromRequestModel(model), Times.Once);
This assumes that deepEquals is available as an extension method.
UPDATE #2
As it turned out the mock repository's Setup code also had some problem. Namely it used the surveyRequest rather than a It.IsAny<SurveyRequest>().
Because surveyRequest was specified as the expected parameter that's why the setupped code path is never called but returned with null.
After changed it to It.IsAny then the whole test started to work :D
repoMock
.Setup(repo => repo.SurveyRequest.Add(It.IsAny<SurveyRequest>()))
.Returns(new Response<SurveyRequest>(true, addedSurveyRequest))
I try to have a unit test for my service, I mocked everything needed, How I can Mock repository methods that Service is calling so that has value and code is not breaking,
This is my unit test:
public async Task Updateuser_ReturnsResponse()
{
// Arrange
var request = new UpdateUserRequest()
{
Guid = new Guid("92296ac1-f8e1-489a-a312-6ea9d31d60f8"),
FirstName = "TestFirst",
LastName = "TestLast",
PhoneWork = "9495467845",
EmailWork = "test123#yahoo.com",
};
var respose = new UpdateUserResponse()
{
Success = true
};
var getGuidRequest = new GetGuidRequest()
{
Guid = request.Guid
};
var getGuidResponse = new GetGuidResponse()
{
Guid = request.Guid
};
var mockUserRepository = new Mock<IUserRepository>();
var mockAwsProxy = new Mock<IAwsProxy>();
mockUserRepository.Setup(s => s.UpdateUserAsync(request)).ReturnsAsync(respose);
mockUserRepository.Setup(i => i.GetGuidAsync(getGuidRequest)).ReturnsAsync(getGuidResponse);
var sut = new FromService.UserService(....);
// Act
var response = await sut.UpdateUserAsync(request);
// Assert
Assert.NotNull(response);
Assert.True(response.Success);
}
My problem is when calling - var response = await sut.UpdateUserAsync(request); It goese to service and this GuidResponse is empty so it break after as shows GuidResponse Null:
public async Task<UpdateUserResponse> UpdateUserAsync(UpdateUserRequest request)
{
if (request.EmailWork.HasValue() || request.Role.HasValue())
{
var GuidResponse = await userRepository.GetGuidAsync(new GetGuidRequest
{
Guid = request.Guid
});
// it breaks here because GuidResponse is Null.
if (GuidResponse.Guid != null && request.EmailWork.HasValue())
{
.......
It fails because the setup does not match what was actually given to the mock when the test was exercised.
Use It.Is<T>() to match the passed argument parameter
//...omitted for brevity
mockUserRepository
.Setup(_ => _.GetGuidAsync(It.Is<GetGuidRequest>(x => x.Guid == request.Guid)))
.ReturnsAsync(getGuidResponse);
//...omitted for brevity
assuming the mocked repository is what was injected into the SUT
Reference Moq Quickstart: Matching Arguments
I am using XUnit unit tests for testing my API Controllers in .NET Core 2.1. I am using MOQ for mocking my interfaced repositories. Debugging my unit test when I do the setup it still comes out null before it even hits the controller.
I have tried constructing the result using Returns or ReturnsAsync. Using a separate function to return a list or a IEnumerable<T>.
Controller
[HttpGet]
public async Task<IActionResult> GetPendingApprovals()
{
var user = _serviceRepository.GetUserName(User);
var userId = await _serviceRepository.GetUserID(user);
var result = await _requestRepository.GetPendingApprovalsByApprover(userId);
if (!result.Any()) return NoContent();
return Ok(result);
}
Test
private Mock<IServiceRepository> mockServiceRepo;
private Mock<IRequestRepository> mockRequestRepo;
private ApprovalController controller;
public ApproverControllerTests()
{
mockServiceRepo = new Mock<IServiceRepository>();
mockRequestRepo = new Mock<IRequestRepository>();
ILogger<ApprovalController> mockLoggerRepo = Mock.Of<ILogger<ApprovalController>>();
controller = new ApprovalController(mockRequestRepo.Object, mockServiceRepo.Object, mockLoggerRepo);
}
[Fact]
public async Task GetPendingApprovals_HasPending_ReturnsResultAsync()
{
// Arrange
var mockRequests = new List<Request>
{
new Request { Id = 1,
PONumber = "ABC0001",
RequestorId = 1,
SubmitDate = new DateTime(),
ApproverId = 2,
StatusId = 1,
Split = false,
VendorId1 = 1,
Remarks = "
},
new Request { Id = 2,
PONumber = "ABC0002",
RequestorId = 1,
SubmitDate = new DateTime(),
ApproverId = 2,
StatusId = 1,
Split = false,
VendorId1 = 1,
Remarks = "
}
};
mockServiceRepo.Setup (repo => repo.GetUserID ("pstaley").Returns (Task.FromResult (1);
//var pending = mockRequests.AsEnumerable();
mockRequestRepo.Setup (repo => repo.GetPendingApprovalsByApprover (1).Returns (Task.FromResult<IEnumerable<Request>> (mockRequests);
// Act
var result = await controller.GetPendingApprovals();
// Assert
var actionResult = Assert.IsType<OkObjectResult>(result);
//Assert.Equal(mockRequests, actionResult);
}
Debugging the test itself it the mockRequest is null so when it goes to the controller it meets the null check and returns no content response.
Most likely User is null as I see no setup for that and GetUserName is not setup to do anything so that will be null as well.
thus the expectation of the mocks do not match and this returns null by default.
Loosen the expectation on the GetUserID with It.IsAny<string>() to get the expected behavior.
[Fact]
public async Task GetPendingApprovals_HasPending_ReturnsResultAsync() {
// Arrange
var mockRequests = getUsers();
var userId = 1;
mockServiceRepo
.Setup(repo => repo.GetUserID(It.IsAny<string>()))
.ReturnsAsync(userId);
mockRequestRepo
.Setup(repo => repo.GetPendingApprovalsByApprover(userId))
.ReturnsAsync(mockRequests);
// Act
var result = await controller.GetPendingApprovals();
// Assert
var actionResult = Assert.IsType<OkObjectResult>(result);
//...
}
List<Request> getUsers() {
//... omitted for brevity
}
I have the following repository:
public class WorkspaceRepo : IWorkspacesRepo
{
private readonly ApplicationContext _applicationContext;
public WorkspaceRepo(ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
public IEnumerable<Workspace> Workspaces => _applicationContext.Workspaces;
public void Save(Workspace workspace)
{
_applicationContext.Workspaces.Add(workspace);
_applicationContext.SaveChanges();
}
}
And the following method is from my business logic class. I need to test this method:
public Workspace CreateWorkspace(Workspace workspace)
{
if (string.IsNullOrEmpty(workspace.UserUuid))
{
throw new RpcException(new Status(StatusCode.NotFound, "Empty user Uuid"));
}
var freSlots = 10 - _workspacesRepo.Workspaces.Count(x => x.UserUuid == workspace.UserUuid);
if (freSlots <= 0)
{
throw new RpcException(new Status(StatusCode.Aborted, "There are no free slots work workspaces"));
}
_workspacesRepo.Save(workspace);
return workspace;
}
The business logic is simple. I can save only 10 Workspace object. The next saving must give me RpcException. Now I would like to test it. Here is the test code:
[Test]
public void User_Cant_Exceed_Workspaces_Limit()
{
// organization
Mock<IWorkspacesRepo> mock = new Mock<IWorkspacesRepo>();
mock.Setup(m => m.Save(It.IsAny<Workspace>())).Callback( /* what to do here?*/ )
var target = new WorkspaceBusinessService(mock.Object, null);
for (var i = 0; i < 10; i++)
{
target.CreateWorkspace(new Workspace
{
Name = Guid.NewGuid().ToString(),
UserUuid = Guid.NewGuid().ToString(),
WorkspaceId = i + 1
});
}
var redundantWorkspace = new Workspace
{
Name = Guid.NewGuid().ToString(),
UserUuid = Guid.NewGuid().ToString(),
WorkspaceId = 11
};
// action
// asserts
var ex = Assert.Throws<RpcException>(() => target.CreateWorkspace(redundantWorkspace));
Assert.That(ex.Message, Is.EqualTo("Status(StatusCode.Aborted, \"There are no free slots work workspaces\")"));
}
But expected behavior is not going on. I watched this with debug and in the CreateWorkspace method I have always 10 freeSlots. How to test this situation?
Based on the Logic in the method under test, you are mocking the wrong member.
Mock Workspaces property so that it behaves as expected when
//..
var freSlots = 10 - _workspacesRepo.Workspaces.Count(x => x.UserUuid == workspace.UserUuid);
if (freSlots <= 0) {
//...
is invoked.
For example
// Arrange
//need common user id
var userUuid = Guid.NewGuid().ToString();
//create workspaces to satisfy Linq Count(Expression)
var workspaces = Enumerable.Range(0, 10).Select(i => new Workspace {
Name = Guid.NewGuid().ToString(),
UserUuid = userUuid, //<-- Note the common user id
WorkspaceId = i + 1
});
Mock<IWorkspacesRepo> mock = new Mock<IWorkspacesRepo>();
//set up the property to return the list
mock.Setup(_ => _.Workspaces).Returns(workspaces);
var target = new WorkspaceBusinessService(mock.Object, null);
var redundantWorkspace = new Workspace {
Name = Guid.NewGuid().ToString(),
UserUuid = userUuid, //<-- Note the common user id
WorkspaceId = 11
};
// Act
Action act = () => target.CreateWorkspace(redundantWorkspace);
//Assert
var ex = Assert.Throws<RpcException>(act);
Assert.That(ex.Message, Is.EqualTo("Status(StatusCode.Aborted, \"There are no free slots work workspaces\")"));
I am developing an ASP.NET MVC project. In my project I am doing unit testing. I am using Moq to mock my business logics. But I am having a problem with Moq. Especially with mock.Verify method.
This is the action I am testing
[HttpPost]
public ActionResult Edit(CreateRegionVM model)
{
if(ModelState.IsValid)
{
Region region = new Region
{
Id = model.Id,
Name = model.Name,
MmName = model.MmName,
Description = model.Description,
MmDescription = model.MmDescription,
GeoLocation = model.GeoLocation,
ImagePath = model.ImagePath
};
String imagePath = String.Empty;
if(model.ImageFile!=null && model.ImageFile.ContentLength>0)
{
imagePath = fileHelper.UploadFile(model.ImageFile, AppConfig.RegionImageDir,null);
if(String.IsNullOrEmpty(imagePath))
{
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
else
{
//create thumb & delete old images - check the image operations
fileHelper.CreateThumb(imagePath, AppConfig.RegionImageDir, AppConfig.RegionMediumThumbWidth, AppConfig.RegionMediumThumbHeight, AppConfig.MediumThumbSuffix);
fileHelper.CreateThumb(imagePath, AppConfig.RegionImageDir, AppConfig.RegionSmallThumbWidth, AppConfig.RegionSmallThumbHeight, AppConfig.SmallThumbSuffix);
fileHelper.DeleteFile(model.ImagePath);
fileHelper.DeleteFile(fileHelper.GetImagePath(model.ImagePath, AppConfig.MediumThumbSuffix));
fileHelper.DeleteFile(fileHelper.GetImagePath(model.ImagePath, AppConfig.SmallThumbSuffix));
model.ImagePath = imagePath;
}
}
try
{
regionRepo.Update(region);
TempData["message"] = "Region successfully edited";
TempData["class"] = AppConfig.FlashSuccessClass;
return RedirectToAction("Edit", new { id = model.Id });
}
catch
{
return new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
}
}
return View("Create",model);
}
This is my test function
[TestMethod]
public void Edited_And_Redirected()
{
var postFile = new Mock<HttpPostedFileBase>();
postFile.Setup(m => m.ContentLength).Returns(1);
CreateRegionVM model = new CreateRegionVM
{
Id = 1,
Name = "test",
ImageFile = postFile.Object
};
Mock<IRegionRepo> regionMock = new Mock<IRegionRepo>();
regionMock.Setup(m => m.Update(new Region())).Verifiable();
Mock<IFileHelper> fileMock = new Mock<IFileHelper>();
fileMock.Setup(m => m.UploadFile(model.ImageFile, It.IsAny<String>(), null)).Returns("upload_file_path");
RegionController controller = new RegionController(regionMock.Object, fileMock.Object, 0);
var unknownView = controller.Edit(model);
regionMock.Verify();
Assert.IsInstanceOfType(unknownView, typeof(RedirectToRouteResult), "Not redirected");
}
As you can see my test method, I am using verify to make sure regionRepo.Update method is called. But it is giving me this error when I run the test.
Moq.MockVerification Exception: The following setups were not matched:
IRegionRepo m=>m=>Update()
Why is this error thrown? How does the verify method of moq work?
Take a look at this line:
regionMock.Setup(m => m.Update(new Region())).Verifiable();
It's going to compare the input to new Region(), but most likely whatever you're passing in is not going to be referentially equal to that.
If the Region doesn't matter, try
regionMock.Setup(m => m.Update(It.IsAny<Region>())).Verifiable();