¿Cómo sabes qué probar al escribir pruebas unitarias? [cerrado]

127

Usando C #, necesito una clase llamada Userque tenga un nombre de usuario, contraseña, bandera activa, nombre, apellido, nombre completo, etc.

Debe haber métodos para autenticar y guardar un usuario. ¿Acabo de escribir una prueba para los métodos? ¿Y debo preocuparme por probar las propiedades ya que son getter y setters de .Net?

nbro
fuente
Esta publicación ayudará a reducir la pregunta más amplia: earnestengineer.blogspot.com/2018/03/… Puede tomar estas pautas para enfocar su pregunta
Lindsay Morsillo
Tenga en cuenta que las contraseñas no deben almacenarse como texto sin formato.
Sr. Anderson

Respuestas:

131

Muchas buenas respuestas a esto también están en mi pregunta: " Principio de TDD - ¿Desafíos? ¿Soluciones? ¿Recomendaciones? "

¿Puedo recomendar también echar un vistazo a mi publicación de blog (que fue inspirada en parte por mi pregunta), tengo algunos buenos comentarios al respecto. A saber:

No sé por dónde empezar

  • Comenzar de nuevo. Solo piense en escribir pruebas cuando escriba un código nuevo. Esto puede ser una reelaboración del código antiguo o una característica completamente nueva.
  • Comience simple. No salgas corriendo e intentes entender un marco de prueba, además de ser un TDD-esque. Debug.Assert funciona bien. Úselo como punto de partida. No se mete con su proyecto ni crea dependencias.
  • Comienza positivo. Estás tratando de mejorar tu oficio, siéntete bien al respecto. He visto muchos desarrolladores que están felices de estancarse y no probar cosas nuevas para mejorar. Estás haciendo lo correcto, recuerda esto y te ayudará a evitar que te rindas.
  • Comience listo para un desafío. Es bastante difícil comenzar a realizar pruebas. Espere un desafío, pero recuerde: los desafíos se pueden superar.

Solo prueba lo que esperas

Tuve problemas reales cuando comencé porque estaba constantemente sentado allí tratando de descubrir cada posible problema que podría ocurrir y luego tratando de probarlo y solucionarlo. Esta es una forma rápida de dolor de cabeza. Las pruebas deberían ser un proceso real de YAGNI. Si sabe que hay un problema, escriba una prueba para solucionarlo. De lo contrario, no te molestes.

Solo prueba una cosa

Cada caso de prueba solo debe probar una cosa. Si alguna vez te encuentras poniendo "y" en el nombre del caso de prueba, estás haciendo algo mal.

Espero que esto signifique que podamos pasar de "captadores y setters" :)

Rob Cooper
fuente
2
"Si sabe que hay un problema, escriba una prueba para solucionarlo. De lo contrario, no se moleste". No estaría de acuerdo con la forma en que está redactado. Tenía la impresión de que las pruebas unitarias deberían cubrir todas las rutas de ejecución posibles.
Corin Blaikie
3
Mientras que algunos pueden abogar por tales cosas, yo personalmente no. Un buen 90% de mi dolor de cabeza vino simplemente de tratar de hacer "todo". Le digo que pruebe lo que espera que suceda (para que sepa que está recuperando los valores correctos) pero no intente resolverlo todo. YAGNI
Rob Cooper
44
Yo también defiendo el enfoque de "prueba tus errores". Si todos tuviéramos tiempo y paciencia infinitos, probaríamos todas las rutas de ejecución posibles. Pero no lo hacemos, por lo que debe dedicar su esfuerzo a donde tendrá el mayor efecto.
Schwern
63

Prueba tu código, no el idioma.

Una prueba unitaria como:

Integer i = new Integer(7);
assert (i.instanceOf(integer));

solo es útil si está escribiendo un compilador y existe una probabilidad distinta de cero de que su instanceofmétodo no funcione.

No pruebe cosas en las que pueda confiar en el idioma para hacer cumplir. En su caso, me enfocaría en sus métodos de autenticación y guardado, y escribiría pruebas que se aseguraran de que pudieran manejar los valores nulos en cualquiera o todos esos campos con gracia.

