Esto me llevó a las pruebas unitarias y me hizo muy feliz
Acabamos de comenzar a hacer pruebas unitarias. Durante mucho tiempo supe que sería bueno comenzar a hacerlo, pero no tenía idea de cómo comenzar y, lo que es más importante, qué probar.
Luego tuvimos que volver a escribir un código importante en nuestro programa de contabilidad. Esta parte fue muy compleja ya que involucraba muchos escenarios diferentes. La parte de la que estoy hablando es un método para pagar facturas de ventas y / o compras ya ingresadas en el sistema de contabilidad.
Simplemente no sabía cómo comenzar a codificarlo, ya que había muchas opciones de pago diferentes. Una factura podría ser de $ 100 pero el cliente solo transfirió $ 99. Tal vez ha enviado facturas de ventas a un cliente pero también ha comprado a ese cliente. Entonces lo vendiste por $ 300 pero lo compraste por $ 100. Puede esperar que su cliente le pague $ 200 para liquidar el saldo. ¿Y qué pasa si vendió por $ 500 pero el cliente le paga solo $ 250?
Así que tuve que resolver un problema muy complejo con muchas posibilidades de que un escenario funcionaría perfectamente pero estaría mal en otro tipo de combinación de factura / pago.
Aquí es donde las pruebas unitarias vinieron al rescate.
Comencé a escribir (dentro del código de prueba) un método para crear una lista de facturas, tanto para ventas como para compras. Luego escribí un segundo método para crear el pago real. Normalmente, un usuario ingresaría esa información a través de una interfaz de usuario.
Luego creé el primer TestMethod, probando un pago muy simple de una sola factura sin ningún descuento de pago. Toda la acción en el sistema ocurriría cuando un pago bancario se guardara en la base de datos. Como puede ver, creé una factura, creé un pago (una transacción bancaria) y guardé la transacción en el disco. En mis afirmaciones pongo lo que deberían ser los números correctos que terminan en la transacción del Banco y en la Factura vinculada. Verifico la cantidad de pagos, los montos de los pagos, el monto del descuento y el saldo de la factura después de la transacción.
Después de ejecutar la prueba, iría a la base de datos y verificaría si lo que esperaba estaba allí.
Después de escribir la prueba, comencé a codificar el método de pago (parte de la clase BankHeader). En la codificación solo me molesté con el código para pasar la primera prueba. Todavía no pensaba en los otros escenarios más complejos.
Ejecuté la primera prueba, arreglé un pequeño error hasta que mi prueba pasara.
Luego comencé a escribir la segunda prueba, esta vez trabajando con un descuento de pago. Después de escribir la prueba, modifiqué el método de pago para admitir descuentos.
Mientras probaba la corrección con un descuento de pago, también probé el pago simple. Ambas pruebas deberían pasar, por supuesto.
Luego me abrí camino hacia los escenarios más complejos.
1) Piensa en un nuevo escenario
2) Escribe una prueba para ese escenario
3) Ejecute esa prueba única para ver si pasaría
4) Si no fuera así, depuraría y modificaría el código hasta que pasara.
5) Mientras modificaba el código seguí ejecutando todas las pruebas
Así es como logré crear mi método de pago muy complejo. Sin pruebas unitarias no sabía cómo comenzar a codificar, el problema parecía abrumador. Con las pruebas, podría comenzar con un método simple y extenderlo paso a paso con la seguridad de que los escenarios más simples seguirían funcionando.
Estoy seguro de que el uso de las pruebas unitarias me ahorró unos días (o semanas) de codificación y garantiza más o menos la exactitud de mi método.
Si luego pienso en un nuevo escenario, puedo agregarlo a las pruebas para ver si funciona o no. Si no, puedo modificar el código pero aún así estar seguro de que los otros escenarios todavía funcionan correctamente. Esto ahorrará días y días en la fase de mantenimiento y corrección de errores.
Sí, incluso el código probado puede tener errores si un usuario hace cosas en las que no pensó o le impidió hacer
A continuación se detallan algunas de las pruebas que creé para probar mi método de pago.
public class TestPayments
{
InvoiceDiaryHeader invoiceHeader = null;
InvoiceDiaryDetail invoiceDetail = null;
BankCashDiaryHeader bankHeader = null;
BankCashDiaryDetail bankDetail = null;
public InvoiceDiaryHeader CreateSales(string amountIncVat, bool sales, int invoiceNumber, string date)
{
......
......
}
public BankCashDiaryHeader CreateMultiplePayments(IList<InvoiceDiaryHeader> invoices, int headerNumber, decimal amount, decimal discount)
{
......
......
......
}
[TestMethod]
public void TestSingleSalesPaymentNoDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 1, "01-09-2008"));
bankHeader = CreateMultiplePayments(list, 1, 119.00M, 0);
bankHeader.Save();
Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(119M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(0M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
}
[TestMethod]
public void TestSingleSalesPaymentDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 2, "01-09-2008"));
bankHeader = CreateMultiplePayments(list, 2, 118.00M, 1M);
bankHeader.Save();
Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(1, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(118M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(1M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
}
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void TestDuplicateInvoiceNumber()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("100", true, 2, "01-09-2008"));
list.Add(CreateSales("200", true, 2, "01-09-2008"));
bankHeader = CreateMultiplePayments(list, 3, 300, 0);
bankHeader.Save();
Assert.Fail("expected an ApplicationException");
}
[TestMethod]
public void TestMultipleSalesPaymentWithPaymentDiscount()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("119", true, 11, "01-09-2008"));
list.Add(CreateSales("400", true, 12, "02-09-2008"));
list.Add(CreateSales("600", true, 13, "03-09-2008"));
list.Add(CreateSales("25,40", true, 14, "04-09-2008"));
bankHeader = CreateMultiplePayments(list, 5, 1144.00M, 0.40M);
bankHeader.Save();
Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(4, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(118.60M, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(400, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
Assert.AreEqual(600, bankHeader.BankCashDetails[0].Payments[2].PaymentAmount);
Assert.AreEqual(25.40M, bankHeader.BankCashDetails[0].Payments[3].PaymentAmount);
Assert.AreEqual(0.40M, bankHeader.BankCashDetails[0].Payments[0].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].PaymentDiscount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[2].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
}
[TestMethod]
public void TestSettlement()
{
IList<InvoiceDiaryHeader> list = new List<InvoiceDiaryHeader>();
list.Add(CreateSales("300", true, 43, "01-09-2008")); //Sales
list.Add(CreateSales("100", false, 6453, "02-09-2008")); //Purchase
bankHeader = CreateMultiplePayments(list, 22, 200, 0);
bankHeader.Save();
Assert.AreEqual(1, bankHeader.BankCashDetails.Count);
Assert.AreEqual(2, bankHeader.BankCashDetails[0].Payments.Count);
Assert.AreEqual(300, bankHeader.BankCashDetails[0].Payments[0].PaymentAmount);
Assert.AreEqual(-100, bankHeader.BankCashDetails[0].Payments[1].PaymentAmount);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[0].InvoiceHeader.Balance);
Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[1].InvoiceHeader.Balance);
}