Ci scusiamo per la domanda prolissa. Spero che la domanda e le sue potenziali risposte possano servire come esempio utile per gli sviluppatori che si stanno chiedendo dove impostare la barra per testare combinazioni abbastanza semplici di funzioni già testate.
Considerare il seguente modello semplice per un ordine (la definizione dei tipi ausiliari è omessa per brevità):
type Order =
{OrderDate: DateTime
CustomerId: CustomerId;
SalesPersonId: SalesPersonId;
OrderLines: OrderLine list}
Diciamo che vogliamo aggregare i dati di tutti gli ordini in un certo intervallo di tempo. Inoltre, diciamo che potremmo voler includere solo i dati degli ordini che corrispondono a un determinato ID cliente e / o un determinato ID di una persona di vendita. Questo potrebbe essere implementato come una serie di funzioni semplici con firma Order -> Order option
(dopo l'applicazione parziale di qualunque sia il parametro del filtro), quindi combinate usando Option.bind
e infine applicate a un elenco di ordini usando List.choose
. Ad esempio:
type OrderFilter =
{StartDate: DateTime;
EndDate: DateTime;
CustomerId: CustomerId option;
SalesPersonId: SalesPersonId option}
// DateTime -> Order -> Order option
let filterStartDate startDate order =
if order.OrderDate >= startDate then Some order else None
// DateTime -> Order -> Order option
let filterEndDate endDate order =
if order.OrderDate <= endDate then Some order else None
// CustomerId -> Order -> Order option
let filterCustomerId custId order =
match custId with
| None | Some id when order.CustomerId = id -> Some order
| _ -> None
// SalesPersonId -> Order -> Order option
let filterSalesPersonId spId order =
match spId with
| None | Some id when order.SalesPersonId = id -> Some order
| _ -> None
// OrderFilter -> Order -> Order option
let applyOrderFilter filter order =
let (>=>) f1 f2 = f1 >> (Option.bind f2)
let combinedFilter =
filterStartDate filter.StartDate
>=> filterEndDate filter.EndDate
>=> filterCustomerId filter.CustomerId
>=> filterSalesPersonId filter.SalesPersonId
order |> combinedFilter
Il filtraggio viene quindi eseguito in questo modo da altrove nel codebase:
let filter =
{StartDate = ...;
...}
let orders = getOrders() |> List.choose (applyOrderFilter filter)
...
Ora, alla domanda in questione: le singole funzioni del filtro sono (e sono state progettate per essere) facili da testare. Secondo la mia opinione piuttosto inesperta, la controllabilità di applyOrderFilter
è quindi discutibile: tutte le sue parti centrali costitutive sono accuratamente testate; si può facilmente verificare (IMHO) semplicemente osservando che funziona correttamente; e la composizione delle funzioni in questo modo sembra essere principalmente un caso di "se compila, funziona".
L'unica eccezione molto importante è l'elenco effettivo di funzioni che componi, in cui non riceverai avvisi se dimentichi di includere una delle funzioni di filtro. (L'applicazione parziale è un'altra fonte di potenziali errori - in questo caso, è possibile cambiare gli argomenti della data di inizio / fine. Poiché il filtraggio dei dati è corretto per l'azienda e non facilmente individuabile dall'utente (che vede solo i dati aggregati) , Sono favorevole ad avere una sorta di test automatico per applyOrderFilter
.
Ora, il test unitario applyOrderFilter
sembra come se dovesse duplicare tutti i test per le singole funzioni del filtro e causare un'esplosione combinatoria di casi di test. Sarebbe anche eccessivo, perché l'unica cosa rilevante da testare sembra essere solo la combinazione dei filtri, per proteggersi dalle regressioni (l'eliminazione accidentale di una linea nella composizione del filtro sarebbe davvero pessima).
Dove ho bisogno di aiuto è nel capire quanto segue:
-
È necessario testare la composizione di tali funzioni? Ho fornito le mie ragioni sopra, ma potrei sbagliare sul lato di troppi test e non vedere i contro-argomenti (ad esempio "fare in modo che il tipo di modifica sia così evidentemente sbagliato sarebbe imperdonabile" - ad un certo punto non è possibile prendere in considerazione più stupidità / controllo da parte degli sviluppatori).
-
In che modo la composizione del filtro può essere testata facilmente e in modo affidabile senza dover testare la logica del filtro vero e proprio? Mi sembra che sarebbe necessario un refactoring per rendere possibile tutto ciò, ma io m bloccato a capire come progettare questo per un facile test.