Tim Howland
fuente
1
Buen punto sobre "No pruebes el framework" - Algo que también entendí cuando era nuevo en esto. + 1'ed :)
Rob Cooper
38

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);
    }
eroijen
fuente
1
¡Encontró un error en su prueba de unidad! Repite esta línea en lugar de incluir un 2 en uno de ellos:Assert.AreEqual(0, bankHeader.BankCashDetails[0].Payments[3].InvoiceHeader.Balance);
Ryan Peschel
2
Usted dice: "Después de ejecutar la prueba, iría a la base de datos y verificaría si lo que esperaba estaba allí". Este es un buen ejemplo de una prueba de integración entre los componentes de su sistema, no una prueba unitaria aislada de una sola pieza de código.
JTech
2
También infringió la regla de más de un Afirmación por prueba.
Steve
13

Si realmente son triviales, entonces no te molestes en probar. Por ejemplo, si se implementan así;

public class User
{
    public string Username { get; set; }
    public string Password { get; set; }
}

Si, por otro lado, está haciendo algo inteligente (como cifrar y descifrar la contraseña en el captador / configurador), entonces pruébelo.

Steve Cooper
fuente
10

La regla es que tienes que probar cada pieza de lógica que escribes. Si implementó alguna funcionalidad específica en los captadores y definidores, creo que vale la pena probarlos. Si solo asignan valores a algunos campos privados, no se moleste.

Slavo
fuente
6

Esta pregunta parece ser una pregunta de dónde se dibuja la línea sobre qué métodos se prueban y cuáles no.

Los establecedores y captadores para la asignación de valor se han creado teniendo en cuenta la coherencia y el crecimiento futuro, y previendo que en algún momento el configurador / captador puede evolucionar hacia operaciones más complejas. Tendría sentido poner en práctica pruebas unitarias de esos métodos, también en aras de la coherencia y el crecimiento futuro.

El objetivo principal es la confiabilidad del código, especialmente durante los cambios para agregar funcionalidad adicional. No estoy al tanto de que alguien haya sido despedido por incluir setters / getters en la metodología de prueba, pero estoy seguro de que existen personas que desearon haber probado métodos que lo último que sabían o pueden recordar eran simples set / get wrappers, pero eso no fue así. Ya no es el caso.

Quizás otro miembro del equipo amplió los métodos set / get para incluir la lógica que ahora necesita ser probada pero que luego no creó las pruebas. Pero ahora su código está llamando a estos métodos y no sabe que han cambiado y necesitan pruebas exhaustivas, y las pruebas que realiza en desarrollo y control de calidad no desencadenan el defecto, pero los datos comerciales reales el primer día del lanzamiento sí desencadenarlo.

Los dos compañeros de equipo ahora debatirán sobre quién dejó caer la pelota y no pudo realizar pruebas unitarias cuando el conjunto / se transforma para incluir una lógica que puede fallar pero no está cubierta por una prueba unitaria. El compañero de equipo que originalmente escribió el set / gets tendrá más facilidad para salir de esta limpieza si las pruebas se implementaron desde el primer día en el set / gets simple.

Mi opinión es que unos minutos de tiempo "perdido" que cubre TODOS los métodos con pruebas unitarias, incluso los triviales, pueden ahorrar días de dolor de cabeza en el futuro y la pérdida de dinero / reputación del negocio y la pérdida del trabajo de alguien.

Y el hecho de que usted envolvió métodos triviales con pruebas unitarias podría ser visto por ese compañero de equipo junior cuando cambian los métodos triviales por otros no triviales y les pide que actualicen la prueba, y ahora nadie está en problemas porque el defecto estaba contenido de alcanzar la producción.

La forma en que codificamos y la disciplina que se puede ver en nuestro código pueden ayudar a otros.

Thomas Carlisle
fuente
4

Otra respuesta canónica. Esto, creo, de Ron Jeffries:

