Как подделать SqlDataAdapter, SqlConnection и SqlCommand в модульных тестах - PullRequest
3 голосов
/ 13 марта 2011

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

public DataSet SelectData(string selectCommand)
    {
        if (!string.IsNullOrEmpty(selectCommand))
        {
            DataSet ds = new DataSet();
            ds.Locale = CultureInfo.InvariantCulture;
            SqlDataAdapter adapter = new SqlDataAdapter(selectCommand, Connection);
            adapter.Fill(ds);
            return ds;
        }
        throw new ArgumentException("SelectCommand was null or empty", "selectCommand");
    }

Обратите внимание, что параметр Connection в конструкторе SqlCommand является свойством, которое возвращает SqlConnection для этого экземпляра моего класса.Очевидно, мне нужно каким-то образом подделать SqlDataAdapter, но это также означает, что мне нужно подделать используемые SqlCommand и SqlConnection.Все классы запечатаны, поэтому я не могу просто создать поддельный объект, который наследуется от этих классов.Создание базы данных как бы отрицательно сказывается на внедрении зависимостей, поэтому я бы хотел этого избежать.У кого-нибудь есть предложения по тестированию этого метода?

Ответы [ 2 ]

8 голосов
/ 13 марта 2011

Как правило, для макетирования запечатанных классов (1) вам нужен макет, который может это сделать, или (2) вам нужно написать (незапечатанные) оболочки вокруг запечатанного класса и использовать / макетировать их. TypeMock может издеваться над закрытыми классами, но это стоит денег. Но, будьте осторожны, способность имитировать запечатанные классы и другие, как правило, немоделируемые элементы, может помешать вам реорганизовать ваш код для улучшения дизайна (если вы согласитесь, что тестируемый код - лучший код). Оболочки или адаптеры относительно просты в написании, но сами по себе они не тестируются по той же причине, по которой вы их пишете. Однако из-за их простоты часто можно утверждать, что они правильны при проверке.

Кроме того, вы можете рассмотреть более современные механизмы доступа к данным, например, с использованием объектно-реляционного преобразователя (ORM). Entity Framework, LINQ-to-SQL, nHibernate, Subsonic ... все это лучший выбор, чем написание собственного уровня доступа к данным на низком уровне, на мой взгляд.

5 голосов
/ 02 июля 2015

Я думаю, что у нас есть все интерфейсы (IDbConnection, IDbTransaction, IDbCommand и IDbDataAdapter), чтобы смоделировать все методы, которые мы использовали, используя NSubstitute и Dependency Injection.

//using init class to inject IDbDataAdapter
IDbDataAdapter adapter = init.DbAdapter("command");
adapter.Connection = connection;

Что касается SqlConnection и SqlCommand

using (IDbConnection connection = init.DbConnection("connection"))
{
  using (IDbTransaction transaction = connection.BeginTransaction())
  {
    using (IDbCommand command = init.DbCommand("sproc"))
    {  
       command.Transaction = transaction;
       command.Connection = connection;
       ...
    }
  }
}

Модульный тест будет выглядеть примерно так

//arrange
var connection = Substitute.For<IDbConnection>();
var command = Substitute.For<IDbCommand>();
var transaction = Substitute.For<IDbTransaction>();
//this is the init class used before
var init = Substitute.For<ISqlInitializer>();
connection.Open();
connection.BeginTransaction(Arg.Any<IsolationLevel>()).Returns(transaction);
init.DbConnection(Arg.Any<string>()).Returns(connection);
init.DbCommand(Arg.Any<string>()).Returns(command);
var client = new SqlClient(init);

//act
var result = await client.CommandMultipleAsync(new SqlConfiguration(FakeConnection, new List<string> { "testSproc1", "testSproc2" }));

//assert
Assert.AreEqual(0, result);
command.Received(2).ExecuteNonQuery();
transaction.Received(1).Commit();

Пожалуйста, ознакомьтесь с проверкой концепции проекта на GitHub для детального использования, а также с любыми предложениями или отзывами приветствуются: p https://github.com/TianyuanC/dals/blob/master/DALs.Sql/SqlClient.cs

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