单元测试中AutoMapper映射JobViewModel至DataAccess.Domain.Lab.Job始终返回空值的问题排查
Alright, let's figure out why your _mapper.Map call returns null in the unit test but works perfectly with Postman. The core issue is how you've configured your IMapper mock—right now, it's not set up to return anything when the Map method is called, so Moq defaults to returning null for reference types.
Let's Break Down the Problem
- Missing
Mapmethod setup: You've configured the mock to return aMapperConfigurationwith yourJobsProfile, but you never told it what to do whenMap<DataAccess.Domain.Lab.Job>(JobViewModel)is invoked. Moq doesn't automatically use the configuration you provided—it only follows explicit setups you define. - Unused mock variable: In your
AddJob_AddSingleEntrytest, you createvar mapperMock = new Mock<IMapper>()but never use it, which is just extra noise.
Solution 1: Fix the Mock Setup
If you want to keep using a mock for IMapper, explicitly define the return value for the Map method. Update your test setup to tell the mock how to convert a JobViewModel to a Job:
public class JobTests { private static Mock<IMapper> _mapper; public JobTests() { if (_mapper == null) { _mapper = new Mock<IMapper>(); // Add this setup to handle the Map call _mapper.Setup(x => x.Map<DataAccess.Domain.Lab.Job>(It.IsAny<JobViewModel>())) .Returns((JobViewModel vm) => new DataAccess.Domain.Lab.Job { // Map properties from vm to Job here—match your JobsProfile logic Id = vm.Id, Name = vm.Name, Notes = vm.Notes?.Select(note => new JobNote { // Map note properties as needed Text = note.Text, IsNew = note.IsNew }).ToList(), // Add all other required properties }); } } [Fact] public async Task AddJob_AddSingleEntry() { // Remove the unused mapperMock variable var data = JobData.AddFakeJobList(); var mockSet = FakeDbSetup.GetMockDbSet<DataAccess.Domain.Lab.Job>(data); var mockContext = FakeDbSetup.GetMockDbContext(); mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object); mockContext.Setup(c => c.Jobs).Returns(mockSet.Object); AddJobCommandHandler handler = new AddJobCommandHandler(mockContext.Object, _mapper.Object); JobViewModel vm= JobData.AddFakeJobList2(); AddJobCommand command = new AddJobCommand(vm); var jobViewModelResult = await handler.Handle(command, new System.Threading.CancellationToken()); Assert.NotNull(jobViewModelResult); } }
Solution 2: Use a Real Mapper (Recommended)
Mocking IMapper can lead to discrepancies between your test and production code—you might forget to map a property in the mock, while the real JobsProfile has it covered. A better approach is to create a real IMapper instance using your actual configuration:
public class JobTests { private static IMapper _mapper; public JobTests() { if (_mapper == null) { // Create a real mapper with your JobsProfile var config = new MapperConfiguration(cfg => { cfg.AddProfile<JobsProfile>(); }); // Validate your mapping configuration to catch issues early config.AssertConfigurationIsValid(); _mapper = config.CreateMapper(); } } [Fact] public async Task AddJob_AddSingleEntry() { var data = JobData.AddFakeJobList(); var mockSet = FakeDbSetup.GetMockDbSet<DataAccess.Domain.Lab.Job>(data); var mockContext = FakeDbSetup.GetMockDbContext(); mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object); mockContext.Setup(c => c.Jobs).Returns(mockSet.Object); AddJobCommandHandler handler = new AddJobCommandHandler(mockContext.Object, _mapper.Object); JobViewModel vm= JobData.AddFakeJobList2(); AddJobCommand command = new AddJobCommand(vm); var jobViewModelResult = await handler.Handle(command, new System.Threading.CancellationToken()); Assert.NotNull(jobViewModelResult); } }
This way, you're testing the exact same mapping logic that runs in production, so you can trust the test results more. The AssertConfigurationIsValid() call will also throw an error if your JobsProfile has missing or invalid mappings, helping you catch issues before they reach production.
Final Checks
- Make sure your
JobsProfilecorrectly defines the mapping fromJobViewModeltoDataAccess.Domain.Lab.Job—if that's missing, even the real mapper will fail. - Double-check that all required properties are being mapped (either explicitly in the profile or via AutoMapper's conventions).
内容的提问来源于stack exchange,提问作者user19639716

