I am trying to automate my UnitTesting with AutoMoq and Xunit for Inserting feature.
But I keep getting that I cannot insert a value into the KeyColumn as the following. EnrolmentRecordID
is the IdentityColumn in my SQL db and its value is generated automatically at the insertion.
Message: Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while updating the entries. See the inner exception for details. ---- System.Data.SqlClient.SqlException : Cannot insert explicit value for identity column in table 'EN_Schedules' when IDENTITY_INSERT is set to OFF.
It can be avoided, if I don't use Moq or I don't set the data to EnrolmentRecordID
column. But I don't know how to exclude EnrolmentRecordID
in AutoMoq. Since it's the key column, I cannot set the NULLABLE feature to that column too.
StudentSchedule.cs
public class StudentSchedule
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EnrolmentRecordID { get; set; }
public string AcademicYearID { get; set; }
[Display(Name = "Student")]
public string StudentName { get; set; }
public string ProposedQual { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? DateCreated { get; set; }
}
AddService
public async Task Add(StudentSchedule model)
{
await _context.Schedules.AddAsync(model);
await _context.SaveChangesAsync();
}
XUnitTest
public class TestCommandsSchedule
{
private ERAppData.Commands.CommandSchedule _command;
public TestCommandsSchedule()
{
_command = new ERAppData.Commands.CommandSchedule(AppsecDBContext.GenerateAppsecDBContext() as ERAppData.DbContexts.AppsecDbContext);
}
[Theory]
[AutoMoqData]
public async Task Should_Add_Schedule(StudentSchedule model)
{
model.AcademicYearID = "16/17";
model.DateCreated = null;
await _command.Add(model);
Assert.True(model.EnrolmentRecordID > 0);
}
}
Could you please help me how I could use Moq to generate the MockObject
and test the Add
Service? Thanks.
this simplified example shows how to decouple the subject under test from concretions so that it can be unit tested in isolation.
Abstract away the DbContext
public interface IStudenScheduleService : IGenericRepository<StudentSchedule> {
}
public interface IGenericRepository<T> {
Task<T> AddAsync(T value, CancellationToken cancellationToken = default(CancellationToken));
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
}
making sure that the implementation wraps the actual context and provides desired functionality.
Have the subject class depend on the abstraction.
public class CommandSchedule {
private readonly IStudenScheduleService _context;
public CommandSchedule(IStudenScheduleService context) {
this._context = context;
}
public async Task Add(StudentSchedule model) {
await _context.AddAsync(model);
await _context.SaveChangesAsync();
}
}
With that in place the dependencies of the subject under test can be mocked and used in exercising the test.
[Theory]
[AutoMoqData]
public async Task Should_Add_Schedule(StudentSchedule model)
//Arrange
var expectedId = 0;
var expectedDate = DateTime.Now;
var context = new Mock<IStudenScheduleService>();
context.Setup(_ => _.SaveChangesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(1)
.Callback(() => {
model.EnrolmentRecordID = ++expectedId;
model.DateCreated = expectedDate;
})
.Verifiable();
context.Setup(_ => _.AddAsync(It.IsAny<StudentSchedule>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((StudentSchedule m, CancellationToken t) => m)
.Verifiable();
var _command = new CommandSchedule(context.Object);
model.AcademicYearID = "16/17";
model.DateCreated = null;
//Act
await _command.Add(model);
//Assert
context.Verify();
Assert.AreEqual(expectedId, model.EnrolmentRecordID);
Assert.AreEqual(expectedDate, model.DateCreated);
}