Столбец Mock ForeignKey не работает в соответствии с ожиданиями - PullRequest
0 голосов
/ 09 мая 2018

Мои сущности похожи на ниже:

public class Class1
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Class2 Class2{ get; set; }
}

public class Class2
{
    [Key, ForeignKey("Class1")]
    public int Id { get; set; }
    public int? Price { get; set; }
    public virtual Class1 Class1{ get; set; }
}

И у меня есть следующий тест, который я ожидал провалить, но он пройден:

[TestMethod]
public void CreateClass2()
{
    var mockSet = new Mock<DbSet<Class2>>();

    var mockContext = new Mock<theContext>();
    mockContext.Setup(m => m.Class2s).Returns(mockSet.Object);

    var service = new Class2Service(mockContext.Object);
    service.AddClass2(10, 100);

    mockSet.Verify(m => m.Add(It.IsAny<Class2>()), Times.Once());
    mockContext.Verify(m => m.SaveChanges(), Times.Once());
}

И это метод service.AddClass2:

public class Class2Service
{
    private readonly theContext _context;

    public CoordService(theContext context)
    {
        _context = context;
    }
    public Class2 AddClass2(int id, int? price)
    {
        var class2 = _context.Class2s.Add(new Class2 { Id = id, Price = price });
        _context.SaveChanges();

        return class2;
    }
}

Так как я еще не добавил Class1 и ввел недопустимый id для Class2 (т.е. он не существует в Class1), новая запись для Class2 должна быть недействительной, поскольку я ограничен это с внешним ключом. В моем проекте и реальной базе данных он работает как положено и выдает ошибку, но в этом тесте он пройден!

1 Ответ

0 голосов
/ 09 мая 2018

Тест использует фиктивный контекст вместо реального, поэтому _context.SaveChanges(); на самом деле ничего не сделает, поэтому исключение не выдается.

Вы можете легко узнать, как правильно смоделировать DbContext, например, здесь или здесь

Также я думаю, что сам модульный тест должен быть написан по-другому. То есть здесь вы тестируете вызовы методов, которые являются базовой реализацией добавления записи.

Правильный модульный тест для этого случая проверит, вызывает ли вызов service.AddClass2(10, 100); исключение. И отдельный тест должен быть написан, чтобы увидеть, появляется ли новая запись в DbSet в результате вызова метода service.AddClass2 вместо проверки того, что некоторые конкретные методы были вызваны.

UPD: пример установки IQueriable в качестве источника для поддельного контекста.

    public static class MockDbSetExtensions
{
    public static void SetSource<T>(this Mock<DbSet<T>> mockSet, IList<T> source) where T : class
    {
        var data = source.AsQueryable();
        var iQuryableSet = mockSet.As<IQueryable<T>>();

        iQuryableSet.SetupGet(m => m.Provider).Returns(data.Provider);
        iQuryableSet.SetupGet(m => m.Expression).Returns(data.Expression);
        iQuryableSet.SetupGet(m => m.ElementType).Returns(data.ElementType);
        iQuryableSet.Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
    }
}

и пример использования:

    [TestClass]
public class GigRepositoryTests
{

    private GigRepository _repository;
    private Mock<DbSet<Gig>> _mockGigs;
    private Mock<IApplicationDbContext> mockContext;

    [TestInitialize]
    public void TestInitialize()
    {
        _mockGigs = new Mock<DbSet<Gig>>();

        mockContext = new Mock<IApplicationDbContext>();


        _repository = new GigRepository(mockContext.Object);
    }

    [TestMethod]
    public void GetUpcomingGigsByArtist_GigsInThePast_ShouldNotBeReturned()
    {
        var gig = new Gig() {DateTime = DateTime.Now.AddDays(-1), ArtistId = "1"};

        _mockGigs.SetSource(new List<Gig> {gig});
        mockContext.SetupGet(c => c.Gigs).Returns(_mockGigs.Object);

        var gigs = _repository.GetUpcomingGigs("1");

        gigs.Should().BeEmpty();
    } }
...