Spring Testing

Post on 25-May-2015

604 views 0 download

description

* Proč testujeme* Rozdíl mezi automatickými a ručními testy* Použití mocku a stubu* Integrace Spring framerku a JUnit

transcript

Spring frameworkMotto: Musíte rozbít vejce když chcete udělat omeletu

Spring framework training materials by Roman Pichlík is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

1Sunday 13 May 2012

Spring frameworkMotto: Musíte rozbít vejce když chcete udělat omeletu

2Sunday 13 May 2012

3Sunday 13 May 2012

Spring TDDTestování aplikací postavených na Spring frameworku

4Sunday 13 May 2012

Proč testujeme

5Sunday 13 May 2012

Primární Motivace

• Pocit sucha a bezpečí

• Verifikace kódu, že opravdu funguje

• Zpětná vazba při změnách

6Sunday 13 May 2012

klidné spaní

Jak testujeme

7Sunday 13 May 2012

Manuální testy

• Historicky běžná součást vývojového procesu

• Testovací fáze

• Vývojáři nebo QA oddělení

8Sunday 13 May 2012

Hlavní rysy

• Pomalé

• Drahé

• Neefektivní

• Náchylné k chybě lidského faktoru

9Sunday 13 May 2012

Pomalé - trvání testovacího kolečka, specifikace, vlastní průchod testuDrahé - lidský čas je oproti strojovému stále dražší (i v Bengalore)Neefektivní - otestování low level změny je kolikrát nemožnéLidský faktor - člověk je tvor líny, monotóní práce je ubijející (ne pro stroj)

Dopady manuálního testování

• Delší release cyklus

• Chyby se odhalí na konci vývojového cyklu

• Problém chyba v RC

• Neškáluje

10Sunday 13 May 2012

RC= Go nebo no Go nebo Hotfix díky tomu, že nám hoří termínNeškáluje = nový tester neznamená větší otestovanost (roste i code base)

Automatické testy• Píší se společně s kódem

• Kontinuální evoluce

• Různé testy podle úrovně abstrakce a cílové oblasti

• Jednotkové

• Integrační

• Výkonové11Sunday 13 May 2012

Dopady aut. testů

• Rychlejší release cyklus

• Test coverage

• Známe slabá místa

• Umožňují lépe pochopit fungování kódu

• Ukazují špatný/dobrý design

12Sunday 13 May 2012

Rychlejší release cyklus díky testům závisí na tom jak drahé je pro nás testy psát a udržovat

Manuální vs Automatické testy

13Sunday 13 May 2012

Kolik stojí opravení chybyN

ákla

dy n

a od

stra

nění

chy

by

čas nalezení chyby

14Sunday 13 May 2012

Náklady na odstranění rostou exponenciálně s čase objevení chyby

Kolik stojí opravení chybyN

ákla

dy n

a od

stra

nění

chy

by

čas nalezení chyby

automatické testy manuální testy15Sunday 13 May 2012

Náklady na odstranění rostou exponenciálně s čase objevení chybyAutomatické testy snižují čas na odhalení chyby

Doporučení

• Zaměřte se na automatické testy

• Testy lze zavádět inkrementálně

• Nový kód vždy

• Starý kód pokud se do něj šahá

• Starý kód podle důležitosti

• Testy jsou zrcadlem návrhu16Sunday 13 May 2012

Automatické testy

17Sunday 13 May 2012

Jednotkové vs. Integrační

• Jednotkové testy

• Konkrétní třída v izolaci

• Žádné závislosti

• Odstíněné od prostředí

• Integrační testy

• Interakce několika komponent

18Sunday 13 May 2012

Jednotkové testy

• Nejdůležitější je izolace

• Pokud možno všechny ostatní třídy odizolované

• Použití Stub a Mock objektů

• Nemíchat testovací scénáře

• Jedna testovací metoda per scénář

19Sunday 13 May 2012

JDK se považuje za důveryhodné => nemusíme dělat stuby

Unit test

public class ReservationServiceImpl implements ReservationService{

private BookStoreDao bookStoreDao; private AtomicLong counter = new AtomicLong(); private Map<Long, Reservation> resevations = new HashMap<Long, Reservation>();

public Reservation reserveBook(Long bookId) { Book book = bookStoreDao.getBook(bookId); if(book == null) { throw new BookNotFoundException(bookId); } ReservationImpl reservationImpl = new ReservationImpl(); reservationImpl.setReservationId(counter.incrementAndGet()); reservationImpl.setBookId(bookId); return reservationImpl; }}

20Sunday 13 May 2012

Unit test

public class ReservationServiceImpl implements ReservationService{

private BookStoreDao bookStoreDao; private AtomicLong counter = new AtomicLong(); private Map<Long, Reservation> resevations = new HashMap<Long, Reservation>();

public Reservation reserveBook(Long bookId) { Book book = bookStoreDao.getBook(bookId); if(book == null) { throw new BookNotFoundException(bookId); } ReservationImpl reservationImpl = new ReservationImpl(); reservationImpl.setReservationId(counter.incrementAndGet()); reservationImpl.setBookId(bookId); return reservationImpl; }}

Externí třída (v testu nahradit stub/mock)

20Sunday 13 May 2012

Unit test

public class ReservationServiceImpl implements ReservationService{

private BookStoreDao bookStoreDao; private AtomicLong counter = new AtomicLong(); private Map<Long, Reservation> resevations = new HashMap<Long, Reservation>();

public Reservation reserveBook(Long bookId) { Book book = bookStoreDao.getBook(bookId); if(book == null) { throw new BookNotFoundException(bookId); } ReservationImpl reservationImpl = new ReservationImpl(); reservationImpl.setReservationId(counter.incrementAndGet()); reservationImpl.setBookId(bookId); return reservationImpl; }}

