180 likes | 303 Views
Jednotkové testy pracující s databází. David Möhwald Pavel Groll. Osnova. JUnit – David Möhwald DbUnit – David Möhwald Přiklad použití DbUnit – Pavel Groll Best practices – David Möhwald Zdroje. JUnit ( testování jednotek programu ).
E N D
Jednotkové testy pracující s databází David Möhwald Pavel Groll
Osnova • JUnit – David Möhwald • DbUnit – David Möhwald • Přiklad použití DbUnit – Pavel Groll • Best practices – David Möhwald • Zdroje
JUnit (testování jednotek programu) • Základní myšlenkou je možnost testovat jednotlivé komponenty programu nezávisle na ostatních • Jednotkou se myslí třída nebo skupina (balík) třídposkytující jako celek určitou funkcionalitu navenek • Testy pracují tak, že si nejprve vytvoří prostředí složené z přípravků (fixture) objektů, které budou v testech figurovat, na nichž se budou spouštět testované metody a ověřovat jejich výsledky • Vytváření a spouštění testů je již dnes nedílnou součástí každého vývojového prostředí
Ukázka fragmentu JUnit testovací třídy k prověření třídy Stack public class StackTest extends JUnit.framework.TestCase { private Stack st; public StackTest(String testCaseName) { super(testCaseName); } // vytvoří přípravek, nastaví prostředí každého testu public void setUp() { st = new Stack(10); } // testuje prázdnost právě vytvořeného zásobníku public void testEmptyAfterCreation() { assertTrue("Stack should be empty after creation.", st.isEmpty()); } // testuje neprázdnost zásobníku po vložení prvku public void testPushPopEquals() { st.push("something"); assertEquals("What was pushed, must be popped...", "something", st.pop()); } ... // uklidí po testu, je-li třeba public void tearDown() { }
DbUnit (testy datové vrstvy) • DbUnit je rozšířením JUnit pro testování databázových aplikací • Pomáhá řešit problémy testování db aplikací jako je uvedení prostředí před testem dovhodného stavu a po testu je “uklidit” • Lze tedy vytvořit databázi, tabulky, naplnit je daty např z XML, XLS nebo jiných zdrojů,spustit testovaný kód (který může data totálně zničit) a po skončení testu uvést opět dokonzistentního stavu • DbUnit umožňují export a import dat do a zXML datasetu. Od verze 2.0, mohou pracovat s velmi velkým objemem dat (datesetem) při použití streaming modu • DbUnit umožňují také ověřovat že jsou v DB data,která odpovídají stanoveným hodnotám
Core Components • IDatabaseConnection – Interface reprezentující DbUnit spojení s DB. • IdataSet - interface reprezentujcící kolekci tabulek a práci s daty (abstract)
Core Components • DatabaseOperation –abstraktní třída reprezentujícíoperace, které jsou spoušteny před a po testu. Dvě nevíce užitečné operace jsouREFRESH a CLEAN_INSERT
DbUnit - jak začít ... • Vytvořte vlastní dataset soubor. Můžete si vytvořit manuálně Flat XML dokument nebo vyexportovat data z DB • Vytvořte testovací třídu rozšířením DBTestCase class, která rozšiřuje Junit TestCase class. Je nutné implementovat metodu getDataSet() pro vrácení datasetu vytvořeného v předchozím kroku • (Optional) implementujte getSetUpOperation() a getTearDownOperation() metody. Defaultně je spouštěna CLEAN_INSERT operace • Implementujte vlastní testovací metody jako normální Junit DbUnit poskytují metody pro porovnání obsahu tabulek nebo datasetů public class Assertion { public static void assertEquals(ITable expected, ITable actual) public static void assertEquals(IDataSet expected, IDataSet actual) }
Příklad použití DbUnit • XML datafile • Metody getConnection() a getDataSet() • Metody getSetUp() a getTearDown() • public void createEmployee( EmployeeValueObject emplVo ) • public EmployeeValueObject getEmployeeBySocialSecNum( String ssn ) • public void updateEmployee( EmployeeValueObject emplVo ) • public void deleteEmployee( EmployeeValueObject emplVo )
XML datafile <?xml version='1.0' encoding='UTF-8'?> <dataset> <EMPLOYEE employee_uid='1' start_date='2001-01-01' first_name='Drew' ssn='333-29-9999' last_name='Smith' /> <EMPLOYEE employee_uid='2' start_date='2002-04-04' first_name='Nick' ssn='222-90-1111' last_name='Marquiss' /> <EMPLOYEE employee_uid='3' start_date='2003-06-03' first_name='Jose' ssn='111-67-2222' last_name='Whitson' /> </dataset>
getConnection() a getDataSet() protected IDatabaseConnection getConnection() throws Exception { Class driverClass = Class.forName("org.gjt.mm.mysql.Driver"); Connection jdbcConnection = DriverManager.getConnection( "jdbc:mysql://127.0.0.1/hr", "hr", "hr"); return new DatabaseConnection(jdbcConnection); } protected IDataSet getDataSet() throws Exception { return new FlatXmlDataSet( new FileInputStream("hr-seed.xml")); }
setUp a tearDown protected DatabaseOperation getSetUpOperation() throws Exception { return DatabaseOperation.REFRESH; } protected DatabaseOperation getTearDownOperation() throws Exception { return DatabaseOperation.NONE; }
Test vyhledání public void testFindBySSN() throws Exception{ EmployeeFacade facade = //obtain somehow EmployeeValueObject vo = facade.getEmployeeBySocialSecNum("333-29-9999"); TestCase.assertNotNull("vo shouldn't be null", vo); TestCase.assertEquals("should be Drew", "Drew", vo.getFirstName()); TestCase.assertEquals("should be Smith", "Smith", vo.getLastName()); }
Test vytvoření public void testEmployeeCreate() throws Exception{ EmployeeValueObject empVo = new EmployeeValueObject(); empVo.setFirstName("Noah"); empVo.setLastName("Awan"); empVo.setSSN("564-55-5555"); EmployeeFacade empFacade = //obtain from somewhere empFacade.createEmployee(empVo); //perform a find by ssn to ensure existence }
Test update public void testUpdateEmployee() throws Exception{ EmployeeFacade facade = //obtain façade EmployeeValueObject vo = facade.getEmployeeBySocialSecNum("111-67-2222"); TestCase.assertNotNull("vo was null", vo); TestCase.assertEquals("first name should be Jose", "Jose", vo.getFirstName()); vo.setFirstName("Ramon"); facade.updateEmployee(vo); EmployeeValueObject newVo = facade.getEmployeeBySocialSecNum("111-67-2222"); TestCase.assertNotNull("vo was null", newVo); TestCase.assertEquals("name should be Ramon", "Ramon", newVo.getFirstName()); }
Test odstranění public void testDeleteEmployee() throws Exception{ EmployeeFacade facade = //obtain façade EmployeeValueObject vo = facade.getEmployeeBySocialSecNum("222-90-1111"); TestCase.assertNotNull("vo was null", vo); facade.deleteEmployee(vo); try{ EmployeeValueObject newVo = facade.getEmployeeBySocialSecNum("222-90-1111"); TestCase.fail("returned removed employee"); }catch(Exception e){ //ignore } }
Best practices • Uveďte Db před testem vždy do očekávaného stavu a nespouštějte více testů v jeden okamžik • Pokud pracuje na projektu více vývojářů, je snadnější, aby si každý udržoval svou lokální instanci DB • Vyhněte se závislostem na výsledcích jiného testu. Pokud se řídíte pravidlem jedna, potom nepotřebujete po testu uklízet • Snažte se nevytvářet jeden velký dataset – rozdělte jej např. podle logických celků • Pokud více testů používá jeden dataset, měl by být nahráván pouze jednou • Snažte se využívat DB pooling místo vytváření jednotlivých DB spojení
Zdroje • DbUnithttp://dbunit.sourceforge.net • Effective Unit Testing with DbUnithttp://www.onjava.com/pub/a/onjava/2004/01/21/dbunit.html • Control your test-environment with DbUnit and Anthillhttp://www.ibm.com/developerworks/java/library/j-dbunit.html • Unit testing database codehttp://www.dallaway.com/acad/dbunit.html • IDE Tutorials CSE 219 Fall 2005http://www.fsl.cs.sunysb.edu/~dquigley/cse219/index.php?it=eclipse&tt=junit&pf=y