Questa è una cosa che mi è venuta in mente per la maggior parte del tempo, ma non ho ancora trovato un buon approccio. Quindi ecco la cosa. Abbiamo un'applicazione server che ha un numero limitato di casi d'uso complessi che coinvolgono un sacco di problemi e gestione dei dati dei clienti. Non posso entrare nei dettagli esatti qui, quindi facciamo un esempio per alcuni contesti. Supponiamo che tu abbia un sistema di elaborazione degli ordini in cui elabori gli ordini per un cliente. Usiamo Java / Spring con l'iniezione delle dipendenze e tutti i campanelli e fischietti, quindi il nostro servizio sarebbe simile a questo:
class OrderService {
// these are all dependency-injected by Spring -leaving this out for clarity.
private CustomerService customerService;
private OrderDao orderDao;
private DiscountCodeService discountCodeService;
private CustomerLoyaltyProgramService loyaltyProgramService;
private TaxService taxService;
public Order createOrder(OrderForm form) {
// form is pre-validated through JSR303 annotations but there are
// some additional validations
// that need to be made
//
if (formHasError) {
throw SomeException();
}
// ok we can continue here.
Customer customer = customerService.getCustomerById(form.getCustomerId())
// build up an order from the form
Order order = new Order();
// copy over stuff..
// check if customer has a discount code
if (form.getDiscountCode() != null) {
discountCodeInfo = discountCodeService.getDiscountCodeInfoFor(form.getDiscountCode())
// now walk over line items to see if there is some applicable order line item.
}
// check if the customer is in any loyalty program that will give him
// additional discounts
List<LoyaltyProgram> programs = loyaltyProgramService.findByCustomer(customer);
// do some more ifs and elses to apply loyalty program discounts
// check applicable sales tax for customer
SalesTax tax = taxService.findSalesTaxFor(form.getAddress())
// do some tax calculations, more ifs and elses
// finally store order
orderDao.save(order);
return order;
}
}
Testare un tale servizio con un test unitario è fondamentalmente un incubo. Devi prendere in giro tutte le dipendenze in cui la derisione dipende dal flusso attraverso il metodo e dalle diverse regole aziendali applicate su determinate circostanze. A una certa quantità di mock e code path la tua testa esploderà semplicemente.
Un altro approccio che abbiamo provato è stato dividerlo in diversi metodi privati (ben protetti, in quanto è necessario accedervi in un test), che lo renderebbero più testabile. Tuttavia, la nostra logica aziendale è diffusa ovunque e questo rende difficile seguire il codice e devi ancora avere un "metodo master" che richiami tutti i piccoli metodi di supporto nell'ordine corretto ed è necessario testarlo anche per te in pratica torniamo al punto di partenza.
Quindi quello che sto cercando è un modo per riorganizzare ciò che consente di testare i metodi più facilmente senza dover pensare a tutte le dipendenze pur mantenendo il codice manutenibile e non diffondendolo in mille luoghi diversi dove non puoi più seguire la logica. Immagino che questo sarà probabilmente un compromesso, ma mi chiedo come potresti averlo affrontato nei tuoi progetti.