2017-04-04 41 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