Kłopot podczas testów przy pomocy bibloteki Mockito.

0

Witam serdecznie !

Ostatni coś wspominałeś jak się spotkaliśmy że robiłeś testy przy pomocy Mock.
Ja utknąłem z tym tematem.

Opisze w czym sprawa i jak pozwolisz podeślę swoje źródła.
W czym tkwi mój kłopot / problem :

  1. Jest sobie serwis IsbnDbV2DataBaseDirectoryModelServiceImpl który implementuje interfejs
    IsbnDbV2DataBaseDirectoryModelService - serwis jako bean Springa ( adnotacja @Service)
  2. Jest sobie DAO IsbnDbV2DataBaseDirectoryModelDao które jest dziedziczy po generycznym
    DAO AbstractHibernateDAO. Te AbstractHibernateDAO jest klsą abstrakcyjna dla pozostałych
    DAO zdefiniowanych w systemie. - dao jak repozytorium springa @Repository.

Chodzi mi o przetestowanie IsbnDbV2DataBaseDirectoryModelServiceImpl i IsbnDbV2DataBaseDirectoryModelDao przy pomocy biblioteki mockito lub ogólnie biblioteki która używa Mock - przykład przetasowania jednej / dwóch metod z parametrami i bez nich.

W załączeniu kod z którego korzystam.

Jak sam próbuje wykonać te testo to coś mi nie wychodzi lub tez nie wiem czy te testy sa prawidłowe napisane. Z Przykładem to sobie jakoś poradzę a tak to drepcze wokół tematu.

1

Pokaż tu kod jednej metody którą chcesz przetestować i dostaniesz przykład jak taki test napisać.

0

Przesyłam kod dwóch metod - jednej z parametrem a drugiej bez niego. Proszę o przykład testu.

kod tu przedstawiony pochodzi z klasy serwisu. Kod jest dołączony do wątku.

Metoda bez parametru :

public List<IsbnDbV2DataBaseDirectoryModel> getAllDirectory()throws IsbnDbV2DataBaseServiceException {
		List<IsbnDbV2DataBaseDirectoryModel> result = new ArrayList<IsbnDbV2DataBaseDirectoryModel>();		
		try {
			result = isbnDbV2DataBaseDirectoryModelDao.getAll();		
			if (result!=null && result.size() > 0) {
				return result;
			}	
		} catch(Exception e) {
			throw new IsbnDbV2DataBaseServiceException("Powstal blad podczas pobierania elementów ze slownika : "+e.getMessage());
		}
		return result;
	}

Metoda z parametrem :
public IsbnDbV2DataBaseDirectoryModel getIsbnDbV2DataBaseDirectoryModelByName(String indexName) throws IsbnDbV2DataBaseServiceException {
		Preconditions.checkNotNull(indexName, "indexName nie moze byc puste");	
		IsbnDbV2DataBaseDirectoryModel result = null;
		try{
			if (indexName== null || indexName.equals("")) {
				throw new NullPointerException ("Parametr nie moze byc pusty");
			}
			List<IsbnDbV2DataBaseDirectoryModel> resultTmp = (List<IsbnDbV2DataBaseDirectoryModel>) isbnDbV2DataBaseDirectoryModelDao.getCurrentSession().createQuery(hqltoExec).setParameter("indexName", indexName).list();			
			if (resultTmp !=null && resultTmp.size() > 0){
				result = resultTmp.get(0);
			}
		} catch(Exception e) {
			throw new IsbnDbV2DataBaseServiceException("Powstal blad podczas pobierania elemtu ze slownika : "+e.getMessage());
		}
		return result;
	}
1

A ja poprosze o kod który ma sens... Bo np. po co ten if w metodzie skoro absolutnie na nic nie wpływa? o_O Równoważny jest zapis:

public List<IsbnDbV2DataBaseDirectoryModel> getAllDirectory()throws IsbnDbV2DataBaseServiceException {
        List<IsbnDbV2DataBaseDirectoryModel> result = new ArrayList<IsbnDbV2DataBaseDirectoryModel>();        
        try {
            result = isbnDbV2DataBaseDirectoryModelDao.getAll();        
        } catch(Exception e) {
            throw new IsbnDbV2DataBaseServiceException("Powstal blad podczas pobierania elementów ze slownika : "+e.getMessage());
        }
        return result;
    }

