¿Cómo se inyectan objetos de prueba cuando los objetos reales se crean dinámicamente?

8

Quiero hacer una clase comprobable usando inyección de dependencia. Pero la clase crea múltiples objetos en tiempo de ejecución y pasa diferentes valores a su constructor. Aquí hay un ejemplo simplificado:

public abstract class Validator {
    private ErrorList errors;

    public abstract void validate();

    public void addError(String text) {
        errors.add(
            new ValidationError(text));
    }

    public int getNumErrors() {
        return errors.count()
    }
}

public class AgeValidator extends Validator {
    public void validate() {
        addError("first name invalid");
        addError("last name invalid");
    }
}

(Hay muchas otras subclases de Validator).

¿Cuál es la mejor manera de cambiar esto, para poder inyectar un objeto falso en lugar de ValidationError?

Puedo crear un AbstractValidationErrorFactory e inyectar la fábrica en su lugar. Esto funcionaría, pero parece que terminaré creando toneladas de pequeñas fábricas e interfaces de fábrica, para cada dependencia de este tipo. ¿Hay una mejor manera?

JW01
fuente
¿Qué intentas probar? ¿Que el Validador crea ValidationErrors? Falsa validación El error no ayudará. Tal como está el código, el Validador no es muy útil porque puede llamar a validar, pero no puede obtener los errores generados.
Michael Brown
Lo dejé fuera, pero hay otros métodos que proporcionan acceso al objeto ErrorList. Entonces puedo probar usando esos.
JW01
Consulte stackoverflow.com/questions/4859523/… para obtener información relacionada
blueberryfields del

Respuestas:

4

El cambio más simple para hacer que su unidad de clase sea comprobable es

  1. (extraiga el código de creación en un nuevo método (virtual), ya lo tiene)
  2. Subclase la clase probada, anulando el método de creación para crear un objeto simulado adecuado en lugar del real
  3. escribe tus pruebas unitarias :-)

Por supuesto, esto no es muy bueno. Pero le permite cubrir la clase con pruebas unitarias, después de lo cual puede comenzar a refactorizar hacia el diseño ideal, sin temor a romper el código con cambios más profundos.

Referencia obligatoria: trabajar eficazmente con código heredado .

Sin embargo, es difícil decir mucho sobre ese diseño ideal, ya que su ejemplo carece de detalles y contexto. Si nos cuenta más sobre lo que pretende evaluar y cuál es el papel de la clase evaluada, podríamos ayudarlo más.

Péter Török
fuente
Ese ha sido mi enfoque general, pero en este caso hay varias subclases de Validator, y hay mucha configuración involucrada en la prueba. Si hago una subclase de prueba, también tengo que hacer una subclase de prueba para cada subclase real de Validator. Así que terminaré duplicando mucho de mi trabajo de configuración. Pero tal vez eso sea necesario en este caso.
JW01
2

Normalmente la solución para este problema es el patrón de proveedor:

public class Validator {
    public Provider<ValidationError> errorProvider;

    public void addError(String text) {
        errors.add(errorProvider.get(text));
    }
}

El proveedor es algo similar a la fábrica. En este caso, volverá nuevo ValidationErrorcada vez que getse llame.

Se puede encontrar más información en el libro de Inyección de dependencias en la sección 3.3.2 (Reinyección con el patrón Proveedor).

Guice , por ejemplo, tiene un proveedor para esto. Si desea algo más sofisticado, puede usar AssistedInject .

Ángel fácil
fuente