Как справиться с ограничением сущностей в памяти классом / сервисом Unit Test, использующим EF 6 - PullRequest
0 голосов
/ 24 августа 2018

Я собираюсь протестировать сервис, использующий Entity Framework 6.

Пример сценария, он будет иметь сущность «Блог и публикация», «Блог» будет иметь ноль или более сообщений. В методе сервиса он возвращает список сущностей, которые имеют заголовок блога и заголовок первого поста этого блога. Похоже ниже.

public class BlogService 
{
    private IBloggingContext _context;

    public BlogService(IBloggingContext context)
    {
        _context = context;
    }

    public List<BlogPostSumarry> GeBlogSummary()
    {
        var query = from b in _context.Blogs
                    orderby b.Name
                    select new BlogPostSumarry
                    {
                        BlogTitle = b.Name,
                        PostTitle = b.Posts.FirstOrDefault().Title
                    };

        return query.ToList();
    }
}

}

Метод модульных испытаний

[Test]
    public void GeBlogSummary_WhenMatchFound()
    {
        var post = new List<Post>()
        {
            new Post() {PostId=45, Title="abc"}
        };

        var data = new List<Blog>
        {
            new Blog { Name = "BBB" },
            new Blog { Name = "ZZZ" },
            new Blog { Name = "AAA" },
        }.AsQueryable();

        var mockSet = new Mock<DbSet<Blog>>();
        mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        var mockContext = new Mock<BloggingContext>();
        mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);

        var service = new BlogService(mockContext.Object);
        var blogs = service.GeBlogSummary();

        Assert.AreEqual(3, blogs.Count);
        Assert.AreEqual("AAA", blogs[0].BlogTitle);
        Assert.AreEqual("BBB", blogs[1].BlogTitle);
        Assert.AreEqual("ZZZ", blogs[2].BlogTitle);
    }

Модульный тест, который я использую, чтобы высмеивать использование DbContext и DbSet в данных памяти. Проблема заключается в том, что в случае, если в блогах нет ни одного пост-модульного теста, будет выдано исключение нулевой ссылки, где, как и в реальном сценарии (база данных), он работает нормально. Это потому, что в случае с памятью это Linq to Object, где, как и в EF, это Linq to Entity.

Например, если я меняю метод, как показано ниже, он работает UnitTest

public List<BlogPostSumarry> GeBlogSummary()
    {
        var query = from b in _context.Blogs
                    orderby b.Name
                    select new BlogPostSumarry
                    {
                        BlogTitle = b.Name,
//Manually checks for null validation works but any approach without change code for sack of unit test
                        PostTitle = b.Posts.Any() ? b.Posts.FirstOrDefault().Title : null
                    };

        return query.ToList();
    }

Однажды подход - это изменить запрос linq для проверки на ноль, но я думаю, что это не очень хороший подход. Если да, то как я могу выполнить тест?

1 Ответ

0 голосов
/ 24 августа 2018

По моему опыту, вы не проводите отдельные модульные тесты для уровня базы данных.Вы делаете интеграционные тесты для этого кода.Вы настраиваете реальную базу данных (может быть LocalDB, ее легко настроить) и запускаете свои тесты для этой тестовой базы данных.

Когда вы пытаетесь смоделировать базу данных, вы тестируете макет слоя базы данных, а не реальную вещь.И вы могли бы потратить много времени, работая с макетом, но никогда не будете вести себя точно так, как ведет себя база данных (если вы не планируете писать копию своей базы данных).

Одна из причин различий- LINQ для объектов памяти просто работает иначе, чем когда он переводится в SQL.Вы можете легко написать LINQ, который будет отлично тестироваться с помощью макетов, и у вас будут хорошие тесты.Но при выполнении на реальной БД тот же LINQ потерпит неудачу, потому что поставщик LINQ не знает, как преобразовать это в SQL.

Что бы вы ни делали с макетом, ваш объект памяти не будет таким же, как запросы SQL, посылаемые в вашу БД.А запросы SQL - это то, что вы действительно тестируете.Если вы их не тестируете, то нет смысла притворяться, что за хранилищем есть БД.Можно пропустить эти тесты.

...