Kpiłabym z bazy danych. Radzenie sobie z bazą danych w teście jest bolesne, ponieważ trzeba utworzyć bazę danych, utworzyć schemat, a następnie go usunąć, upewnić się, że nie ma żadnych połączeń, itp. Jest bolesne.
Inną rzeczą, która sprawia, że czuję się niekomfortowo, jest fakt, że weryfikacja logiki kodu jest "zbyt daleko" od kodu. Poszedłem na drogę umieszczania funkcji Sql (połączeń, komend itd.) Za klasą, którą można uzyskać i sprawdzałem, czy DAL wywołuje odpowiednie metody. Również testy przebiegają o wiele szybciej w ten sposób.
Oto kilka szybkich klas abstrakcji sql i przykład użycia + test jednostki.
public class SqlConnectionBase : IDisposable {
private readonly SqlConnection m_Connection;
public SqlConnectionBase(string connString) {
m_Connection = new SqlConnection(connString);
}
public virtual SqlConnection Object { get { return m_Connection; } }
public virtual void Open() {
m_Connection.Open();
}
public virtual void Close() {
m_Connection.Close();
}
#region IDisposable Members
public virtual void Dispose() {
m_Connection.Dispose();
}
#endregion
}
public class SqlCommandBase : IDisposable{
private readonly SqlCommand m_Command;
public SqlCommandBase() {
m_Command = new SqlCommand();
}
public SqlCommandBase(string cmdText, SqlConnectionBase connection) {
m_Command = new SqlCommand(cmdText, connection.Object);
}
public SqlCommandBase(SqlConnectionBase connection) {
m_Command = new SqlCommand();
m_Command.Connection = connection.Object;
}
public virtual int ExecuteNonQuery() { return m_Command.ExecuteNonQuery(); }
public virtual string CommandText { get { return m_Command.CommandText; } set { m_Command.CommandText = value; } }
public virtual void AddParameter(SqlParameter sqlParameter) {
m_Command.Parameters.Add(sqlParameter);
}
#region IDisposable Members
virtual public void Dispose() {
m_Command.Dispose();
}
#endregion
}
public class SqlFactory {
public virtual SqlCommandBase CreateCommand(string query, SqlConnectionBase conn) {
return new SqlCommandBase(query, conn);
}
public virtual SqlCommandBase CreateCommand(SqlConnectionBase conn) {
return new SqlCommandBase(conn);
}
public virtual SqlConnectionBase CreateConnection(string connString) {
return new SqlConnectionBase(connString);
}
}
public class DBUser {
public DBUser(SqlFactory factory) {
m_factory = factory; //dependency constructor, will be used during unit testing
}
public DBUser() {
m_factory = new SqlFactory(); //used during normal execution
}
public void DoSomething() {
var conn = m_factory.CreateConnection("server=servername,database=...");
var cmd = m_factory.CreateCommand(conn);
cmd.CommandText = "Select * from users";
cmd.ExecuteNonQuery();
}
[TestMethod]
public void DoSomethingTest() {
var factoryMock = new Mock<SqlFactory>();
var cmdMock = new Mock<CommandBase>();
factoryMock.Setup(f=>f.CreateConnection(It.IsAny<string>())).Returns(cmdMock.Object);
DBUser dbUser = new DBUser(factoryMock.Object);
dbUser.DoSomething();
//Verify that DoSomething is called.
cmdMock.Verify(c=>c.DoSomething());
}
}
Testy przebiegają o wiele szybciej w ten sposób: mogę sobie wyobrazić. Czy masz przykładowy kod tego, co opisałeś? – XIII
Mam bity i kawałki, poważnie rozważam napisanie biblioteki 'System.Sql.Abstractions', podobnej do [System.IO.Abstractions] (http://systemioabstractions.codeplex.com/) –