2017-04-04 82 views
1

我正在尝试使用AutoMoq和Xunit插入特性来自动化UnitTesting。Xunit - 如何使用Moq和EF Core作为标识主键

但我一直得到,我不能插入一个值到KeyColumn作为以下。 EnrolmentRecordID是我的SQL数据库中的IdentityColumn,它的值是在插入时自动生成的。

消息:Microsoft.EntityFrameworkCore.DbUpdateException:更新条目时发生错误 。请参阅 的内部例外详情。 ---- System.Data.SqlClient.SqlException:当IDENTITY_INSERT为 设置为OFF时,无法在表'EN_Schedules'中插入标识列的显式值。

这可以避免,如果我不使用Moq或我不设置数据到EnrolmentRecordID列。但我不知道如何排除AutoMoq中的EnrolmentRecordID。由于它是关键列,因此我无法将NULLABLE功能设置为该列。

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); 
    } 
} 

你能帮我^ h我可以使用Moq生成MockObject并测试Add服务吗?谢谢。

+0

这是一个集成测试吗? – Nkosi

+0

不,单元测试 – TTCG

+0

@Nkosi我不应该在UnitTest中使用真实的数据库吗? – TTCG

回答

1

这个简化的例子展示了如何将测试对象与结核分离,以便它可以单独进行单元测试。

抽象掉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)); 
} 

确保实施包装的实际情况和提供所需的功能。

主题类取决于抽象。

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(); 
    } 
} 

有了这样的地方,测试对象的依赖关系就可以被模拟并用于测试。

[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); 
} 
+0

在.Net Core中使用InMemoryDatabase功能如何?你怎么看呢? 什么是“context.Verify();”做? – TTCG

+1

@TTCG,我的偏好是在一定程度上将抽象用于单元测试和内存中的dbs进行集成测试。 'context.Verify'要求模拟器验证所有设置是否按预期执行。您必须将设置标记为'.Verifiable()'以使'.Verify()'工作。 – Nkosi