Solo pruebe el código que desea trabajar.

Liam
fuente
3

Probar el código repetitivo es una pérdida de tiempo, pero como dice Slavo, si agrega un efecto secundario a sus captadores / establecedores, entonces debe escribir una prueba para acompañar esa funcionalidad.

Si está realizando un desarrollo basado en pruebas, primero debe escribir el contrato (por ejemplo, la interfaz) y luego escribir las pruebas para ejercer esa interfaz que documenta los resultados / comportamiento esperados. Luego escriba sus propios métodos, sin tocar el código en sus pruebas unitarias. Finalmente, tome una herramienta de cobertura de código y asegúrese de que sus pruebas ejerciten todas las rutas lógicas en su código.

warren_s
fuente
3

Los códigos realmente triviales como getters y setters que no tienen un comportamiento adicional que establecer un campo privado son excesivos para probar. En 3.0 C # incluso tiene algo de azúcar sintáctica donde el compilador se encarga del campo privado para que no tenga que programar eso.

Normalmente escribo muchas pruebas muy simples para verificar el comportamiento que espero de mis clases. Incluso si es algo simple como sumar dos números. Cambio mucho entre escribir una prueba simple y escribir algunas líneas de código. La razón de esto es que luego puedo cambiar el código sin tener miedo de romper cosas en las que no pensé.

Mendelt
fuente
Me alegra que hayas hecho un buen punto del principio KISS. A menudo tengo pruebas que son literalmente como 2-3 líneas de código, pruebas realmente pequeñas y simples. Fácil de asimilar y difícil de romper :) + 1'ed
Rob Cooper
3

Deberías probar todo. En este momento tienes captadores y establecedores, pero algún día podrías cambiarlos un poco, tal vez para hacer la validación o algo más. Las pruebas que escriba hoy se usarán mañana para asegurarse de que todo siga funcionando como de costumbre. Cuando escriba prueba, debe olvidar consideraciones como "ahora es trivial". En un contexto ágil o basado en pruebas, debe probar asumiendo una futura refactorización. Además, ¿trataste de poner valores realmente extraños como cadenas extremadamente largas u otro contenido "malo"? Bueno, deberías ... nunca asumir lo mal que se puede abusar de tu código en el futuro.

En general, encuentro que escribir extensas pruebas de usuario es, por un lado, agotador. Por otro lado, aunque siempre le brinda información invaluable sobre cómo debería funcionar su aplicación y lo ayuda a descartar suposiciones fáciles (y falsas) (como: el nombre de usuario siempre tendrá menos de 1000 caracteres de longitud).

Sklivvz
fuente
3

Para los módulos simples que pueden terminar en un kit de herramientas, o en un tipo de proyecto de código abierto, debe probar tanto como sea posible, incluidos los captadores y establecedores triviales. Lo que debe tener en cuenta es que generar una prueba unitaria a medida que escribe un módulo en particular es bastante simple y directo. Agregar getters y setters es un código mínimo y se puede manejar sin pensarlo mucho. Sin embargo, una vez que su código se coloca en un sistema más grande, este esfuerzo adicional puede protegerlo contra cambios en el sistema subyacente, como los cambios de tipo en una clase base. Probar todo es la mejor manera de tener una regresión completa.

Dirigible
fuente
2

No está de más escribir pruebas unitarias para sus captadores y establecedores. En este momento, es posible que solo estén haciendo get / sets de campo bajo el capó, pero en el futuro es posible que tenga una lógica de validación o dependencias entre propiedades que deben probarse. Es más fácil escribirlo ahora mientras lo piensas y luego recordar actualizarlo si llega ese momento.

Bob King
fuente
bueno, si sus captadores / establecedores necesitan pruebas unitarias, debe haber alguna lógica asociada con ellos, por lo que significa que la lógica debe escribirse dentro de ellos, si no tienen ninguna lógica, no es necesario escribir pruebas unitarias.
Pop Catalin el
2
Su punto es que la lógica se les puede agregar más tarde.
LegendLength
2