Poza tym łapanie Exception albo rzucanie NullPoitnerException to jest jakaś masakra i WTF tygodnia. I jeszcze zwracanie nulli z tych metod, żeby potem robić takie ifologie. Dramat.

Testy dla takiej metody muszą być 2. Jeden dla poprawnego wywołania, drugi kiedy leci wyjątek. Możesz się tu bawić w użycie testowego kontekstu springa, ale jako że prosiłeś tylko o proste testy... Użyje EasyMock a nie Mockito, bo bardziej go lubie, ale zapis będzie analogiczny. Nie wiem jak sie nazywa klasa w której to siedzi więc będzie MyClass

@Test
public void testGetAllDirectory(){
    MyClass classUnderTest = new MyClass();

    IsbnDbV2DataBaseDirectoryModelDao mockDao = createNiceMock(IsbnDbV2DataBaseDirectoryModelDao.class);
    classUnderTest.setDao(mockDao); //jak nie ma settera to można przez Whitebox to zrobić  zawsze
    List<IsbnDbV2DataBaseDirectoryModel> expectedResult = new ArrayList<IsbnDbV2DataBaseDirectoryModel>();
    expect(mockDao.getAll()).andReturn(expectedResult).once();

    replayAll();
    List<IsbnDbV2DataBaseDirectoryModel> result = classUnderTest.getAll();
    
    verifyAll();
    assertEqual(result, expectedResult);
}

Analogicznie dla testu z wyjątkiem:

@Test(expected = IsbnDbV2DataBaseServiceException.class)  
public void testGetAllDirectory(){
    MyClass classUnderTest = new MyClass();

    IsbnDbV2DataBaseDirectoryModelDao mockDao = createNiceMock(IsbnDbV2DataBaseDirectoryModelDao.class);
    classUnderTest.setDao(mockDao); //jak nie ma settera to można przez Whitebox to zrobić  zawsze
    expect(mockDao.getAll()).andThrow(new JakiśtamWyjątek()).once();

    replayAll();
    List<IsbnDbV2DataBaseDirectoryModel> result = classUnderTest.getAll();
}

Generalnie taki jest zawsze schemat testu.

  1. Ustawianie parametrów testu, tworzenie mocków i ustalanie spodziewanego zachowania.
  2. replay mocków + wywołanie faktycznej metody którą testujesz
  3. verify mocków + asercje które sprawdzają czy efekt jest taki jakiego się spodziewałeś. Oczywiście jak leci wyjątek to ten krok załatwia automatycznie oczekiwanie wyjątku na poziomie JUnit.
0

Dzięki za pomoc.
Co do kodu to jeszcze się uczę i wygląda on jak wygląda.
Z Poważaniem.

0

Witam serdecznie !

Próbowałem wykonać testy według tego co zostało wcześniej przedstawione.
W porządku przechodzi test bez parametrów lecz w przypadku metody przedstawionej jako druga leci wyjątek -
choć powinna być zwrócona instancja obiektu.

Proszę jeszcze raz o pomoc.

Testy były wykonywane w środowisku TestNg

Kod poniżej :

@Test(dataProvider="dataToTest",priority=1)
	public void getIsbnDbV2DataBaseDirectoryModelByName(IsbnDbV2DataBaseDirectoryModel isbnDbV2DataBaseDirectoryModel) throws IsbnDbV2DataBaseServiceException {
		 IsbnDbV2DataBaseDirectoryModelDao mockDao = createNiceMock(IsbnDbV2DataBaseDirectoryModelDao.class);
		 isbnDbV2DataBaseDirectoryModelService.setIsbnDbV2DataBaseDirectoryModelDao(mockDao);		 
		 expect(mockDao.getById(isbnDbV2DataBaseDirectoryModel.getDataSourceId())).andReturn(isbnDbV2DataBaseDirectoryModel).once();
		 
		 replayAll();
		 IsbnDbV2DataBaseDirectoryModel result = isbnDbV2DataBaseDirectoryModelService.getIsbnDbV2DataBaseDirectoryModelByName(isbnDbV2DataBaseDirectoryModel.getDirectorySearchName());
		 
		 verifyAll();
		 Assert.assertEquals(result, isbnDbV2DataBaseDirectoryModel);	 
	}
	
	@DataProvider(name = "dataToTest")
	private Object[][] createDataForTest() {		
		IsbnDbV2DataBaseDirectoryModel isbnDbV2DataBaseDirectoryModel = new IsbnDbV2DataBaseDirectoryModel();
		java.util.Date date= new java.util.Date();
		isbnDbV2DataBaseDirectoryModel.setDateCreate(new Timestamp(date.getTime()));
		isbnDbV2DataBaseDirectoryModel.setDirectorySearchName(RandomStringUtils.randomAlphabetic(20));
		return new Object[][]{{isbnDbV2DataBaseDirectoryModel}};
	}
