2017-04-04 1 views
1

Ich versuche, meine UnitTesting mit AutoMoq und Xunit für das Einfügen zu automatisieren.Xunit - So verwenden Sie Moq und EF Core für Identity Primärschlüssel

Aber ich bekomme immer wieder, dass ich keinen Wert wie folgt in die KeyColumn einfügen kann. EnrolmentRecordID ist die IdentityColumn in meiner SQL-Datenbank und ihr Wert wird automatisch beim Einfügen generiert.

Nachricht: Microsoft.EntityFrameworkCore.DbUpdateException: Ein Fehler ist aufgetreten, während die Einträge zu aktualisieren. Siehe die innere Ausnahme für Details. ---- System.Data.SqlClient.SqlException: Der explizite Wert für die Identitätsspalte kann nicht in die Tabelle 'EN_Schedules' eingefügt werden, wenn IDENTITY_INSERT auf OFF gesetzt ist.

Es kann vermieden werden, wenn ich Moq nicht oder ich nicht setzen die Daten an EnrolmentRecordID Spalte. Aber ich weiß nicht, wie man EnrolmentRecordID in AutoMoq ausschließt. Da es sich um die Schlüsselspalte handelt, kann ich die NULLABLE-Funktion auch nicht auf diese Spalte setzen.

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

Könnten Sie mir bitte helfen h wie könnte ich Moq verwenden, um die MockObject zu generieren und den Add Service zu testen? Vielen Dank.

+0

Ist dies ein Integrationstest? – Nkosi

+0

Nein, Unit Test – TTCG

+0

@Nkosi Soll ich nicht die echte Datenbank im UnitTest verwenden? – TTCG

Antwort

1

Dieses vereinfachte Beispiel zeigt, wie das zu testende Objekt von Konkretionen entkoppelt wird, so dass es einzeln isoliert getestet werden kann.

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

dafür sorgen, dass die Umsetzung des eigentlichen Kontext wickelt und gewünschte Funktionalität zur Verfügung stellt.

Lassen Sie die Subjektklasse von der Abstraktion abhängig sein.

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

Damit können die Abhängigkeiten des zu testenden Objekts verspottet und bei der Ausübung des Tests verwendet werden.

[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

Wie wäre es mit der InMemoryDatabase-Funktion in .Net Core? Was denkst du darüber? Und was bedeutet "context.Verify();" machen? – TTCG

+1

@TTCG, Meine Präferenz verwendet die Abstraktion für Komponententests und In-Memory-dbs für Integrationstests in gewissem Maße. 'context.Verify' fragt den Mock, um zu überprüfen, dass alle Einstellungen wie erwartet ausgeführt wurden. Sie müssen das Setup als '.Verifizierbar()' markieren, damit '.Verify()' funktioniert. – Nkosi

Verwandte Themen