en general, cuando un método solo se define para ciertos valores, pruebe los valores dentro y fuera del límite de lo que es aceptable. En otras palabras, asegúrese de que su método haga lo que se supone que debe hacer, pero nada más . Esto es importante, porque cuando vas a fallar, quieres fallar temprano.

En las jerarquías de herencia, asegúrese de probar el cumplimiento de LSP .

Probar getters y setters predeterminados no me parece muy útil, a menos que esté planeando hacer alguna validación más adelante.

Rik
fuente
2

bueno, si crees que puede romperse, escribe una prueba para ello. Por lo general, no pruebo setter / getter, pero digamos que haces uno para User.Name, que concatenan el nombre y el apellido, escribiría una prueba para que si alguien cambia el orden de apellido y nombre, al menos lo sabría él cambió algo que fue probado.

pmlarocque
fuente
2

La respuesta canónica es "prueba cualquier cosa que pueda romperse". Si está seguro de que las propiedades no se romperán, no las pruebe.

Y una vez que se descubre que algo se ha roto (encuentra un error), obviamente significa que necesita probarlo. Escriba una prueba para reproducir el error, vea cómo falla, luego corrija el error, luego vea la prueba pasar.

Eric Normand
fuente
1

Según entiendo las pruebas unitarias en el contexto del desarrollo ágil, Mike, sí, debe probar los captadores y establecedores (suponiendo que sean públicamente visibles). Todo el concepto de pruebas unitarias es probar la unidad de software, que es una clase en este caso, como una caja negra . Dado que los captadores y establecedores son visibles desde el exterior, debe probarlos junto con Autenticar y Guardar.

Onorio Catenacci
fuente
1

Si los métodos Autenticar y Guardar utilizan las propiedades, sus pruebas tocarán indirectamente las propiedades. Siempre y cuando las propiedades solo proporcionen acceso a los datos, entonces no deberían ser necesarias pruebas explícitas (a menos que esté buscando una cobertura del 100%).

Tom Walker
fuente
1

Probaría tus captadores y setters. Dependiendo de quién está escribiendo el código, algunas personas cambian el significado de los métodos getter / setter. He visto la inicialización de variables y otras validaciones como parte de los métodos getter. Para probar este tipo de cosas, querrás pruebas unitarias que cubran ese código explícitamente.

Peter Bernier
fuente
1

Personalmente, "probaría cualquier cosa que pueda romperse" y el getter simple (o incluso las mejores propiedades automáticas) no se romperán. Nunca me ha fallado una simple declaración de retorno y, por lo tanto, nunca he tenido una prueba para ellos. Si los captadores tienen cálculos dentro de ellos o alguna otra forma de declaraciones, ciertamente agregaría pruebas para ellos.

Personalmente, uso Moq como un marco de objeto simulado y luego verifico que mi objeto llama a los objetos circundantes como debería.

tronda
fuente
1

Debe cubrir la ejecución de cada método de la clase con UT y verificar el valor de retorno del método. Esto incluye getters y setters, especialmente en el caso de que los miembros (propiedades) sean clases complejas, lo que requiere una gran asignación de memoria durante su inicialización. Llame al setter con una cadena muy grande, por ejemplo (o algo con símbolos griegos) y verifique que el resultado sea correcto (no truncado, la codificación es buena, etc.)

En el caso de enteros simples que también se aplican, ¿qué sucede si pasa mucho tiempo en lugar de un entero? Esa es la razón por la que escribes UT para :)

m_pGladiator
fuente
1

No probaría la configuración real de las propiedades. Me preocuparía más cómo esas propiedades son pobladas por el consumidor y con qué las pueblan. Con cualquier prueba, debe sopesar los riesgos con el tiempo / costo de la prueba.

Sabueso
fuente
1

Debe probar "cada bloque de código no trivial" utilizando pruebas unitarias en la medida de lo posible.

Si sus propiedades son triviales y es poco probable que alguien introduzca un error en ellas, entonces debería ser seguro no probarlas en la unidad.

