2009-06-24 14 views
5

cómo se puede probar un método que devuelve un objeto complicado que no permite el acceso a sus campos. Considere este fragmento--unidad que prueba un método que devuelve un objeto no trivial

public ResultState getResultState(){ 
    ResultState resultState = new ResultState(); 
    // logic // 
    this.resultState = resultState; //set parent class field 
    return resultState; 
} 

ResultState es un objeto que no permite muchos de sus métodos y campos. soy ni idea de qué otra manera de probar un método de este tipo que no sea:

assertNotNull(object.getResultState()) //the return ResultState is used by other class methods 

Mi pregunta es general, pero estoy esperando que muestra lo desesperado que estoy de obtener una pista sobre cómo pasar la prueba. .. gracias por la ayuda

Respuesta

11

Bueno, ¿qué hay de importante en el resultado? Presumiblemente puede hacer algo o sería un resultado inútil. ¿Qué diferenciaría el código de trabajo del código roto en este caso?

(No estoy tratando de olvidar el problema, a veces es muy difícil mantener una encapsulación estricta pero realizar pruebas de manera efectiva. A veces vale la pena abrir un objeto un poco para facilitar las pruebas ...)

+0

+1 - casi palabra por palabra lo que iba a decir –

+0

Jon, buena pregunta - lo que es importante sobre el resultado. Después de pensar en esto, lo más importante sobre el resultado es que debe ser un objeto esperado por los métodos que lo usarán.Y esto se logra en la sección // LOGIC // del código, donde LOGIC manipula los campos de "esto" Así que hice los siguientes escenarios de prueba: 1. Configuré setters y getters para estos campos 2. set ellos con + ve (valores buenos) y -ve (valores incorrectos) 3. y compilarlo sin excepciones ... esto son pruebas triviales pero un buen comienzo-- gracias –

5

¿Qué hace este objeto? Si no puede consultar campos, etc., supongo que actúa sobre algún objeto que pase a él. Si ese es el caso, ¿puede pasar un objeto ficticio que implementa la misma interfaz, pero verifica que el objeto resultante lo llame con los argumentos apropiados, en la secuencia esperada, etc.?

p. Ej.

public class ResultObject { 
    public void actOn(ActedUpon obj) { 
     // does stuff 

donde ActedUpon es una interfaz, y tendría una aplicación 'normal', y una aplicación de validación (utilizando un marco de burla como JMock aquí sería ideal)

2

En primer lugar, por convención un captador no debería tener efectos secundarios. Por lo tanto, es casi seguro que es una mala idea tener

this.resultState = resultState;

en su captador.

Dicho esto, return resultState debe tener algunos propósito, de lo contrario, ¿por qué lo devuelve? Así que prueba si resultState hace lo que debería.

0

Si lo que desea probar es simplemente que después de llamar o.setResultState (resultState), se puede obtener el resultState con o.getResultState(), la prueba sería algo muy simple como:

ResultState resultState = new ResultState(...); 
o.setResultState(resultState); 
assertEquals(resultState, o.getResultState()); 
2

Basado en ese código. Yo diría que el cheque por Not Null es suficiente. Comprobar la funcionalidad del ResultState devuelto pertenece a las pruebas de ResultState. Está intentando probar la funcionalidad de getResultState(). Lo único que hace es crear un nuevo objeto y guardar una referencia en it.resultState. Entonces verifique si creó uno y vea si guardó la referencia.

2

Desafortunadamente está usando getResultState() para crear el objeto ResultState, que es realmente su bloqueador.Consideremos refactorización al siguiente interfaz:

protected ResultState initResultState () 
{ 
    this.resultState = new ResultState(); 
    // Initialization Logic... 
    return this.resultState; 
} // END initResultState 

public ResultState getResultState () 
{ 
    if (this.resultState === null) 
    { 
     return this.initResultState(); 
    } 

    return this.resultState; 
} // END getResultState() 

Desde esta posición, es más fácil poner a prueba su método de obtención. Definir una clase descendiente que devuelve un conocido ResultState del trozo de initResultState() que puede ser interrogado, es decir:

/** 
* The test fixutre's initResultState() method merely returns a simple Stub of 
* the ResultState object, which can be more easily interrogated. 
*/ 
public ResultState initResultState () 
{ 
    return new Stub_ResultState(); 
} // END initResultState 

Eso todavía le deja con la initResultState() protegido, por supuesto. Considere el siguiente refactorización:

protected ResultState initResultState (ResultState newResultState) 
{ 
    this.resultState = newResultState; 
    // Initialization Logic... 
    return this.resultState; 
} // END initResultState 

public ResultState getResultState () 
{ 
    if (this.resultState === null) 
    { 
     return this.initResultState(new ResultState()); 
    } 

    return this.resultState; 
} // END getResultState 

Esto le permite reemplazar el método getResultState() en una clase descendiente de tal manera que se puede pasar otro objeto ResultState del trozo a ser manipulado e interrogado después, por ejemplo:

/** 
* The test fixture's getResultState() method always acts as a passthrough for 
* initResultState(), which is protected, so that the output of the protected 
* method can be interrogated. 
*/ 
public ResultState getResultState () 
{ 
    return this.initResultState(new Stub_ResultState()); 
} // END getResultState 

Desde esta posición, necesita dos dispositivos de prueba: uno que anula el comportamiento de initResultState() para probar la funcionalidad de los captadores; otro que anula el comportamiento de getResultState() para probar el comportamiento de initResultState(). Ambos usan una instancia de la clase Stub_ResultState, que es necesariamente un descendiente de la clase ResultState, pero proporciona acceso público a los valores internos de su objeto principal para la interrogación en pruebas unitarias.

Espero que tenga sentido, pero no dude en pedir aclaraciones.

0

Al hacer lo nuevo en el getter, se está comprometiendo con una implementación concreta de ResultState. En su caso actual, eso significa que la implementación es opaca en cuanto a su estado.

Imagínese por un momento que en lugar de actualizar NewState en su lugar, lo obtuvo de una fábrica, y que la fábrica proporcionó alguna forma de sustituir un Estado de falsa, para que pudiera verificar que getResultState() estaba haciendo con el objeto ResultState entre crearlo y devolverlo.

Una manera simple de obtener ese efecto es dividir la creación de ResultState en un método sobrecargable. Por ejemplo,

public ResultState getResultState() { 
    ResultState resultState = createResultState(); 
    ... 

protected ResultState createResultState() { 
    return new ResultState(); 
} 

Entonces, asumiendo que su clase de prueba está en el mismo paquete, se puede crear una subclase en línea que anula getResultState() para devolver una maqueta.

Mientras eso funciona, es una forma frágil de escribir pruebas.

Un enfoque alternativo es dirigirse hacia algo así como

public ResultState getResultState() { 
    ResultState resultState = _resultStateFactory.newResultState(); 
    ... 

Esto requiere que averiguar una forma apropiada de la inyección de un ResultStateFactory en el objeto, que es algo un marco de inyección de dependencias es grande para. Para las pruebas, inyecte una fábrica que devuelva un falso ResultState que haya sido preparado con las expectativas apropiadas. Esto reduce el problema de prueba a "¿getResultState() manipula el objeto ResultState correctamente antes de devolverlo?"

Cuestiones relacionadas