Test utilizzando il mocking, devo prendere in giro anche tutte le dipendenze?

1

Ho il seguente metodo per testare:

public List<MarkId> getMarkIdList(ICar carDoc) {

    ICourseCar courseCarDoc = courseCarRep.get(carDoc);

    List<MarkWag> markWagList = courseCarDoc.getMarks();

    List<Integer> markIdList = new ArrayList<>();

    for (MarkWag markWag : markWagList)
        markIdList.add(markWag.getMarkId());

    List<ICourseMark> courseMarkDocList = courseMarkRep.getList(markIdList);

    List<MarkId> markIds = new ArrayList<>();

    for (ICourseMark courseMark : courseMarkDocList)
        markIds.add( new MarkId(courseMark.getMarkDescriptor()));

    return markIds;
}

Questo è il test dell'unità che ho creato:

@RunWith(PowerMockRunner.class)
@SpringApplicationCourseuration(classes=SpringConf.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PowerMockIgnore({"javax.management.*"})
public class CourseCarTest {

    @Mock private CourseCarRepImpl courseCarRep;
    @Mock private CourseMarkRepImpl courseMarkRep;
    @Mock private CourseCarDoc courseCat;
    @Mock private ICourseCar courseCarDoc;
    @Mock MarkWag markWag;
    List<MarkWag> markWagList = new ArrayList<>();
    @Mock  ICourseMark courseMark;
    List<ICourseMark> courseMarkDocList = new ArrayList<>();
    @Mock ICar carDoc;

    @InjectMocks
    @Autowired
    @Qualifier("CourseCarBO")
    private CourseCarBO courseCarBo;

    @Before
    public void setup() throws Exception {

        markWagList.add(markWag);
        courseMarkDocList.add(courseMark);

        whenNew(CourseCarRepImpl.class).withAnyArguments().thenReturn(courseCarRep);
        whenNew(CourseMarkRepImpl.class).withAnyArguments().thenReturn(courseMarkRep);
        when(courseCarRep.get(anyInt())).thenReturn(courseCarDoc);
        when(courseCarDoc.getMarks()).thenReturn(markWagList);
        when(markWag.getMarkId()).thenReturn(1);
        when(courseMarkRep.getList(anyList())).thenReturn(courseMarkDocList);
        when(courseMark.getMarkDescriptor()).thenReturn(1);
    }

    @Test
    public void testGetMarkIdList() {

        courseCarBo.getMarkIdList(1);

        verify(courseCarRep).get(anyInt());
    }
}   

La mia intenzione era di avere un approccio ibrido, sia prendendo in giro alcuni oggetti e iniettando altri con Spring. Tuttavia ho notato che mentre deridevo uno degli oggetti, devo prendere in giro gli altri che seguono poiché dipendono l'uno dall'altro. Quindi mi sembra che non sia davvero possibile avere un approccio ibrido.

Riesci a individuare qualcosa di sbagliato nell'approccio utilizzato in questo test unitario?

Quando devo prendere in giro tutto, mi sembra di non provare nulla.

C'è un modo migliore? O il mio approccio è corretto, e prendere in giro tutto è la strada da percorrere?

    
posta user1883212 30.12.2015 - 11:58
fonte

2 risposte

4

Dovresti iniziare passando dalla programmazione procedurale, che attualmente è il tuo codice, a OOP.

"Ho oggetti", potresti protestare, ma in realtà i tuoi oggetti non incapsulano nulla. In un vero mondo OO, il tuo codice dovrebbe assomigliare a questo:

public List<MarkId> getMarkIdList(ICar carDoc)
{
    return courseCarRep.get(carDoc).getCourseMarkIdsAsList();
}

Il metodo get() di courseCarRep restituisce un oggetto CourseCar , che ha già tutto ciò di cui ha bisogno e il getCourseMarkIdsAsList() è un metodo pubblico che la classe CourseCar fornisce per nascondere la logica più complicata dietro di esso (che sta prendendo la sua lista di CourseMarks e restituisce solo le loro identificazioni).

Che può far apparire una domanda: Perché ho bisogno del metodo getMarkIdList(ICar) in primo luogo?

Quindi, come faccio a testare unitamente il metodo corrente getMarkIdList ?

Risposta semplice: non lo fai.

Risposta più elaborata: la procedura che stai tentando di testare sembra essere così alta nel tuo livello applicativo che non è più considerata una singola unità e pertanto non dovrebbe essere testata usando i test unitari.

Se vuoi verificare che il metodo funzioni, usa i test di integrazione. Se vuoi concentrarti sul test delle unità, prova invece i seguenti oggetti e metodi evidenziati:

  • courseCarRep::get
  • courseCarDoc::getMarks
  • courseMarkRep::getList
  • courseMark::getMarkDescriptor

Se collaudi, che quei metodi funzionano in tutte le condizioni, puoi essere abbastanza sicuro, anche la tua procedura funzionerà.

Trust. Il tuo. Test.

Che cosa dovrei (generalmente) prendere in giro davvero, quando faccio test unitari?

Dovresti solo prendere in giro il comportamento degli oggetti che sono necessari per il test. Tutto il resto dovrebbe essere sostituito da un manichino o nullo (se possibile).

I valori nulli sono un ottimo indicatore del fatto che qualcosa non è utilizzato. Se passo un test unitario e vedo qualcuno passare un null invece di un oggetto, presumo che la variabile specifica non abbia nulla a che fare con il test stesso, non fa parte di esso e anche se sostituisco null oggetto con un'istanza reale, i risultati del test non possono essere modificati.

Prova anche a rimuovere l'operatore new dalla tua logica aziendale. O sostituiscilo con le fabbriche, se ritieni di aver davvero bisogno di creare qualcosa o di usare l'iniezione di dipendenza.

    
risposta data 31.12.2015 - 21:13
fonte
1

Il codice sotto test non ti sta rendendo davvero più facile. Se potessi riorganizzare quel codice, lo troveremmo molto più facile da testare. Altrimenti sei bloccato con questi test troppo complicati che passano attraverso tutti i cerchi per arrivare da ICar a MarkId.

È difficile fare raccomandazioni di riorganizzazione con una sola funzione data. Sembra che la tua funzione richieda una macchina quando ha davvero bisogno di una lista per ottenere la tua lista. Questo costringe la tua funzione a guardare attraverso diversi oggetti per ottenere ciò di cui ha bisogno. L'approccio più semplice e verificabile consiste nel chiedere i parametri necessari in anticipo e lavorare da lì. Questo taglierà tutto il codice di ricerca e renderà i tuoi test molto più facili da scrivere.

Rompere il codice in parti più piccole renderà il tuo codice basato su test multipli per dimostrare che funziona invece di un test master troppo difficile da gestire che fa tutto.

    
risposta data 31.12.2015 - 17:33
fonte

Leggi altre domande sui tag