I am writing unit test case for my Entity Framework Core SaveAsync method.
try
{
// Here add business logic to insert record in to new database
var dbObj = new TableName()
{
Code = 1,
Description = "test"
};
_ = _dbContext.TableName.AddAsync(dbObj);
_ = _dbContext.SaveChangesAsync(context.CancellationToken);
}
catch (Exception ex)
{
_logger.LogCritical($"{GetType().Name}:{nameof(Consume)} {ex}");
}
await Task.CompletedTask;
I have written unit test case by mocking:
Mock<MyDbContext> dbContext = new();
var data = return new List<TableName>()
{
new TableName
{
Id = 1,
Description = "test"
}
};
dbContext.Setup(r => r.TableName).ReturnsDbSet(data);
dbContext.Setup(r => r.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1);
// Check that each method was only called once.
//dbContext.Verify(x => x.TableName.Add(It.IsAny<TableName>()), Times.Once());
dbContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
Below is what I get as a result
Moq.MockException : Expected invocation on the mock once, but was 0 times
While debugging the test case, I am getting the below.
Firstly,add await into try catch block.otherwise it would not catch excption
try
{
// Here add business logic to insert record in to new database
var dbObj = new TableName()
{
Code = 1,
Description = "test"
};
_ = await _dbContext.TableName.AddAsync(dbObj);
_ = await _dbContext.SaveChangesAsync(context.CancellationToken);
}
//remove this line
await Task.CompletedTask;
The document related with Asynchronous programming
And you could check the document related with testing non query scenarios
I tried as below:
Assuming a controller:
public async Task<IActionResult> Create([Bind("Id,Name")] SomeEntity someEntity)
{
if (ModelState.IsValid)
{
try
{
await _context.AddAsync(someEntity);
await _context.SaveChangesAsync();
}
catch (Exception e)
{
}
return RedirectToAction(nameof(Index));
}
return View(someEntity);
}
Test:
public class UnitTest1
{
[Fact]
public async Task Test1()
{
var mockset = new Mock<DbSet<SomeEntity>>();
var mockdbcontext = new Mock<WebApplication8Context>(new DbContextOptions<WebApplication8Context>() );
mockdbcontext.Setup(x=>x.SomeEntity).Returns(mockset.Object);
var a = mockdbcontext.Object;
var controller = new SomeEntitiesController(mockdbcontext.Object);
await controller.Create(new SomeEntity() { Name = "someName" });
mockdbcontext.Verify(x => x.SaveChangesAsync(default(CancellationToken)), Times.Once);
}
}
Result:
Related
I'm new at asp.net and moq and xunit test, I want to create a test case for my asp.net project. I have the service file that is CRUD action,
I have been written for Create Method it's Ok but at Get, GetAll, Delete, Put method I don't know what should I setup
public void Test1()
{
var tutorRequest = new Mock<ITutorRequestService>();
//What should i set up for Get and GetAll
tutorRequest.Setup(r => r.Get(It.IsAny<int>(), It.IsAny<string?>()))
.ReturnsAsync(() => new Entity());
var logger = new Mock<ILogger<TutorRequestsController>>();
var controller = new TutorRequestsController(tutorRequest.Object, logger.Object);
var rs = controller.Get(1, "");
Assert.NotNull(rs);
}
My service class
public async Task<TutorRequest> Create(TutorRequestModel tutor)
{
var entity = _mapper.Map<TutorRequest>(tutor);
CheckDataNotNull("entity", entity);
entity = await _repository.Add(entity);
return _mapper.Map<TutorRequest>(entity);
}
public async Task<TutorRequest> Delete(int tutorId)
{
var entity = await _repository.Delete(tutorId);
return _mapper.Map<TutorRequest>(entity);
}
public async Task<Entity> Get(int id, string? fields)
{
var entity = await _repository.Get(id);
CheckDataNotNull("tutor request", entity);
var shaped = _dataShaper.ShapeData(entity, fields);
CheckDataNotNull("shaped", shaped);
return _mapper.Map<Entity>(shaped);
}
public PageList<Entity> GetAll(TutorRequestParams param)
{
var listAll = _repository.GetAll();
CheckDataNotNull("shaped", listAll);
Search(ref listAll, param);
var sortedStudent = _sortHelper.ApplySort(listAll, param.OrderBy);
var shapedOwners = _dataShaper.ShapeData(sortedStudent, param.Fields);
return PageList<Entity>.ToPageList(shapedOwners, param.PageNume, param.PageSize);
}
public async Task<TutorRequest> Update(TutorRequestModel tutor)
{
var entity = _mapper.Map<TutorRequest>(tutor);
entity = await _repository.Update(entity);
return _mapper.Map<TutorRequest>(entity);
}
If you're expecting the GetAll method to return something, you need to set up the call for the mocked service.
mockRepo.Setup(r => r.GetAll(It.IsAny<TutorRequestParams>())).Returns(
new PagedList<ExpandoObject>
{
// result you're expecting
}
);
I have write a test case for Get method that similar to your example, you can check it out
public void Test_Get_Method()
{
var questService = new Mock<IQuestService>();
questService.Setup(x => x.Get(It.IsAny<Guid>()))
.ReturnsAsync(new QuestResponseModel()
{
Id = Guid.Empty,
Title = "Test",
Description = "TestDescription",
Price = 100,
Status = "Pending",
AvailableTime = DateTime.Now,
EstimatedTime = "120",
QuestTypeId = 1
});
var mapperMock = new Mock<IMapper>();
var controller = new QuestController(questService.Object);
var result = controller.Get(Guid.Empty);
Assert.Equal("TestDescription", result.Result.Data.Description);
}
I am using ASP.NET identites userManager.CreateAsync so it automatically populates NormalizedEmail from email field and stuff like that. However this time I want to insert user together with some metadata (in some other tables) which means I need transaction.
I successfully made it work without throwing exception but it seems transaction is not rollbacked (UserManager uses some different dbContext?)
Here is code:
try
{
await ResilientTransaction.New(_databaseContext).ExecuteAsync(async () =>
{
var currentUser = await _userManager.FindByIdAsync(_identityContext.UserId.ToString());
user.ParentUserId = _identityContext.UserId;
var existingUserWithEmail = await FindByEmailAsync(user.Email);
if (existingUserWithEmail != null)
{
throw new ValidationException($"User with email {user.Email} already exists");
}
var currentUserRoles = await _roleRepository.GetWithPermissionsByUserId(_identityContext.UserId);
var result = await _userManager.CreateAsync(user);
var rolesResult = await _userManager.AddToRolesAsync(user, currentUserRoles.Select(x => x.Name));
if (!result.Succeeded)
{
throw new ValidationException(result.Errors.FirstOrDefault()!.Description);
}
if (currentUser.OverrideVehicleAccessSettingsByEmailDomainName)
{
user.OverrideVehicleAccessSettingsByEmailDomainName = true;
var vehicleAccessSettings = await _vehicleAccessSettingRepository.GetAll(x => x.UserId == _identityContext.UserId);
foreach (var vehicleAccessSetting in vehicleAccessSettings)
{
vehicleAccessSetting.UserId = user.Id;
// vehicleAccessSetting.Id = 0;
_ = await _vehicleAccessSettingRepository.Insert(vehicleAccessSetting);
}
}
user.Roles = currentUserRoles;
});
}
catch (Exception ex)
{
throw;
}
And this ResillianteTransaction is just this:
public class ResilientTransaction
{
private readonly DbContext _context;
private ResilientTransaction(DbContext context) =>
_context = context ?? throw new ArgumentNullException(nameof(context));
public static ResilientTransaction New(DbContext context) =>
new ResilientTransaction(context);
public async Task ExecuteAsync(Func<Task> action)
{
// Use of an EF Core resiliency strategy when using multiple DbContexts
// within an explicit BeginTransaction():
// https://learn.microsoft.com/ef/core/miscellaneous/connection-resiliency
var strategy = _context.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
await using var transaction = await _context.Database.BeginTransactionAsync();
try
{
await action();
await transaction.CommitAsync();
}
catch (Exception)
{
await transaction.RollbackAsync();
throw;
}
});
}
}
I've read similar questions but could not manage to solve my own. I'm using xUnit and I'm running into an issue when one of my methods calls, it's returning null but in fact that I have mocked it.
Interface
public interface IApplicantService
{
Task<Applicant> AddAsync(Applicant applicant);
// other methods
}
Test Case
public class ApplicationControllerTests
{
private readonly Mock<IApplicantService> _mockApplicantService;
private readonly ApplicantController _applicantController;
private readonly IMapper _mockMapper;
public ApplicationControllerTests()
{
_mockApplicantService = new Mock<IApplicantService>();
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new ResourceToModelProfile());
cfg.AddProfile(new ModelToResourceProfile());
});
_mockMapper = mapperConfig.CreateMapper();
_applicantController = new ApplicantController(_mockApplicantService.Object, _mockMapper);
}
[Fact]
public async void CreateAsync_WhenApplicantNotExist_ShouldReturn_CreatedAtActionResult_With_Resource()
{
var applicantDto = new ApplicantCreateDto
{
PersonId = 1,
VacancyId = 1
};
_mockApplicantService.Setup(e => e.AddAsync(It.IsAny<Applicant>()))
.Returns(Task.FromResult(new Applicant { Id = 1, PersonId = 1, VacancyId = 1}));
var result = await _applicantController.CreateAsync(applicantDto);
var createdAtActionResult = result as CreatedAtActionResult;
var model = createdAtActionResult.Value as ApplicantResponseDto;
var actual = model.PersonId;
Assert.NotNull(model);
Assert.Equal(1, actual);
Assert.NotNull(createdAtActionResult);
}
}
Controller
[HttpPost]
[Route("CreateAsync")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> CreateAsync([FromBody] ApplicantCreateDto applicantCreateDto)
{
try
{
var applicant = _mapper.Map<ApplicantCreateDto, Applicant>(applicantCreateDto);
var result = await _applicantService.AddAsync(applicant);
// here, result is null, but it was mocked to return an Applicant object
var resource = _mapper.Map<Applicant, ApplicantResponseDto>(result);
return CreatedAtAction(nameof(GetAsync), new { id = result.Id }, resource);
}
catch (ResourceExistException ex)
{
return Conflict(ex.Message);
}
catch(Exception ex)
{
// log exception
return StatusCode(500);
}
}
The mocked method is returning null and I'm getting System.NullReferenceException
This is how your fixed unit test could look like:
[Fact]
public async Task CreateAsync_WhenApplicantNotExist_ShouldReturn_CreatedAtActionResult_With_Resource()
{
//Arrange
var applicantDto = new ApplicantCreateDto { PersonId = 1, VacancyId = 1 };
var applicant = new Applicant { Id = 1, PersonId = 1, VacancyId = 1 };
_mockApplicantService
.Setup(svc => svc.AddAsync(It.IsAny<Applicant>()))
.ReturnsAsync(applicant);
//Act
var result = await _applicantController.CreateAsync(applicantDto);
//Assert
var createdAtActionResult = Assert.IsAssignableFrom<CreatedAtActionResult>(result);
var model = Assert.IsAssignableFrom<ApplicationResponseDto>(createdAtActionResult.Value);
Assert.Equal(1, model.PersonId);
}
I've replaced async void to async Task that way your await will be evaluated properly
I've changed the Returns(Task.FromResult(...)) to ReturnsAsync(...) because this is the recommended way to specify return value in case of async methods
I've also added some comments to separate the different phases of your unit test from each other (Arrange-Act-Assert)
I've changed your assertion logic to use IsAssingableFrom to verify the type itself rather than doing null checks
I'm working on a ASP.NET Core 2.1 Api controller that returns a FileStreamResult. My business layer returns a MemoryStream object that has been rewound to the start position. What I am trying to do is write a unit test that checks if the expected MemoryStream is return from the method. However, when I attempt this, the test method just hangs. I'm using Automapper, NSubstitute and Xunit for mapping, mocking, and testing respectively.
//Action Method
[Route("Excel")]
[HttpPost]
[ProducesResponseType(typeof(string), 200)]
public ActionResult CreateExcelExport([FromBody]ExportRequestApiModel exportRequest)
{
try
{
var records = _mapper.Map<IEnumerable<ExportRecord>>(exportRequest.Records);
var result = _excelFileManager.GenerateFile(records, "sheet 1", 1);
return new FileStreamResult(result,
new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
{
FileDownloadName = "export.xlsx"
};
}
catch (Exception ex)
{
if (!(ex is BusinessException))
_logger?.LogError(LoggingEvents.GeneralException, ex, ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
//Action Method test.
[Fact]
public void CanCreateExportFile()
{
//Arrange
var exportRequestApiModel = new ExportRequestApiModel()
{
Records = new List<ExportRecordApiModel>() { }
};
var exportRecords = new List<ExportRecord>
{
new ExportRecord()
};
_mapper.Map<IEnumerable<ExportRecord>>(exportRequestApiModel.Records)
.Returns(exportRecords);
_excelFileManager.GenerateFile(exportRecords, "sheet 1", 1)
.Returns(new MemoryStream(){Position = 0});
//Act
var result = (ObjectResult) _controller.CreateExcelExport(exportRequestApiModel);
//Assert
Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
Assert.IsType<FileStream>(result.Value);
}
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.