Externí třída (v testu nahradit stub/mock)

dvě testovací metody (scénáře)20Sunday 13 May 2012

Unit test

public class ReservationServiceImpl implements ReservationService{

private BookStoreDao bookStoreDao; private AtomicLong counter = new AtomicLong(); private Map<Long, Reservation> resevations = new HashMap<Long, Reservation>();

public Reservation reserveBook(Long bookId) { Book book = bookStoreDao.getBook(bookId); if(book == null) { throw new BookNotFoundException(bookId); } ReservationImpl reservationImpl = new ReservationImpl(); reservationImpl.setReservationId(counter.incrementAndGet()); reservationImpl.setBookId(bookId); return reservationImpl; }}

Externí třída (v testu nahradit stub/mock)

dvě testovací metody (scénáře)20Sunday 13 May 2012

Unit test

public class ReservationServiceImpl implements ReservationService{

private BookStoreDao bookStoreDao; private AtomicLong counter = new AtomicLong(); private Map<Long, Reservation> resevations = new HashMap<Long, Reservation>();

public Reservation reserveBook(Long bookId) { Book book = bookStoreDao.getBook(bookId); if(book == null) { throw new BookNotFoundException(bookId); } ReservationImpl reservationImpl = new ReservationImpl(); reservationImpl.setReservationId(counter.incrementAndGet()); reservationImpl.setBookId(bookId); return reservationImpl; }}

Externí třída (v testu nahradit stub/mock)

dvě testovací metody (scénáře)20Sunday 13 May 2012

Stubpublic class BookStoreDaoStub implements BookStoreDao{

private Book book;

public Book getBook(Long bookId) { if(book != null && bookId.equals(book.getId())) { return book; } return null; }

public void saveBook(Book book) { this.book = book; }

public Book getBook() { return book; }

public void setBook(Book book) { this.book = book; }}

Implementuje stejné rozhraní

jednoduché chování připravené pro test

Možnost verifikovat stav stubu

21Sunday 13 May 2012

Použití stubupublic class ReservationServiceImplTest {

private BookStoreDao bookStoreDao; private ReservationServiceImpl reservationServiceImpl = new ReservationServiceImpl(); @Before public void setup(){ bookStoreDao = new BookStoreDaoStub(); reservationServiceImpl.setBookStoreDao(bookStoreDao); }

@Test public void testReserveNonExistingBook() { try { reservationServiceImpl.reserveBook(-1l); fail("BookNotFoundException musi byt vyhozena pro neexistujici knihu"); } catch(BookNotFoundException e) { //expected } }

}

Příprava stubu

22Sunday 13 May 2012

Použití stubu

• Výhody

• Znovupoužitelný kód

• Test support modul

• Nevýhody

• Změna interface znamená změnu stubu

• Implementace všech metod23Sunday 13 May 2012

žádné smart defaults

Použití mocku

• Mock je automatický generovaná třía

• Popisujeme chování, které nás zajímá

• Můžeme zkontrolovat interakci

24Sunday 13 May 2012

Mockovací knihovná Mockito

• Velmi pohodlné mockování

• Fluent API

• Jednoduchá na použití

• Umožňuje verifikovat stav mocku po testu

25Sunday 13 May 2012

jsou i další mock knihovny

Mock s Mockitem

import static org.mockito.Mockito.mock;import static org.mockito.Mockito.when;

public class ReservationServiceImplTest { private BookStoreDao bookStoreDao; private ReservationServiceImpl reservationServiceImpl = new ReservationServiceImpl(); @Before public void setup(){ bookStoreDao = mock(BookStoreDao.class); when(bookStoreDao.getBook(-1l)).thenReturn(null); reservationServiceImpl.setBookStoreDao(bookStoreDao); } @Test public void testReserveNonExistingBook() { try { reservationServiceImpl.reserveBook(-1l); fail("BookNotFoundException musi byt vyhozena pro neexistujici knihu"); } catch(BookNotFoundException e) { //expected } }

Příprava mocku

26Sunday 13 May 2012

Mock vs. Stub

• Mock

• Smart defaults

• Pokud se jedná o složitější rozhraní

• Stub

• Znovupoužitelné

27Sunday 13 May 2012

Integrační testy se Springem

28Sunday 13 May 2012

Spring test• spring-test modul

• Umožňuje nainicializovat Spring

• Umožňuje nainjectovat testované beany do testu

• Centrální třídy

• SpringJUnit4ClassRunner

• @ContextConfiguration29Sunday 13 May 2012

import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:META-INF/applicationContext.xml")public class ReservationServiceImplTest {

@Autowired private ReservationService reservationService;

@Test public void testReserveNonExistingBook() { try { reservationService.reserveBook(-1l); fail("BookNotFoundException musi byt vyhozena pro neexistujici knihu"); } catch(BookNotFoundException e) { //expected } }}

Integrace Spring a JUnit

Konfigurace Springu

Nainjectování testované beany

30Sunday 13 May 2012

• Napište jednotkový test pro ReservationServiceImp

• použijte mock pro BookStoreDao

• Napište integrační test pro ReservationServiceImp za pomocí Springu

• Jako BookStoreDao nedefinujte beanu, která bude naimplementovaná jako stub

31Sunday 13 May 2012