Модульное тестирование базы данных с использованием EF - PullRequest
0 голосов
/ 11 января 2019

Как один модуль тестирует сервис, который подключается к базе данных?

У меня есть класс playerRepository на уровне доступа к данным, который напрямую взаимодействует с базой данных, и класс playerService на бизнес-уровне, который создает экземпляр playerRepository и обслуживает случайные вещи, такие как - удаление игрока, сохранение игрока , получение всех игроков, получение игрока по идентификатору / имени yadda yadda.

Я хочу провести модульное тестирование playerService без использования реальной базы данных, но с использованием базы данных в памяти, поставляемой с EF.

Проблема в том, что я не могу понять, как его настроить.

У меня есть класс PlayerContext : DbContext, который используется в качестве модели для кодирования вначале (это было сделано в руководстве по EF). И я должен добавить параметр в конструктор DbContextOptionsBuilder<PlayerContext>, но я не знаю, с чего начать. Я не знаю, как и где настроить строку подключения, где хранится база данных «по умолчанию».

Я хочу сделать это, используя EF без NSubstitute или Moq, я делаю это, чтобы узнать, как это делается без использования других фреймворков, кроме EF.

public class PlayerContext : DbContext
{
    public PlayerContext() : base()
    {

    }

    public DbSet<Player> Players { get; set; }
}

public class Player
{
    public string Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int DciNumber { get; set; }
}

Я использую Visual Studio при условии модульного тестирования

[TestClass]
public class PlayerServiceTest
{
    // Should get all the players from database
    [TestMethod]
    public void GetAllPlayers()
    {
        // arrange - act
        // TODO: Return all the players from the database

        // assert
        // TODO: Should return empty list
    }

И класс PlayerService выглядит примерно так

public class PlayerService
{
    private PlayerRepository _playerRepository = new PlayerRepository();


    public List<Player> GetAllPlayers()
    {
        var players = _playerRepository.GetAllPlayers();

        return players;
    }

PlayerRepository

public class PlayerRepository
{
    public List<Player> GetAllPlayers()
    {
        using (var context = new PlayerContext())
        {
            var players = context.Players.ToList();

            return players;
        }
    }

Обычно мои вопросы:

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

  2. Как изменить местоположение базы данных, поскольку она использует путь по умолчанию в C:\Users.

Я не ищу интеграционных тестов с DAL PlayerRepository, я просто хочу протестировать бизнес-уровень, и все, что мне нужно, это когда я запускаю тесты, которые PlayerService использует PlayerRepository, который подключается к оперативной памяти база данных и все тут. В противном случае он должен подключиться к локальной базе данных, хранящейся в той же папке, что и основной исполняемый файл приложения

Требуется помощь!

1 Ответ

0 голосов
/ 14 января 2019

Недостающим элементом является внедрение зависимостей / IoC. Принцип здесь заключается в том, чтобы определить ваши зависимости (репозиторий) с помощью интерфейса контракта, который можно макетировать. Добавьте эту зависимость в классы, которые зависят от нее. Таким образом, вы можете заменить конкретные реализации, такие как базы данных, обработка файлов и т. Д., На макеты объектов, которые возвращают известное состояние, генерируют ожидаемое исключение и т. Д., Чтобы протестировать обработку бизнес-логики этих сценариев.

public class PlayerService
{
    private readonly IPlayerRepository _playerRepository = null;

    public PlayerService(IPlayerRepository playerRepository)
    {
         _playerRepository = playerRepository ?? throw new ArgumentNullException("playerRepository");
    }


    public List<Player> GetAllPlayers()
    {
        var players = _playerRepository.GetAllPlayers();

        return players;
    }
}

Посмотрите на контейнеры IoC, такие как Autofac, Unity, Ninject и т. Д., Чтобы найти примеры того, как контейнер может быть создан для автоматической идентификации и внедрения конкретных экземпляров классов в ваши службы при их создании.

Когда вы собираетесь написать модульный тест, вы создаете макет класса IPlayerRepository (см., Например, Moq) и передаете его тестируемой службе. I.e.:

[Test]
public void TestService()
{
   var mockRepository = new Mock<IPlayerRepository>();
   mockRepository.Setup(x => x.GetPlayers()).Returns(buildTestPlayerList());
   var serviceUnderTest = new PlayerService(mockRepository.Object);
   // continue with test...
}

В худшем случае: если вы хотите отказаться от контейнера, это также может сработать:

public class PlayerService
{
    private IPlayerRepository _playerRepository = null;
    public IPlayerRepository PlayerRepository
    {
        get { return _playerRepository ?? (_playerRepository = new PlayerRepository()); }
        set { _playerRepository = value; }
    }

    // ...
}

... а затем с тестом ...

[Test]
public void TestService()
{
   var mockRepository = new Mock<IPlayerRepository>();
   mockRepository.Setup(x => x.GetPlayers()).Returns(buildTestPlayerList());
   var serviceUnderTest = new PlayerService { PlayerRepository = mockRepository.Object };
   // continue with test...
}

Это шаблон, который я называю «внедрение ленивых свойств», в котором вы можете выбрать отправку зависимости, но по умолчанию он просто создает зависимость по умолчанию. Это может быть полезным шаблоном при внедрении подстановки зависимостей и модульного тестирования в унаследованный код, который в значительной степени зависел от обновления среднего класса классов. Это лениво в том смысле, что зависимость «обновляется» только при первом обращении к ней. Если вы вызываете метод в службе, который не нуждается в зависимости, то инициализация каждой зависимости не происходит, только те, которые используются. Я настоятельно рекомендую прочитать о контейнерах IoC, так как они помогают автоматизировать соединение зависимостей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...