Sus métodos Authenticate () y Save () parecen buenos candidatos para las pruebas.

usuario7015
fuente
1

Idealmente, habrías hecho tus pruebas unitarias mientras escribías la clase. Así es como debes hacerlo cuando utilizas Test Driven Development. Agrega las pruebas a medida que implementa cada punto de función, asegurándose de cubrir también los casos límite con la prueba.

Escribir las pruebas después es mucho más doloroso, pero factible.

Esto es lo que haría en tu posición:

  1. Escriba un conjunto básico de pruebas que prueben la función central.
  2. Obtenga NCover y ejecútelo en sus pruebas. Su cobertura de prueba probablemente será de alrededor del 50% en este punto.
  3. Siga agregando pruebas que cubran sus casos límite hasta que obtenga una cobertura de alrededor del 80% -90%

Esto debería darle un buen conjunto de pruebas unitarias que funcionarán como un buen amortiguador contra las regresiones.

El único problema con este enfoque es que el código debe diseñarse para ser comprobable de esta manera. Si cometió algún error de acoplamiento desde el principio, no podrá obtener una alta cobertura muy fácilmente.

Es por eso que es realmente importante escribir las pruebas antes de escribir el código. Te obliga a escribir código que está débilmente acoplado.

Simon Johnson
fuente
1

No pruebe el código que funciona obviamente (repetitivo). Por lo tanto, si sus establecedores y captadores son simplemente "propertyvalue = value" y "return propertyvalue", no tiene sentido probarlo.

erlando
fuente
1

Incluso get / set puede tener consecuencias extrañas, dependiendo de cómo se hayan implementado, por lo que deben tratarse como métodos.

Cada prueba de estos necesitará especificar conjuntos de parámetros para las propiedades, definiendo propiedades aceptables e inaceptables para asegurar que las llamadas regresen / fallen de la manera esperada.

También debe conocer las trampas de seguridad, como un ejemplo de inyección SQL, y probarlas.

Entonces sí, debe preocuparse por probar las propiedades.

CestLaGalere
fuente
1

Creo que es una tontería probar getters y setters cuando solo hacen una operación simple. Personalmente, no escribo pruebas unitarias complejas para cubrir ningún patrón de uso. Intento escribir suficientes pruebas para asegurarme de que he manejado el comportamiento de ejecución normal y la mayor cantidad de casos de error que se me ocurren. Escribiré más pruebas unitarias como respuesta a los informes de errores. Utilizo la prueba unitaria para asegurar que el código cumpla con los requisitos y para facilitar futuras modificaciones. Me siento mucho más dispuesto a cambiar el código cuando sé que si rompo algo, una prueba fallará.

Andrei Savu
fuente
1

Escribiría una prueba para cualquier cosa para la que esté escribiendo código que sea comprobable fuera de la interfaz GUI.

Por lo general, cualquier lógica que escriba que tenga alguna lógica de negocios la coloco dentro de otro nivel o capa de lógica de negocios.

Luego, escribir pruebas para cualquier cosa que haga algo es fácil de hacer.

Primer paso, escriba una prueba de unidad para cada método público en su "Capa de lógica de negocios".

Si tuviera una clase como esta:

   public class AccountService
    {
        public void DebitAccount(int accountNumber, double amount)
        {

        }

        public void CreditAccount(int accountNumber, double amount)
        {

        }

        public void CloseAccount(int accountNumber)
        {

        }
    }

Lo primero que haría antes de escribir cualquier código sabiendo que tenía que realizar estas acciones sería comenzar a escribir pruebas unitarias.

   [TestFixture]
    public class AccountServiceTests
    {
        [Test]
        public void DebitAccountTest()
        {

        }

        [Test]
        public void CreditAccountTest()
        {

        }

        [Test]
        public void CloseAccountTest()
        {

        }
    }

Escriba sus pruebas para validar el código que ha escrito para hacer algo. Si itera sobre una colección de cosas y cambia algo sobre cada una de ellas, escriba una prueba que haga lo mismo y afirme que realmente sucedió.

