From fcfdbe71f5ffe2daa7689e9318b44fe6aca9131c Mon Sep 17 00:00:00 2001 From: Jeroen Meulemeester Date: Tue, 29 Dec 2015 01:19:46 +0100 Subject: [PATCH] Added tests for service-layer pattern --- service-layer/pom.xml | 5 + .../servicelayer/common/BaseEntity.java | 31 +++- .../servicelayer/hibernate/HibernateUtil.java | 63 +++++--- .../servicelayer/spell/SpellDaoImpl.java | 5 +- .../servicelayer/spellbook/Spellbook.java | 3 +- .../iluwatar/servicelayer/app/AppTest.java | 9 ++ .../servicelayer/common/BaseDaoTest.java | 123 ++++++++++++++++ .../magic/MagicServiceImplTest.java | 138 ++++++++++++++++++ .../servicelayer/spell/SpellDaoImplTest.java | 35 +++++ .../spellbook/SpellbookDaoImplTest.java | 35 +++++ .../wizard/WizardDaoImplTest.java | 35 +++++ 11 files changed, 453 insertions(+), 29 deletions(-) create mode 100644 service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java create mode 100644 service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java create mode 100644 service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java create mode 100644 service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java create mode 100644 service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java diff --git a/service-layer/pom.xml b/service-layer/pom.xml index b0a57cf5b..7991b61d4 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -22,5 +22,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java b/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java index d8c796427..ab0000922 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java @@ -12,8 +12,37 @@ import javax.persistence.Version; */ @MappedSuperclass @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) -public class BaseEntity { +public abstract class BaseEntity { @Version private Long version; + + /** + * Indicates the unique id of this entity + * + * @return The id of the entity, or 'null' when not persisted + */ + public abstract Long getId(); + + /** + * Set the id of this entity + * + * @param id The new id + */ + public abstract void setId(Long id); + + /** + * Get the name of this entity + * + * @return The name of the entity + */ + public abstract String getName(); + + /** + * Set the name of this entity + * + * @param name The new name + */ + public abstract void setName(final String name); + } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java index efc87dd15..9920a50df 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java @@ -1,41 +1,56 @@ package com.iluwatar.servicelayer.hibernate; -import org.hibernate.SessionFactory; -import org.hibernate.cfg.Configuration; - import com.iluwatar.servicelayer.spell.Spell; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.wizard.Wizard; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; + /** - * * Produces the Hibernate {@link SessionFactory}. - * */ public class HibernateUtil { - private static final SessionFactory SESSION_FACTORY; - - static { - try { - SESSION_FACTORY = - new Configuration().addAnnotatedClass(Wizard.class).addAnnotatedClass(Spellbook.class) - .addAnnotatedClass(Spell.class) - .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect") - .setProperty("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") - .setProperty("hibernate.current_session_context_class", "thread") - .setProperty("hibernate.show_sql", "true") - .setProperty("hibernate.hbm2ddl.auto", "create-drop").buildSessionFactory(); - } catch (Throwable ex) { - System.err.println("Initial SessionFactory creation failed." + ex); - throw new ExceptionInInitializerError(ex); - } - } + /** + * The cached session factory + */ + private static volatile SessionFactory sessionFactory; private HibernateUtil() { } - public static SessionFactory getSessionFactory() { - return SESSION_FACTORY; + /** + * Create the current session factory instance, create a new one when there is none yet. + * + * @return The session factory + */ + public static synchronized SessionFactory getSessionFactory() { + if (sessionFactory == null) { + try { + sessionFactory = + new Configuration().addAnnotatedClass(Wizard.class).addAnnotatedClass(Spellbook.class) + .addAnnotatedClass(Spell.class) + .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect") + .setProperty("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") + .setProperty("hibernate.current_session_context_class", "thread") + .setProperty("hibernate.show_sql", "true") + .setProperty("hibernate.hbm2ddl.auto", "create-drop").buildSessionFactory(); + } catch (Throwable ex) { + System.err.println("Initial SessionFactory creation failed." + ex); + throw new ExceptionInInitializerError(ex); + } + } + return sessionFactory; } + + /** + * Drop the current connection, resulting in a create-drop clean database next time. This is + * mainly used for JUnit testing since one test should not influence the other + */ + public static void dropSession() { + getSessionFactory().close(); + sessionFactory = null; + } + } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java index 66d06448b..708ba033e 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java @@ -1,12 +1,12 @@ package com.iluwatar.servicelayer.spell; +import com.iluwatar.servicelayer.common.DaoBaseImpl; + import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.criterion.Restrictions; -import com.iluwatar.servicelayer.common.DaoBaseImpl; - /** * * SpellDao implementation. @@ -24,7 +24,6 @@ public class SpellDaoImpl extends DaoBaseImpl implements SpellDao { Criteria criteria = session.createCriteria(persistentClass); criteria.add(Restrictions.eq("name", name)); result = (Spell) criteria.uniqueResult(); - result.getSpellbook().getWizards().size(); tx.commit(); } catch (Exception e) { if (tx != null) { diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java index 49d81a955..165dcdc22 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java @@ -6,6 +6,7 @@ import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToMany; @@ -50,7 +51,7 @@ public class Spellbook extends BaseEntity { private String name; - @ManyToMany(mappedBy = "spellbooks") + @ManyToMany(mappedBy = "spellbooks", fetch = FetchType.EAGER) private Set wizards; @OneToMany(mappedBy = "spellbook", orphanRemoval = true, cascade = CascadeType.ALL) diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java index 43e81d406..f92af7cff 100644 --- a/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java @@ -1,5 +1,8 @@ package com.iluwatar.servicelayer.app; +import com.iluwatar.servicelayer.hibernate.HibernateUtil; + +import org.junit.After; import org.junit.Test; /** @@ -14,4 +17,10 @@ public class AppTest { String[] args = {}; App.main(args); } + + @After + public void tearDown() throws Exception { + HibernateUtil.dropSession(); + } + } diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java new file mode 100644 index 000000000..1dabe117a --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java @@ -0,0 +1,123 @@ +package com.iluwatar.servicelayer.common; + +import com.iluwatar.servicelayer.hibernate.HibernateUtil; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Date: 12/28/15 - 10:53 PM + * + * @author Jeroen Meulemeester + */ +public abstract class BaseDaoTest> { + + /** + * The number of entities stored before each test + */ + private static final int INITIAL_COUNT = 5; + + /** + * The unique id generator, shared between all entities + */ + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); + + /** + * Factory, used to create new entity instances with the given name + */ + private final Function factory; + + /** + * The tested data access object + */ + private final D dao; + + /** + * Create a new test using the given factory and dao + * + * @param factory The factory, used to create new entity instances with the given name + * @param dao The tested data access object + */ + public BaseDaoTest(final Function factory, final D dao) { + this.factory = factory; + this.dao = dao; + } + + @Before + public void setUp() throws Exception { + for (int i = 0; i < INITIAL_COUNT; i++) { + final String className = dao.persistentClass.getSimpleName(); + final String entityName = String.format("%s%d", className, ID_GENERATOR.incrementAndGet()); + this.dao.persist(this.factory.apply(entityName)); + } + } + + @After + public void tearDown() throws Exception { + HibernateUtil.dropSession(); + } + + protected final D getDao() { + return this.dao; + } + + @Test + public void testFind() throws Exception { + final List all = this.dao.findAll(); + for (final E entity : all) { + final E byId = this.dao.find(entity.getId()); + assertNotNull(byId); + assertEquals(byId.getId(), byId.getId()); + } + } + + @Test + public void testDelete() throws Exception { + final List originalEntities = this.dao.findAll(); + this.dao.delete(originalEntities.get(1)); + this.dao.delete(originalEntities.get(2)); + + final List entitiesLeft = this.dao.findAll(); + assertNotNull(entitiesLeft); + assertEquals(INITIAL_COUNT - 2, entitiesLeft.size()); + } + + @Test + public void testFindAll() throws Exception { + final List all = this.dao.findAll(); + assertNotNull(all); + assertEquals(INITIAL_COUNT, all.size()); + } + + @Test + public void testSetId() throws Exception { + final E entity = this.factory.apply("name"); + assertNull(entity.getId()); + + final Long expectedId = Long.valueOf(1); + entity.setId(expectedId); + assertEquals(expectedId, entity.getId()); + } + + @Test + public void testSetName() throws Exception { + final E entity = this.factory.apply("name"); + assertEquals("name", entity.getName()); + assertEquals("name", entity.toString()); + + final String expectedName = "new name"; + entity.setName(expectedName); + assertEquals(expectedName, entity.getName()); + assertEquals(expectedName, entity.toString()); + } + +} \ No newline at end of file diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java new file mode 100644 index 000000000..48f3ae9d3 --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java @@ -0,0 +1,138 @@ +package com.iluwatar.servicelayer.magic; + +import com.iluwatar.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.SpellDao; +import com.iluwatar.servicelayer.spellbook.Spellbook; +import com.iluwatar.servicelayer.spellbook.SpellbookDao; +import com.iluwatar.servicelayer.wizard.Wizard; +import com.iluwatar.servicelayer.wizard.WizardDao; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 12:06 AM + * + * @author Jeroen Meulemeester + */ +public class MagicServiceImplTest { + + @Test + public void testFindAllWizards() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllWizards(); + verify(wizardDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindAllSpellbooks() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllSpellbooks(); + verify(spellbookDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindAllSpells() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllSpells(); + verify(spellDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindWizardsWithSpellbook() throws Exception { + final String bookname = "bookname"; + final Spellbook spellbook = mock(Spellbook.class); + final Set wizards = new HashSet<>(); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + + when(spellbook.getWizards()).thenReturn(wizards); + + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + when(spellbookDao.findByName(eq(bookname))).thenReturn(spellbook); + + final WizardDao wizardDao = mock(WizardDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao, spellbook); + + final List result = service.findWizardsWithSpellbook(bookname); + verify(spellbookDao).findByName(eq(bookname)); + verify(spellbook).getWizards(); + + assertNotNull(result); + assertEquals(3, result.size()); + + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindWizardsWithSpell() throws Exception { + final Set wizards = new HashSet<>(); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + + final Spellbook spellbook = mock(Spellbook.class); + when(spellbook.getWizards()).thenReturn(wizards); + + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final WizardDao wizardDao = mock(WizardDao.class); + + final Spell spell = mock(Spell.class); + when(spell.getSpellbook()).thenReturn(spellbook); + + final String spellName = "spellname"; + final SpellDao spellDao = mock(SpellDao.class); + when(spellDao.findByName(eq(spellName))).thenReturn(spell); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao, spellbook); + + final List result = service.findWizardsWithSpell(spellName); + verify(spellDao).findByName(eq(spellName)); + verify(spellbook).getWizards(); + + assertNotNull(result); + assertEquals(3, result.size()); + + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java new file mode 100644 index 000000000..99a8e142f --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java @@ -0,0 +1,35 @@ +package com.iluwatar.servicelayer.spell; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:02 PM + * + * @author Jeroen Meulemeester + */ +public class SpellDaoImplTest extends BaseDaoTest { + + public SpellDaoImplTest() { + super(Spell::new, new SpellDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final SpellDaoImpl dao = getDao(); + final List allSpells = dao.findAll(); + for (final Spell spell : allSpells) { + final Spell spellByName = dao.findByName(spell.getName()); + assertNotNull(spellByName); + assertEquals(spell.getId(), spellByName.getId()); + assertEquals(spell.getName(), spellByName.getName()); + } + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java new file mode 100644 index 000000000..fda46009e --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java @@ -0,0 +1,35 @@ +package com.iluwatar.servicelayer.spellbook; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:44 PM + * + * @author Jeroen Meulemeester + */ +public class SpellbookDaoImplTest extends BaseDaoTest { + + public SpellbookDaoImplTest() { + super(Spellbook::new, new SpellbookDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final SpellbookDaoImpl dao = getDao(); + final List allBooks = dao.findAll(); + for (final Spellbook book : allBooks) { + final Spellbook spellByName = dao.findByName(book.getName()); + assertNotNull(spellByName); + assertEquals(book.getId(), spellByName.getId()); + assertEquals(book.getName(), spellByName.getName()); + } + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java new file mode 100644 index 000000000..1812f4c36 --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java @@ -0,0 +1,35 @@ +package com.iluwatar.servicelayer.wizard; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:46 PM + * + * @author Jeroen Meulemeester + */ +public class WizardDaoImplTest extends BaseDaoTest { + + public WizardDaoImplTest() { + super(Wizard::new, new WizardDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final WizardDaoImpl dao = getDao(); + final List allWizards = dao.findAll(); + for (final Wizard spell : allWizards) { + final Wizard byName = dao.findByName(spell.getName()); + assertNotNull(byName); + assertEquals(spell.getId(), byName.getId()); + assertEquals(spell.getName(), byName.getName()); + } + } + +}