Veja como meu código funciona. Eu tenho um objeto que representa o estado atual de algo semelhante a um pedido de carrinho de compras, armazenado em uma API de compras de terceiros. No código do meu controlador, desejo poder chamar:
myOrder.updateQuantity(2);
Para realmente enviar a mensagem para terceiros, o terceiro também precisa saber várias coisas específicas para ESTE pedido, como oe orderID
o loginID
que não serão alteradas durante a vida útil do aplicativo.
Então, quando eu crio myOrder
originalmente, injeto a MessageFactory
, o que sabe loginID
. Então, quando updateQuantity
é chamado, o Order
passa adiante orderID
. O código de controle é fácil de escrever. Outro encadeamento lida com o retorno de chamada e as atualizações, Order
se a alteração foi bem-sucedida, ou informa Order
que sua alteração falhou, se não foi.
O problema está testando. Como o Order
objeto depende de a MessageFactory
e precisa MessageFactory
retornar Message
s reais (que são chamados .setOrderID()
, por exemplo), agora tenho que configurar MessageFactory
zombarias muito complicadas . Além disso, não quero matar nenhuma fada, pois "Toda vez que um Mock devolve um Mock, uma fada morre".
Como posso resolver esse problema, mantendo o código do controlador tão simples quanto? Li esta pergunta: /programming/791940/law-of-demeter-on-factory-pattern-and-dependency-injection, mas não ajudou porque não falou sobre o problema de teste .
Algumas soluções em que pensei:
- De alguma forma, refatorar o código para não exigir que o método de fábrica retorne objetos reais. Talvez seja menos de uma fábrica e mais de uma
MessageSender
? - Crie uma implementação somente de teste
MessageFactory
e injete isso.
O código está bastante envolvido, aqui está minha tentativa de um 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.send
a mensagem é enviada ou se ela está correta ?
verify(messageMock).setQuantity(2)
, e verify(layer).send(messageMock);
também o updateQuantity
deve retornar false se o Order
já tiver uma atualização pendente, mas eu omiti esse código por razões de sscce.
Order
objeto e oMessageFactory
. Esta é uma boa descrição, mas é um pouco abstrato abordar diretamente com uma resposta clara.