Hay muchos otros enfoques que puede tomar, a saber, Behavoir Driven Development (BDD), que es más complicado y no es un gran lugar para comenzar con sus habilidades de prueba de unidad.

Por lo tanto, la moraleja de la historia es: pruebe cualquier cosa que haga cualquier cosa que le preocupe, mantenga las pruebas unitarias probando cosas específicas de tamaño pequeño, muchas pruebas son buenas.

Mantenga su lógica empresarial fuera de la capa de interfaz de usuario para que pueda escribir fácilmente pruebas para ellos, y estará bien.

Recomiendo TestDriven.Net o ReSharper, ya que ambos se integran fácilmente en Visual Studio.

Code Monkey
fuente
1

Recomendaría escribir múltiples pruebas para sus métodos de autenticación y guardado. Además del caso de éxito (donde se proporcionan todos los parámetros, todo está escrito correctamente, etc.), es bueno tener pruebas para varios casos de falla (parámetros incorrectos o faltantes, conexiones de base de datos no disponibles si corresponde, etc.). Recomiendo pruebas de unidad pragmáticas en C # con NUnit como referencia.

Como han dicho otros, las pruebas unitarias para captadores y establecedores son excesivas, a menos que haya lógica condicional en sus captadores y establecedores.

Scott Lawrence
fuente
1

Si bien es posible adivinar correctamente dónde debe probarse su código, generalmente creo que necesita métricas para respaldar esta suposición. Las pruebas unitarias, en mi opinión, van de la mano con las métricas de cobertura de código.

Código con muchas pruebas pero una pequeña cobertura no ha sido bien probada. Dicho esto, el código con una cobertura del 100% pero que no prueba los casos de errores y límites tampoco es excelente.

Desea un equilibrio entre una alta cobertura (90% mínimo) y datos de entrada variables.

¡Recuerde hacer una prueba de "basura adentro"!

Además, una prueba unitaria no es una prueba unitaria a menos que verifique si hay una falla. ¡Las pruebas unitarias que no tienen afirmaciones o están marcadas con excepciones conocidas simplemente probarán que el código no muere cuando se ejecuta!

¡Necesita diseñar sus pruebas para que siempre reporten fallas o datos inesperados / no deseados!

Ray Hayes
fuente
1

Mejora nuestro código ... ¡punto!

Una cosa que los desarrolladores de software olvidamos cuando hacemos un desarrollo basado en pruebas es el propósito de nuestras acciones. Si se escribe una prueba unitaria después de que el código de producción ya está en su lugar, el valor de la prueba disminuye (pero no se pierde por completo).

En el verdadero espíritu de las pruebas unitarias, estas pruebas no están principalmente allí para "probar" más de nuestro código; o para obtener una cobertura de código 90% -100% mejor. Estos son todos los beneficios adicionales de escribir las pruebas primero. La gran recompensa es que nuestro código de producción final se escribirá mucho mejor debido al proceso natural de TDD.

Para ayudar a comunicar mejor esta idea, lo siguiente puede ser útil en la lectura:

La teoría defectuosa de las pruebas unitarias
Desarrollo de software con propósito

Si creemos que el acto de escribir más pruebas unitarias es lo que nos ayuda a obtener un producto de mayor calidad, entonces podemos estar sufriendo de un Cult Cargo de Desarrollo Impulsado por Pruebas.

Scott Saad
fuente
No estoy de acuerdo con la afirmación de que las pruebas unitarias no tienen valor después de que el código de producción ya está en su lugar. Tales afirmaciones no tienen en cuenta su utilidad para replicar las condiciones de error encontradas en la producción, o en la comprensión del código heredado de un desarrollador o equipo anterior.
Scott Lawrence
Puede que me haya encontrado incorrectamente. No quise decir que las pruebas unitarias no tienen valor después de que el código de producción esté en su lugar. Sin embargo, su valor baja. El mayor beneficio para las pruebas unitarias proviene de la magia inherente que ocurre cuando les permitimos impulsar nuestro desarrollo de producción.
Scott Saad