2010-07-28 15 views
7

He estado leyendo en la unidad probando la capa de acceso a datos de un proyecto. La mayoría de las opciones se reducen a:Unidad probando una capa de acceso a datos

  • Usar una base de datos de prueba dedicado, pero la limpieza en la fase de finalización de todas las pruebas unitarias (o lo hacen de forma manual)
  • Uso de la base de datos pero no se comprometen o simplemente hacer retroceder
  • Simulacros de la base de datos

En un proyecto anterior solíamos usar la forma de deshacer, pero me gustaría saber más sobre las otras opciones y cómo se realizan mejor. Si tiene muestras/artículos/videos/... por favor compártalos.

Respuesta

3

Necesita tener 2 tipos de pruebas con un proyecto. pruebas unitarias y pruebas de integración

Las pruebas unitarias prueban un aspecto de su proyecto sin las dependencias de acceso y presentación de datos. Para las pruebas unitarias, se burlaría de su base de datos y de la inyección de dependencia del usuario para desacoplar su código del proveedor de datos. Esto lleva a una mejor arquitectura y le permite conectar un proveedor de datos diferente si lo desea. p.ej. pasar de ADO.net a nHibernate. Pruebas de integración son pruebas de integridad. Debe tratar de automatizar la creación y la población de su base de datos para que pueda volver rápida y fácilmente a una buena copia de la base de datos. Las herramientas como nant y DBFit lo ayudarán a guiar la creación de sus bases de datos.

No utilizaría una base de datos central para probar, ya que otros desarrolladores pueden estar probando al mismo tiempo y es posible que no esté en buen estado y que pueda obtener falsos positivos y gastar años intentando depurar un problema que no es un problema

2

Prefiero usar una base de datos de prueba en lugar de no cometer idea.

Mi base de datos de desarrollo tiene registros ficticios o un muestreo completamente desinfectado de los datos de producción.

Mi base de datos de pruebas de integración es una copia de la base de datos de producción real (esta versión es la que se usa para probar justo antes de transferir los cambios en vivo).

0

Me burlaría de la base de datos. Tratar con la base de datos en una prueba es doloroso ya que es necesario crear la base de datos, crear un esquema, luego soltarlo, asegurarse de que no haya conexiones, etc. es doloroso.

Otra cosa que me incomoda es el hecho de que la verificación de la lógica del código está "demasiado lejos" del código. Me gustaría ir por el camino de poner funciones Sql (conexiones, comandos, etc.) detrás de una clase ficticia y verificar que DAL llame a los métodos correctos. Además, las pruebas se ejecutan mucho más rápido de esta manera.

Aquí hay algunas clases rápidas de abstracción sql y ejemplo de uso + prueba de unidad.

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()); 
    } 
} 
+0

Las pruebas se ejecutan mucho más rápido de esta manera: me lo imagino. ¿Tiene algún código de muestra de lo que describió? – XIII

+0

tengo bits y piezas, estoy considerando seriamente escribir la biblioteca 'System.Sql.Abstractions' sin embargo, similar a [System.IO.Abstractions] (http://systemioabstractions.codeplex.com/) –

1

La principal responsabilidad de DAL es persistir/obtener datos de la base de datos, por lo que el sistema bajo prueba es DAL + Base de datos. No hay ninguna razón para escribir pruebas en DAL utilizando la simulación de la base de datos, ¿a quién le importa realmente qué consulta sql se ejecutó para recuperar una entidad en particular?Es necesario verificar si se seleccionó la entidad correcta y todos los atributos se mapearon correctamente.

Para hacer esto, suelo limpiar la base de datos, llenar la base de datos con los datos de prueba y buscarlos con los métodos DAL.

 [SetUp] 
    public void SetUp() 
    { 
     Database.Clear(); 
     manager = new ServiceManager(); 
    } 

    [TearDown] 
    public void TearDown() 
    { 
     manager.Dispose(); 
    } 

    [Test] 
    public void InsertAndLoadOrderContract() 
    { 
     MinOrderModel model = new OrderBuilder().InsertMinimalOrder(manager); 

     Contract contract = TestObjectGenerator.GenerateEntity<Contract>(); 
     manager.Contract.InsertOrderContract(model.Order.OrderCompositeId, contract); 

     Contract selectedContract = manager.Contract.SelectById(contract.ContractId); 

     AssertContract(selectedContract, contract); 
    } 

    private static void AssertContract(IContract actual, IContract expected) 
    { 
     Assert.That(actual.AgencyCodeOther, Is.EqualTo(expected.AgencyCodeOther)); 
     Assert.That(actual.AgencyFK, Is.EqualTo(expected.AgencyFK)); 
     Assert.That(actual.ContractId, Is.EqualTo(expected.ContractId)); 
     Assert.That(actual.Ident, Is.EqualTo(expected.Ident)); 
    } 

Algunas partes de esta prueba pueden ser reemplazados en los más convenientes:

  1. Es posible utilizar DbUnit para llenar hasta la base de datos con los datos
  2. no limpiar la base de datos, pero rollo transacción posterior en "TearDown" método
  3. Utilice el motor de base de datos más conveniente para las pruebas (SQLLite, por ejemplo)
Cuestiones relacionadas