Sono stato alle prese con un problema sempre più fastidioso per quanto riguarda le nostre unit test che stiamo implementando nel mio team. Stiamo tentando di aggiungere unit test nel codice legacy che non era ben progettato e mentre non abbiamo avuto alcuna difficoltà con l'effettiva aggiunta dei test, stiamo iniziando a lottare con il modo in cui i test si stanno rivelando.
Come esempio del problema, supponiamo che tu abbia un metodo che chiama altri 5 metodi come parte della sua esecuzione. Un test per questo metodo potrebbe essere quello di confermare che si verifica un comportamento a seguito della chiamata di uno di questi 5 altri metodi. Quindi, poiché un test unitario dovrebbe fallire per un motivo e un motivo, si desidera eliminare i potenziali problemi causati chiamando questi altri 4 metodi e deridendoli. Grande! Viene eseguito il test unitario, i metodi derisi vengono ignorati (e il loro comportamento può essere confermato come parte di altri test unitari) e la verifica funziona.
Ma c'è un nuovo problema: il test unitario ha una conoscenza intima di come hai confermato che il comportamento e le eventuali modifiche alla firma in uno di quegli altri 4 metodi in futuro, o qualsiasi nuovo metodo che deve essere aggiunto al "metodo genitore", comportare la necessità di modificare il test unitario per evitare possibili guasti.
Naturalmente il problema potrebbe essere mitigato in qualche modo semplicemente avendo più metodi per realizzare meno comportamenti, ma speravo che ci fosse forse una soluzione più elegante disponibile.
Ecco un esempio di unit test che rileva il problema.
Come nota rapida, "MergeTests" è una classe di unit test che eredita dalla classe che stiamo testando e ignora il comportamento secondo necessità. Questo è un "modello" che impieghiamo nei nostri test per permetterci di ignorare le chiamate a classi / dipendenze esterne.
[TestMethod]
public void VerifyMergeStopsSpinner()
{
var mockViewModel = new Mock<MergeTests> { CallBase = true };
var mockMergeInfo = new MergeInfo(Mock.Of<IClaim>(), Mock.Of<IClaim>(), It.IsAny<bool>());
mockViewModel.Setup(m => m.ClaimView).Returns(Mock.Of<IClaimView>);
mockViewModel.Setup(
m =>
m.TryMergeClaims(It.IsAny<Func<bool>>(), It.IsAny<IClaim>(), It.IsAny<IClaim>(), It.IsAny<bool>(),
It.IsAny<bool>()));
mockViewModel.Setup(m => m.GetSourceClaimAndTargetClaimByMergeState(It.IsAny<MergeState>())).Returns(mockMergeInfo);
mockViewModel.Setup(m => m.SwitchToOverviewTab());
mockViewModel.Setup(m => m.IncrementSaveRequiredNotification());
mockViewModel.Setup(m => m.OnValidateAndSaveAll(It.IsAny<object>()));
mockViewModel.Setup(m => m.ProcessPendingActions(It.IsAny<string>()));
mockViewModel.Object.OnMerge(It.IsAny<MergeState>());
mockViewModel.Verify(mvm => mvm.StopSpinner(), Times.Once());
}
In che modo il resto di voi ha affrontato questo problema o non esiste un modo "semplice" per gestirlo?
Aggiornamento: apprezzo il feedback di tutti. Sfortunatamente, e non sorprende davvero, non sembra esserci un'ottima soluzione, modello o pratica che si possa seguire nei test unitari se il codice da testare è scarso. Ho segnato la risposta che meglio ha catturato questa semplice verità.