1
  1. Proponuje nadawać tym klasom jeszcze dłuższe nazwy, bo te za łatwo się czyta ;]
  2. Rozumiem że twoją klasą którą testujesz jest tutaj IsbnDbV2DataBaseDirectoryModelService?
  3. Zupełnie nie rozumiem jak to miałoby ci zadziałac. Mockujesz to swoje Dao, ok. Ale przecież ty wcale na tym DAO w tej metodzie nie wywołujesz getById() tylko coś zupełnie innego. Więc czego się spodziewasz niby?
0

Czyli z tego co napisałeś rozumiem iż powinienem wołać taką samą metodę jaka wołam w DAO ?
W tym przypadku getById() ?
W wywołanej metodzie poprzez zapytanie hql chce pobrać dane takie same jak dane zwrócone przez metodę getById() obiektu DAO.
Czy aby przetestować tą metodę muszę inaczej skonstruować test ?

1

Jejku, jej! Zacznijmy od początku...
Mockowanie służy do usuwania zewnętrznych zależności w trakcie wykonywania testu. Jeśli twój obiekt w trakcie działania metody korzysta z innych obiektów to jak coś się wysypie to nie wiesz czy to ten twój obiekt zawinił czy te obiekty z których korzysta. Dodatkowo czasem trudno przetestować jakąś błędną sytuację. W twoim przypadku zależnością jest na przykład DAO. Chcemy więc stworzyć takie udawane DAO które na pewno się przypadkiem nie wysypie i będzie się zachowywać zawsze tak jak sobie tego życzymy.
W tym celu ustalamy spodziewane zachowanie mocka za pomocą expect().
Dla jasności: normalnie dla zwykłego mocka musisz ustalić wszystkie wywołania jakie mają nastąpić, tzn musisz dać expect() dla każdej operacji wołanej na mocku, inaczej mock zasygnalizuje błąd bo wywołano na nim metodę której się nie spodziewał. Często jest to dość uciążliwe i nieprzydatne dlatego masz NiceMock który dla niespodziewanych metod zwraca 0 / nulla.
Ale wracając do naszego problemu: otóż ustalając zachowanie dla mocka musisz to zrobić dokładnie! Tzn skoro mówisz że spodziewasz się wywołania metody ala() to mock tego się właśnie spodziewa. Przecież to nie jest jasnowidz żeby wiedzieć że wywołanie metody ela() dałoby taki sam efekt. Mówisz że ma się spodziewać ala() i tego się spodziewa i na ela() nie zareaguje ustalonym zachowaniem.

W twoim przypadku mówisz mockowi że ma się spodziewać getById() a wołasz na nim getSession() a potem jeszcze jakieś operacje na tym session. To nie ma prawa zadziałać... Musiałbyś mockowi powiedziec że jak ktoś wywoła getSession() to ma zwrócic mockowane Session. A temu Session dać expect że jak ktoś wywoła na nim... i tak dalej.

1

IsbnDbV2DataBaseDirectoryModelServiceImpl...
przypomniało mi się
https://twitter.com/dibblego/status/441374060211998720

0

Prosto , łopatlogicznie :)
Dziękuję za wytłumaczenie.

Z Poważaniem.

0

Mockito jeszcze żyje? Z tego co widziałem to ostatnia wersja jest z '12

1 użytkowników online, w tym zalogowanych: 0, gości: 1