¿Debería cada método devolver un valor para Pruebas unitarias?

12

Estoy aprendiendo a crear pruebas de Unidad simples para eventualmente (y con suerte) comenzar a hacer solo TDD; por ahora estoy tratando de escribir pruebas para el código ya escrito para ver qué puede causar problemas. Este es uno de ellos.

Digamos que tengo esta clase simple (con Typecript-> Javascript):

class PrivateStuff {
    greeting: string;
    private _thisIsPrivate;

    constructor(isPrivate: boolean) {
        this._thisIsPrivate = isPrivate;
    }

    setPrivate(option) {
        this._thisIsPrivate = option;
        console.log("_thisIsPrivate changed to : " + option);
    }

    getPrivate() {
        console.log("_thisIsPrivate is : " + this._thisIsPrivate);
        return this._thisIsPrivate;        
    }
}

Y lo uso de esta manera:

let privateStuff = new PrivateStuff(false);

let buttonSet = document.createElement('button');
buttonSet.textContent = "Set True";
buttonSet.onclick = function () {
    privateStuff.setPrivate(true);
}

let buttonGet = document.createElement('button');
buttonGet.textContent = "Get";
buttonGet.onclick = function() {
    console.log(privateStuff.getPrivate());
}
document.body.appendChild(buttonSet);
document.body.appendChild(buttonGet);

setPrivate()no necesita devolver nada, pero por eso no puedo probarlo. Al crear una prueba unitaria, ¿debo refactorizar el código?

Si estuviera haciendo TDD, ¿debería crear siempre métodos que devuelvan algo solo para poder probarlo? ¿O me falta algo?

PD Puedes ver y ejecutar el código aquí

distante
fuente
44
Si establecesPrivate, ¿el valor de getPrivate no te dirá si funcionó?
JeffO
@JeffO sí, ahora veo que no solo tengo que usar el retorno de la función para verificar si funcionó.
distante
"setPrivate () no necesita devolver nada, pero por eso no puedo probarlo". ¿Induce algún efecto observable? Si es así, puedes probarlo. De lo contrario, no hay interés en probarlo (y no hay interés en implementarlo).
mgoeminne
@mgoeminne ¿Qué sucede si el método es nulo y escribe en la base de datos / registro o envía un mensaje a otro objeto como único efecto?
Andres F.
@AndresF. Puede leer desde la base de datos / registro o simular el otro objeto para confirmar que los datos fueron enviados. Puede que no sea una prueba de "unidad", pero eso no es lo que realmente importa.
Jacob Raihle

Respuestas:

21

Supongo que tu idea errónea aquí es que un "sujeto bajo prueba" debe ser siempre un método por sí solo. Pero eso no es cierto, aunque algunos métodos se pueden probar sin utilizar ningún otro método, el tamaño típico de un SUT es una clase o algunos métodos y funciones interactivos de una clase. Por lo tanto, si tiene un método que cambia el estado interno de un objeto, siempre debe haber algún cambio externo visible en el comportamiento de ese objeto (de lo contrario, no tendría sentido tener el método en primer lugar). Y en una prueba unitaria, puede validar exactamente este comportamiento.

Por ejemplo, supongamos que tiene una clase NumberFormattercon la responsabilidad de formatear números de punto flotante de una manera predefinida. Supongamos que contiene un método FormatToString(double d). Supongamos además que tiene un método setDecimalSeparator, pero no getDecimalSeparator. Sin embargo, puede escribir fácilmente una prueba si después de una llamada al setDecimalSeparatormétodo se FormatToStringcomporta de la manera deseada. Tal prueba podría verse así

  var nf = new NumberFormatter();
  nf.setDecimalSeparator(".");
  AssertEqual("12.34",nf.FormatToString(12.34))
  nf.setDecimalSeparator(",");
  AssertEqual("12,34",nf.FormatToString(12.34))

Entonces esta es una prueba significativa de setDecimalSeparatorun método sin valor de retorno.

Doc Brown
fuente
Ya veo, así que en mi ejemplo poco realista podría usar la función 'getPrivate ()' para ver si'_thisIsPrivate 'ha cambiado y pensar que' getPrivate () 'funciona, pero en un escenario de caso más real debería probar el objeto que se está cambiando . Gracias por tu clara explicación!
distante