Способы модульного доступа к данным

Я пытаюсь найти эффективный способ модульного тестирования моего уровня доступа к данным в С#. Я являюсь основным разработчиком Java и использую только С# около 6 месяцев, в прошлом я использовал библиотеку DBUnit для тестирования с известной государственной базой данных. Мне не удалось найти подобную активную библиотеку, которая может быть использована, ближайшая, кажется, nDBUnit, но она неактивна уже некоторое время.

Кажется, что существует много противоречивых методов о том, как и почему в С#. В идеале я хочу протестировать уровень доступа к данным, используя насмешку без необходимости подключения к базе данных, а затем unit test процедуру хранения в отдельном наборе тестов.

В системе, над которой я работаю, уровень доступа к данным заключается в использовании ADO.net(без использования Entity Framework) для вызова процедур хранилища на SQL Server.

Ниже приведен пример кода, с которым мне нужно работать; чтобы спуститься с насмешливым путем, я должен был бы высмеять SqlCommand (используя IDbCommand) и/или высмеять SqlConnection.

Итак, мой вопрос - это лучший способ (если есть такая вещь) сделать это? До сих пор единственным способом было бы сделать объект Proxy, который передается в конструктор, чтобы он мог вернуть издеваемые объекты Sql * для тестирования.

У меня не было возможности посмотреть все доступные доступные библиотеки С#.

public class CustomerRepository : ICustomerRepository
{
   private string connectionString;

   public CustomerRepository (string connectionString)
   {
     this.connectionString = connectionString;
   }

   public int Create(Customer customer)
   {

     SqlParameter paramOutId = new SqlParameter("@out_id", SqlDbType.Int);
     paramOutId.Direction = ParameterDirection.Output;
     List<SqlParameter> sqlParams = new List<SqlParameter>()
     {
       paramOutId,
       new SqlParameter("@name", customer.Name)
     }

     SqlConnection connection = GetConnection();
     try
     {
       SqlCommand command = new SqlCommand("store_proc_name", connection);

       command.CommandType = CommandType.StoredProcedure;

       command.Parameters.AddRange(sqlParams.ToArray());

       int results = command.ExecuteNonQuery();

       return (int) paramOutId.Value;
     }
     finally
     {
       CloseConnection(connection);
     }

   }

}

Ответы

Ответ 1

К сожалению, вы не можете найти инструмент, который помещает вашу базу данных в известное состояние и позволяет запускать CustomerRepository против базы данных для проверки CustomerRepository. Тем не менее, ответ заключается не в том, чтобы начать использовать mocks для издевательства над всеми вызовами ADO. Поступая таким образом, вы создаете unit test, который действительно не тестирует какую-либо логику: он просто проверяет, что код написан так, как вы думаете, что он должен быть написан.

Скажем, что в итоге я ввел SQL INSERT в качестве моей команды для создания клиента в базе данных SQL. Теперь скажем, что мы вносим изменения, чтобы таблица клиентов имела разные поля (что нарушает нашу команду INSERT), и теперь мы должны использовать хранимую процедуру для создания клиента. Тест с mocks все равно пройдет, хотя реализация, которую он тестирует, теперь нарушена. Кроме того, если вы исправили реализацию для использования хранимых процедур, ваши модульные тесты теперь терпят неудачу. В чем смысл unit test, если он продолжает проходить, когда он должен потерпеть неудачу, но затем не удастся при исправлении системы?

См. этот вопрос для некоторых возможных альтернатив. Похоже, что отмеченный ответ состоит в том, чтобы на самом деле просто использовать DBUnit в С# с помощью IKVM.

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

Ответ 2

Это задание уровня - это подключение кода к базе данных. Он должен инкапсулировать знания о подключении и синтаксисе db. Обычно он отображает язык домена на язык базы данных. Я рассматриваю эту часть модульных тестов как интеграционный тест, и поэтому я проверяю, что схема базы данных эквивалентна реальной или тестовой базе данных. Подробнее о теме здесь.

Ответ 3

Я начинаю с получения IDbConnection, тогда вы можете в основном сделать остальную часть своего кода.

using(IDbConnection conn = factory.GetConnection(connectionstring))
{
    conn.Open();
    using(IDbTransaction trans = conn.BeginTransaction())
    {
        IDbCommand command = conn.CreateCommand();
        IDataParameter param = command.CreateParameter();
        // Do something with your command
        trans.Commit();
    }
}

Затем вы можете издеваться над factory, IDbConnection, IDBTransaction и любыми другими объектами ADO, которые вы создаете из них. Что касается библиотеки Mock, я использую Moq.

Ответ 4

Для тестирования уровня DataAccess вам потребуется более сложная структура.

DataAccess Layer вызовет ссылки из объектов репозитория. Объект Repo будет ссылаться на ссылки из Entity Framework DbSets через шаблон проектирования UnitOfWork.

Уровень данных (TOP)
|
UnitOfWork
|
Классы шаблонов хранилища
|
Контекст EF
|
Фактическая база данных

После того, как вы установите структуру, вы будете издеваться над классами репозитория. Например, элементы будут вставлены в БД, а затем отправятся в mock-объект. Позже вы будете утверждать против вашего макетного объекта, чтобы увидеть вставленный элемент или нет.

Пожалуйста, взгляните на Внедрение репозитория и шаблонов рабочих единиц