Тестирование базы данных вызывает С#
Во-первых, дайте адресную терминологию здесь. Все, что я ищу, говорит: "Модульные тесты не касаются базы данных!" Я не хочу 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; мне потребовалось некоторое время для достижения этого решения, и я считаю, что это не лишено заслуг, но я буду смотреть на этот вопрос, чтобы увидеть, какие другие решения предлагаются.