Así es como funciona mi código. Tengo un objeto que representa el estado actual de algo similar a un pedido de carrito de compras, almacenado en una API de compras de terceros. En mi código de controlador, quiero poder llamar:
myOrder.updateQuantity(2);
Para enviar realmente el mensaje al tercero, el tercero también necesita saber varias cosas que son específicas de ESTE pedido, como el orderIDy el loginID, que no cambiarán durante la vigencia de la aplicación.
Entonces, cuando creo myOrderoriginalmente, inyecto un MessageFactory, que sabe loginID. Luego, cuando updateQuantityse llama, Orderpasa orderID. El código de control es fácil de escribir. Otro hilo maneja la devolución de llamada y se actualiza Ordersi su cambio fue exitoso, o informa Orderque su cambio falló si no fue así.
El problema es la prueba. Debido a que el Orderobjeto depende de a MessageFactory, y necesita MessageFactorydevolver Messages reales (que llama .setOrderID(), por ejemplo), ahora tengo que configurar simulaciones muy complicadas MessageFactory. Además, no quiero matar a ninguna hada, ya que "cada vez que un Mock devuelve un Mock, un hada muere".
¿Cómo puedo resolver este problema manteniendo el código del controlador igual de simple? Leí esta pregunta: /programming/791940/law-of-demeter-on-factory-pattern-and-dependency-injection pero no ayudó porque no habló sobre el problema de la prueba .
Algunas soluciones que he pensado:
- Refactorice de alguna manera el código para que no requiera que el método de fábrica devuelva objetos reales. ¿Quizás es menos una fábrica y más una
MessageSender? - Cree una implementación de solo prueba
MessageFactorye inyecte eso.
El código está bastante involucrado, aquí está mi intento de un sscce:
public class Order implements UpdateHandler {
private final MessageFactory factory;
private final MessageLayer layer;
private OrderData data;
// Package private constructor, this should only be called by the OrderBuilder object.
Order(OrderBuilder builder, OrderData initial) {
this.factory = builder.getFactory();
this.layer = builder.getLayer();
this.data = original;
}
// Lots of methods like this
public String getItemID() {
return data.getItemID();
}
// Returns true if the message was placed in the outgoing network queue successfully. Doesn't block for receipt, though.
public boolean updateQuantity(int newQuantity) {
Message newMessage = factory.createOrderModification(messageInfo);
// *** THIS IS THE KEY LINE ***
// throws an NPE if factory is a mock.
newMessage.setQuantity(newQuantity);
return layer.send(newMessage);
}
// from interface UpdateHandler
// gets called asynchronously
@Override
public handleUpdate(OrderUpdate update) {
messageInfo.handleUpdate(update);
}
}
layer.sendenvía el mensaje o que envía el mensaje correcto ?
verify(messageMock).setQuantity(2), y verify(layer).send(messageMock);también updateQuantitydebería devolver falso si Orderya tiene una actualización pendiente, pero omití ese código por razones de seguridad.
Orderobjeto y elMessageFactory. Esta es una buena descripción, pero es un poco abstracto abordarla directamente con una respuesta clara.