#c# #unit-testing #generics #inheritance #mocking
#c# #модульное тестирование #общие сведения #наследование #насмешливый
Вопрос:
У меня есть универсальный класс, SqlDataImporter
который считывает данные из источника данных SQL. Вот некоторые из его методов:
#region properties
protected IDbConnection DbConnection; // Maintain this connection
protected IDataReader DataReader; // Used to import data via the DbConnection
protected DataTable DataSegment;
#endregion
#region constructors
protected SqlDataImporter(IDbConnection importConnection, string importQuery)
{
ValidateConstructorArguments(importConnection, importQuery); // throws exception if invalid
this.DbConnection = importConnection;
InitialiseDataReader(importQuery);
}
#endregion
#region methods
protected void ValidateConstructorArguments(IDbConnection importConnection, string importQuery)
{
if (String.IsNullOrEmpty(importQuery))
{
string msg = "Provided importQuery has null or empty value. A query must be provided for importing data.";
throw new ArgumentException(msg);
}
}
private void InitialiseDataReader(string importQuery)
{
if (this.DbConnection.State != ConnectionState.Open)
this.DbConnection.Open();
using (IDbCommand command = this.DbConnection.CreateCommand())
{
command.CommandTimeout = 1200;
command.CommandText = importQuery;
this.DataReader = command.ExecuteReader();
}
if (this.DataReader == null) // only reason this could happen is source doesn't exist?
throw new Exception("Source does not exist: " importQuery);
}
Идея заключается в том, что этот класс является достаточно общим для чтения как из источника данных Postgresql (с использованием переданного OdbcConnection), так и из источника данных Sql Server (с использованием переданного SqlConnection). Тип переданного соединения определяет тип IDataReader
созданного из-за содержимого InitialiseDataReader()
метода.
Затем я создал 2 дочерних класса PostgreSqlImporter
, SqlServerImporter
которые будут использоваться при чтении из разных источников. Они оба наследуются от этого родительского класса. На самом деле вся работа выполняется в родительском SqlDataImporter
классе, чтобы уменьшить дублирование.
Все, что делают дочерние классы, это вызывают этот метод ValidateConstructorArguments() и проверяют, что переданные соединения имеют ожидаемый тип (OdbcConnection или SqlConnection в зависимости от класса). Например:
public SqlServerImporter(IDbConnection importConnection, string importQuery) : base(importConnection, importQuery)
{
ValidateConstructorArguments(importConnection);
}
protected void ValidateConstructorArguments(IDbConnection importConnection)
{
if (importConnection.GetType() != typeof(SqlConnection))
{
string msg = "Provided importConnection must be of type SqlConnection. Actual type is " importConnection.GetType();
throw new ArgumentException(msg);
}
}
Первоначально я сделал параметр подключения универсальным типом IDBConnection
, потому что хотел иметь возможность передавать макет объекта в качестве параметра из моих модульных тестов. Но теперь я добавил этот код, чтобы проверить тип переданного соединения ( ValidateConstructorArguments(IDbConnection importConnection)
) Я получаю свое собственное исключение ArgumentException, когда я передаю издевательский объект: System.ArgumentException: Provided importConnection must be of type SqlConnection. Actual type is Castle.Proxies.IDbConnectionProxy
Теперь я заблудился. Я все еще хочу иметь возможность передавать макетные объекты для тестирования, но не могу проверить тип времени выполнения соединения, если захочу это сделать. Если я откажусь от проверки типа соединения, то мне интересно, есть ли смысл иметь эти два дочерних класса (PostgreSqlImport и SqlServerImporter).
- Должен ли я отказаться от двух дочерних классов и проверки типа соединения во время выполнения? Не кажется надежным делать это…
- Должен ли я добавить код к
ValidateConstructorArguments
методу, чтобы игнорировать тип, если это фиктивный тип? - Является ли мой дизайн ошибочным?
Комментарии:
1. Какой тип драйвера вы планируете использовать? ODBC и OleDb — это разные общие спецификации для интерфейса с базами данных. Я не уверен, какой тип использует Net SqlClient. Для SqlClient у вас может быть несколько различных типов драйверов (см. : connectionstrings.com/sql-server ). Я думаю, вам нужно определить драйвер, который будет возвращать тип.
2. @jdweng немного выходит за рамки моих знаний, но я планирую использовать ODBC полностью, потому что он уже использовался ранее в кодовой базе. Я не уверен, как будет выглядеть определение драйвера, который возвращает тип. Имейте в виду, что планируется, чтобы этот код существовал в задаче сценария SSIS с ограниченными библиотеками и зависимостями.