Тестирование базы данных вызывает С#

Во-первых, дайте адресную терминологию здесь. Все, что я ищу, говорит: "Модульные тесты не касаются базы данных!" Я не хочу unit test. Я хочу проверить, что когда я отправляю данные в базу данных, я знаю, что он правильно сохраняет ее (и тестирование других операций crud). У меня есть слой репозитория, который по существу принимает DTO, а затем сопоставляет DTO с моделью сущности, а затем сохраняет эту модель в базе данных.

Мне нужно убедиться, что отправка DTO этим методам фактически сохраняется в базе данных.

Подписями примерного метода в репозитории является:

public bool Save(SomeObjectDTO someObject)

Мне просто нужно проверить, действительно ли этот метод возвращает true.

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

Кроме того, существует ли стандартный способ создания пустой базы данных тестирования? Было бы здорово, если бы я нажал "Run tests", он построил пустую базу данных, заполнил ее исходными данными, что необходимо, а затем выполнил все операции CRUD (все мои вызовы репозитория), чтобы увидеть, что все они сохраняются, как они должен быть.

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

Мне просто нужен пример и/или стандартная практика того, как эти типы тестов должны быть настроены.

Ответ 1

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

Я думаю, что также важно, чтобы вы протестировали против того же поставщика баз данных, что и ваша производственная система, в противном случае существует риск несоблюдения конкретного конкретного поведения. Я использую Azure SQL для проекта, и вместо создания экземпляра SQL CE в памяти у меня есть отдельная база данных Azure, которая используется только для моих тестов интеграции.

Если вы используете XUnit (и я уверен, что он существует для других тестовых фреймворков), есть удобный атрибут [AutoRollback], который автоматически откатит вашу транзакцию после каждого теста.

[Fact]
[AutoRollback]
public void AddProductTest_AddsProductAndRetrievesItFromTheDatabase()
{
    // connect to your test db
    YourDbContext dbContext = new YourDbContext("TestConnectionString")

    dbContext.Products.Add(new Product(...));

    // get the recently added product (or whatever your query is)
    var result = dbContext.Single();

    // assert everything saved correctly
    Assert.Equals(...);
}

После завершения теста ваша база данных снова будет на пустой шифер (или что бы это было до запуска теста).

Ответ 2

Для тестирования базы данных при использовании EntityFramework, вот как я скачу:

Прежде всего, я определяю класс, который будет обращаться к ObjectContext с factory для ObjectContext при необходимости: в моем случае я работаю в службе NT, поэтому контекст не живет во время запрос или какую-либо другую область: YMMV, но если вы тестируете компонент, вы можете работать в полной изоляции без лишних хлопот, так как ваш factory для контекста в сети, конечно, извлечет контекст из запроса: просто не инициализируйте/закройте его в вашем классе DAL.

public DataAccessClass: IWorkOnStuff
{
    public Func<DataEntities> DataAccessFactory { get; internal set; }

    private string ConnectionString;
    public PortailPatientManagerImplementation(string connectionString)
    {
        ConnectionString = connectionString;
        DataAccessFactory = () => { return new DataEntities(ConnectionString); };
    }

    /* interface methods */

    public IEnumerable<Stuff> GetTheStuff(SomeParameters params)
    {
        using (var context = DataAccessFactory())
        {
             return context.Stuff.Where(stuff => params.Match(stuff));
        }
    }
}

Теперь интересно то, что, когда вы хотите протестировать это, вы можете использовать библиотеку под названием Effort, которая позволяет вам сопоставлять базу данных в Память. Для этого просто создайте свой класс, и в тестовой настройке скажите "Усилия", чтобы взять его отсюда:

public class TestDataAccessClass
{
    public DataAccessClass Target { get; set; }

    protected int Calls = 0;
    protected DataEntities DE;

    [SetUp]
    public void before_each_test()
    {
        Target = new DataAccessClass(string.Empty);
        Calls = 0;
        FullAccessCalls = 0;

        var fakeConnection = "metadata=res://*/bla.csdl|res://*/bla.ssdl|res://*/bla.msl;provider=System.Data.SqlClient";

        DE = Effort.ObjectContextFactory.CreateTransient<DataEntities>(fakeConnection);
        Target.DataAccessFactory = () => { Calls++; return DE; };

        SetupSomeTestData(DE);
    }

}

В SetupSomeTestData просто добавьте объекты, которые вы хотите (ссылки и т.д.), и теперь вы можете вызвать свои методы, чтобы убедиться, что ваши данные поступают из ObjectContext, как определено в вашей настройке.

Как ни странно, как отмечает mfanto, это тест интеграции, а не unit test, но как он сам говорит:

Это не похоже на единицу, но интеграционное тестирование для меня!

Вы правы, я использую термин "модульное тестирование" в названии из-за SEO причины:) Также большинство людей, похоже, не знают о различия между ними.

Я не знаю, является ли это лучшим способом протестировать сущность Entity Framework DAL; мне потребовалось некоторое время для достижения этого решения, и я считаю, что это не лишено заслуг, но я буду смотреть на этот вопрос, чтобы увидеть, какие другие решения предлагаются.