Xunit - How to use Moq and EF Core for Identity Primary Key

c# entity-framework-core moq xunit

Question

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.

1
1
4/4/2017 2:35:11 PM

Accepted Answer

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);
}
1
4/4/2017 2:33:47 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow