From 139321fa42f420b35f937830ac5a4be6dcea2f64 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Tue, 23 Aug 2011 01:45:46 +0300 Subject: [PATCH 01/32] first unit test --- .../devclub/model/PhotoSpotServiceTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/ee/devclub/model/PhotoSpotServiceTest.java diff --git a/test/ee/devclub/model/PhotoSpotServiceTest.java b/test/ee/devclub/model/PhotoSpotServiceTest.java new file mode 100644 index 0000000..df0a5e0 --- /dev/null +++ b/test/ee/devclub/model/PhotoSpotServiceTest.java @@ -0,0 +1,31 @@ +package ee.devclub.model; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.orm.hibernate3.HibernateOperations; + +import java.util.List; + +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class PhotoSpotServiceTest { + PhotoSpotService service = new PhotoSpotService(); + + @Before + public void setUp() throws Exception { + service.hibernate = mock(HibernateOperations.class); + } + + @Test + public void allPhotoSpotsComeFromDB() throws Exception { + when(service.hibernate.loadAll(PhotoSpot.class)).thenReturn(asList(new PhotoSpot("Kohtuotsa", "", new Location(59.437755f, 24.74209f)))); + + List spots = service.getAllSpots(); + assertThat(spots.size(), is(1)); + assertThat(spots.get(0).name, is("Kohtuotsa")); + } +} From 3791722f9944733072b9cf4d0e4c34b131f6abea Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Tue, 23 Aug 2011 19:30:49 +0300 Subject: [PATCH 02/32] commented out code removed --- build.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.xml b/build.xml index 9dab332..329724f 100644 --- a/build.xml +++ b/build.xml @@ -2,10 +2,6 @@ - - - - From ae7cda4e01480cdb1a074eefabe34edbbb041e5d Mon Sep 17 00:00:00 2001 From: Jevgeni Holodkov Date: Sat, 29 Oct 2011 13:29:22 +0300 Subject: [PATCH 03/32] Update retrieve pattern, otherwise you can get java.lang.RuntimeException: Multiple artifacts of the module org.mockito#mockito-all;1.8.4 are retrieved to the same file! Update the retrieve pattern to fix this error. at org.apache.ivy.core.retrieve.RetrieveEngine.retrieve(RetrieveEngine.java:206) when have dependencies like: --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 329724f..d918a07 100644 --- a/build.xml +++ b/build.xml @@ -3,7 +3,7 @@ - + From 6c2aa3ea25381110fdc7cb384e4fe3b1d3d4cfcc Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sun, 20 Nov 2011 13:05:04 +0200 Subject: [PATCH 04/32] service renamed to repository for semantical correctness --- ...ServiceTest.java => PhotoSpotRepositoryTest.java} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename test/ee/devclub/model/{PhotoSpotServiceTest.java => PhotoSpotRepositoryTest.java} (58%) diff --git a/test/ee/devclub/model/PhotoSpotServiceTest.java b/test/ee/devclub/model/PhotoSpotRepositoryTest.java similarity index 58% rename from test/ee/devclub/model/PhotoSpotServiceTest.java rename to test/ee/devclub/model/PhotoSpotRepositoryTest.java index df0a5e0..0fb0b03 100644 --- a/test/ee/devclub/model/PhotoSpotServiceTest.java +++ b/test/ee/devclub/model/PhotoSpotRepositoryTest.java @@ -12,19 +12,19 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class PhotoSpotServiceTest { - PhotoSpotService service = new PhotoSpotService(); +public class PhotoSpotRepositoryTest { + PhotoSpotRepository repo = new PhotoSpotRepository(); @Before - public void setUp() throws Exception { - service.hibernate = mock(HibernateOperations.class); + public void initMocks() throws Exception { + repo.hibernate = mock(HibernateOperations.class); } @Test public void allPhotoSpotsComeFromDB() throws Exception { - when(service.hibernate.loadAll(PhotoSpot.class)).thenReturn(asList(new PhotoSpot("Kohtuotsa", "", new Location(59.437755f, 24.74209f)))); + when(repo.hibernate.loadAll(PhotoSpot.class)).thenReturn(asList(new PhotoSpot("Kohtuotsa", "", new Location(59.437755f, 24.74209f)))); - List spots = service.getAllSpots(); + List spots = repo.getAllSpots(); assertThat(spots.size(), is(1)); assertThat(spots.get(0).name, is("Kohtuotsa")); } From f09c74e079ba63bec1aef52b4d0056331746472c Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sun, 20 Nov 2011 13:12:59 +0200 Subject: [PATCH 05/32] photo resource is now also tested --- .../devclub/rest/PhotoSpotResourceTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/ee/devclub/rest/PhotoSpotResourceTest.java diff --git a/test/ee/devclub/rest/PhotoSpotResourceTest.java b/test/ee/devclub/rest/PhotoSpotResourceTest.java new file mode 100644 index 0000000..d9696d0 --- /dev/null +++ b/test/ee/devclub/rest/PhotoSpotResourceTest.java @@ -0,0 +1,34 @@ +package ee.devclub.rest; + +import ee.devclub.model.Location; +import ee.devclub.model.PhotoSpot; +import ee.devclub.model.PhotoSpotRepository; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.*; + +public class PhotoSpotResourceTest { + PhotoSpotResource resource = new PhotoSpotResource(); + + @Before + public void initMocks() throws Exception { + resource.repo = mock(PhotoSpotRepository.class); + } + + @Test + public void newPhotoSpotsArePersisted() throws Exception { + resource.newPhotoSpot("Aegna island", "WWI defence structures", 59.583771f, 24.749720f); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PhotoSpot.class); + verify(resource.repo).persist(captor.capture()); + + PhotoSpot spot = captor.getValue(); + assertThat(spot.getName(), is("Aegna island")); + assertThat(spot.getDescription(), is("WWI defence structures")); + assertThat(spot.getLocation(), is(new Location(59.583771f, 24.749720f))); + } +} From 9b1323ffbd6788c034dbba3bc8543cf998351cda Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sun, 20 Nov 2011 18:24:39 +0200 Subject: [PATCH 06/32] more info in README --- README | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/README b/README index 7e2e73c..8a5095c 100644 --- a/README +++ b/README @@ -1 +1,33 @@ -This project has some branches that can be merged into your project to add skeleton stuff. +This is a skeleton project for a Java webapp, done right. + +It includes: +- Ant & Ivy for dependency management +- Jersey for handling of http requests in a RESTful way +- Spring for dependency injection in the code +- H2 database (Windows users may need to edit jetty-web.xml) +- Liquibase for DB migration +- Hibernate for working with the DB +- JUnit & Mockito for unit testing + +After cloning, run src/Launcher.java to start. Everything is preconfigured. + +Small amount of code it contains implements a simple database of good Photo Spots, +which uses google maps to browse and edit them. + +Or you can follow how this all have been created by pulling various branches +into your own branch in this order: +- ivy +- launcher +- map +- web.xml +- logging +- jersey +- map-with-spots +- add-spot +- spring +- db +- hibernate +- tests + +See related talk from GOTOCon and Devclub here: +http://www.slideshare.net/antonkeks/simplicity-8971441 From 03a1707be18719e591f69774e793be9c8361a723 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sun, 20 Nov 2011 18:24:39 +0200 Subject: [PATCH 07/32] more info in README --- README | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/README b/README index 7e2e73c..8a5095c 100644 --- a/README +++ b/README @@ -1 +1,33 @@ -This project has some branches that can be merged into your project to add skeleton stuff. +This is a skeleton project for a Java webapp, done right. + +It includes: +- Ant & Ivy for dependency management +- Jersey for handling of http requests in a RESTful way +- Spring for dependency injection in the code +- H2 database (Windows users may need to edit jetty-web.xml) +- Liquibase for DB migration +- Hibernate for working with the DB +- JUnit & Mockito for unit testing + +After cloning, run src/Launcher.java to start. Everything is preconfigured. + +Small amount of code it contains implements a simple database of good Photo Spots, +which uses google maps to browse and edit them. + +Or you can follow how this all have been created by pulling various branches +into your own branch in this order: +- ivy +- launcher +- map +- web.xml +- logging +- jersey +- map-with-spots +- add-spot +- spring +- db +- hibernate +- tests + +See related talk from GOTOCon and Devclub here: +http://www.slideshare.net/antonkeks/simplicity-8971441 From 0574c12c76a09fdb460ed6e4f70eb935af5edbed Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Tue, 13 Mar 2012 15:44:26 +0200 Subject: [PATCH 08/32] jar path updated after ivy change --- h2.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h2.sh b/h2.sh index 25b44b2..96d60ce 100755 --- a/h2.sh +++ b/h2.sh @@ -1,4 +1,4 @@ #!/bin/bash # This script will give you H2 database shell -java -cp lib/h2*.jar org.h2.tools.Shell -url 'jdbc:h2:~/.devclub-java/db;AUTO_SERVER=TRUE;USER=devclub;PASSWORD=devclub' -user devclub -password devclub +java -cp lib/default/h2*.jar org.h2.tools.Shell -url 'jdbc:h2:~/.devclub-java/db;AUTO_SERVER=TRUE;USER=devclub;PASSWORD=devclub' -user devclub -password devclub From 592fc0b0885b84357959496836c58496cb0a4a6e Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Wed, 9 May 2012 23:06:15 +0300 Subject: [PATCH 09/32] extracted interface for PhotoSpotRepository --- .../model/HibernatePhotoSpotRepository.java | 23 +++++++++++++++++++ src/ee/devclub/model/PhotoSpotRepository.java | 18 +++------------ ... => HibernatePhotoSpotRepositoryTest.java} | 4 ++-- 3 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 src/ee/devclub/model/HibernatePhotoSpotRepository.java rename test/ee/devclub/model/{PhotoSpotRepositoryTest.java => HibernatePhotoSpotRepositoryTest.java} (87%) diff --git a/src/ee/devclub/model/HibernatePhotoSpotRepository.java b/src/ee/devclub/model/HibernatePhotoSpotRepository.java new file mode 100644 index 0000000..424dc38 --- /dev/null +++ b/src/ee/devclub/model/HibernatePhotoSpotRepository.java @@ -0,0 +1,23 @@ +package ee.devclub.model; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.orm.hibernate3.HibernateOperations; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public class HibernatePhotoSpotRepository implements PhotoSpotRepository { + @Autowired HibernateOperations hibernate; + + @Override + public List getAllSpots() { + return hibernate.loadAll(PhotoSpot.class); + } + + @Override + public PhotoSpot persist(PhotoSpot spot) { + hibernate.saveOrUpdate(spot); + return spot; + } +} diff --git a/src/ee/devclub/model/PhotoSpotRepository.java b/src/ee/devclub/model/PhotoSpotRepository.java index 2ba89a6..e1e6af8 100644 --- a/src/ee/devclub/model/PhotoSpotRepository.java +++ b/src/ee/devclub/model/PhotoSpotRepository.java @@ -1,21 +1,9 @@ package ee.devclub.model; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.orm.hibernate3.HibernateOperations; -import org.springframework.stereotype.Repository; - import java.util.List; -@Repository -public class PhotoSpotRepository { - @Autowired HibernateOperations hibernate; - - public List getAllSpots() { - return hibernate.loadAll(PhotoSpot.class); - } +public interface PhotoSpotRepository { + List getAllSpots(); - public PhotoSpot persist(PhotoSpot spot) { - hibernate.saveOrUpdate(spot); - return spot; - } + PhotoSpot persist(PhotoSpot spot); } diff --git a/test/ee/devclub/model/PhotoSpotRepositoryTest.java b/test/ee/devclub/model/HibernatePhotoSpotRepositoryTest.java similarity index 87% rename from test/ee/devclub/model/PhotoSpotRepositoryTest.java rename to test/ee/devclub/model/HibernatePhotoSpotRepositoryTest.java index 0fb0b03..7ed20fa 100644 --- a/test/ee/devclub/model/PhotoSpotRepositoryTest.java +++ b/test/ee/devclub/model/HibernatePhotoSpotRepositoryTest.java @@ -12,8 +12,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class PhotoSpotRepositoryTest { - PhotoSpotRepository repo = new PhotoSpotRepository(); +public class HibernatePhotoSpotRepositoryTest { + HibernatePhotoSpotRepository repo = new HibernatePhotoSpotRepository(); @Before public void initMocks() throws Exception { From 8bd6ecf378d0a73059a68ee07c3d4a7a3bd0201d Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Wed, 9 May 2012 23:15:52 +0300 Subject: [PATCH 10/32] jdbc version of photospotrepository added (for demo purposes) --- .../model/JDBCPhotoSpotRepository.java | 66 +++++++++++++++++++ src/ee/devclub/model/Location.java | 4 +- .../model/JDBCPhotoSpotRepositoryTest.java | 50 ++++++++++++++ 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/ee/devclub/model/JDBCPhotoSpotRepository.java create mode 100644 test/ee/devclub/model/JDBCPhotoSpotRepositoryTest.java diff --git a/src/ee/devclub/model/JDBCPhotoSpotRepository.java b/src/ee/devclub/model/JDBCPhotoSpotRepository.java new file mode 100644 index 0000000..c0b7d26 --- /dev/null +++ b/src/ee/devclub/model/JDBCPhotoSpotRepository.java @@ -0,0 +1,66 @@ +package ee.devclub.model; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.orm.hibernate3.HibernateOperations; +import org.springframework.stereotype.Repository; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class JDBCPhotoSpotRepository implements PhotoSpotRepository { + @Autowired DataSource dataSource; + + public List getAllSpots() { + Connection conn = null; + try { + List spots = new ArrayList(); + conn = dataSource.getConnection(); + ResultSet rs = conn.prepareStatement("select * from PhotoSpot").executeQuery(); + while (rs.next()) { + spots.add(new PhotoSpot(rs.getString("name"), rs.getString("description"), + new Location(rs.getFloat("latitude"), rs.getFloat("longitude")))); + } + return spots; + } + catch (SQLException e) { + throw new RuntimeException(e); + } + finally { + closeSilently(conn); + } + } + + public PhotoSpot persist(PhotoSpot spot) { + Connection conn = null; + try { + conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement("insert into PhotoSpot (name, description, latitude, longitude) values (?, ?, ?, ?)"); + stmt.setString(1, spot.name); + stmt.setString(2, spot.description); + stmt.setFloat(3, spot.location.latitude); + stmt.setFloat(4, spot.location.longitude); + stmt.execute(); + return spot; + } + catch (SQLException e) { + throw new RuntimeException(e); + } + finally { + closeSilently(conn); + } + } + + private void closeSilently(Connection conn) { + try { + if (conn != null) conn.close(); + } + catch (SQLException ignore) { + } + } +} diff --git a/src/ee/devclub/model/Location.java b/src/ee/devclub/model/Location.java index 1f79f3f..b8bba2c 100644 --- a/src/ee/devclub/model/Location.java +++ b/src/ee/devclub/model/Location.java @@ -5,8 +5,8 @@ @Embeddable public class Location implements Serializable { - private float latitude; - private float longitude; + float latitude; + float longitude; public Location() { } diff --git a/test/ee/devclub/model/JDBCPhotoSpotRepositoryTest.java b/test/ee/devclub/model/JDBCPhotoSpotRepositoryTest.java new file mode 100644 index 0000000..f6dadf2 --- /dev/null +++ b/test/ee/devclub/model/JDBCPhotoSpotRepositoryTest.java @@ -0,0 +1,50 @@ +package ee.devclub.model; + +import org.junit.Before; +import org.junit.Test; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class JDBCPhotoSpotRepositoryTest { + JDBCPhotoSpotRepository repository = new JDBCPhotoSpotRepository(); + + @Before + public void setUp() throws Exception { + repository.dataSource = mock(DataSource.class, RETURNS_DEEP_STUBS); + } + + @Test + public void spotFieldsAreCorrectlyMappedToDBColumns() throws Exception { + ResultSet rs = repository.dataSource.getConnection().prepareStatement("select * from PhotoSpot").executeQuery(); + when(rs.next()).thenReturn(true, false); + + when(rs.getString("name")).thenReturn("Kohtuotsa"); + when(rs.getString("description")).thenReturn("Mega place!"); + when(rs.getFloat("latitude")).thenReturn(59.437755f); + when(rs.getFloat("longitude")).thenReturn(24.74209f); + + List spots = repository.getAllSpots(); + + assertEquals(1, spots.size()); + PhotoSpot spot = spots.get(0); + + assertThat(spot.name, is("Kohtuotsa")); + assertThat(spot.description, is("Mega place!")); + assertThat(spot.location, is(new Location(59.437755f, 24.74209f))); + + verify(repository.dataSource.getConnection()).close(); + } + + @Test + public void manySpotFieldsCanBeLoaded() throws Exception { + ResultSet rs = repository.dataSource.getConnection().prepareStatement("select * from PhotoSpot").executeQuery(); + when(rs.next()).thenReturn(true, true, true, false); + assertThat(repository.getAllSpots().size(), is(3)); + } +} From 240400533f093ca61971c20884afb0c0862e63f6 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Thu, 10 May 2012 00:02:43 +0300 Subject: [PATCH 11/32] jdbc integration test added --- src/ee/devclub/model/PhotoSpot.java | 16 ++++++ ...DBCPhotoSpotRepositoryIntegrationTest.java | 50 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java diff --git a/src/ee/devclub/model/PhotoSpot.java b/src/ee/devclub/model/PhotoSpot.java index 34729c8..a7eeabb 100644 --- a/src/ee/devclub/model/PhotoSpot.java +++ b/src/ee/devclub/model/PhotoSpot.java @@ -31,4 +31,20 @@ public String getDescription() { public Location getLocation() { return location; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PhotoSpot photoSpot = (PhotoSpot) o; + return !(description != null ? !description.equals(photoSpot.description) : photoSpot.description != null) && !(id != null ? !id.equals(photoSpot.id) : photoSpot.id != null) && !(location != null ? !location.equals(photoSpot.location) : photoSpot.location != null) && !(name != null ? !name.equals(photoSpot.name) : photoSpot.name != null); + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (name != null ? name.hashCode() : 0); + return result; + } } diff --git a/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java b/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java new file mode 100644 index 0000000..2612507 --- /dev/null +++ b/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java @@ -0,0 +1,50 @@ +package ee.devclub.model; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.jdbc.datasource.DriverManagerDataSource; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +public class JDBCPhotoSpotRepositoryIntegrationTest { + DataSource dataSource; + Connection conn; + JDBCPhotoSpotRepository repo = new JDBCPhotoSpotRepository(); + + @Before + public void setUp() throws Exception { + dataSource = new DriverManagerDataSource("jdbc:h2:mem:test", "sa", "sa"); + conn = dataSource.getConnection(); + + conn.createStatement().execute("create table PhotoSpot (id int auto_increment primary key, name varchar, description varchar, latitude float, longitude float)"); + conn.createStatement().execute("insert into PhotoSpot values (1, 'Kohtuotsa', 'Mega place!', 59.437755, 24.74209)"); + + repo.dataSource = dataSource; + } + + @Test + public void loading() throws Exception { + List spots = repo.getAllSpots(); + assertThat(spots.size(), is(1)); + + PhotoSpot spot = spots.get(0); + assertThat(spot.name, is("Kohtuotsa")); + assertThat(spot.description, is("Mega place!")); + assertThat(spot.location, is(new Location(59.437755f, 24.74209f))); + } + + @Test + public void fullCycle() throws Exception { + PhotoSpot spot = new PhotoSpot("Teletorn", "Tallinn TV Tower", new Location(59.47111f, 24.8875f)); + repo.persist(spot); + + PhotoSpot spot2 = repo.getAllSpots().get(1); + assertThat(spot2, not(sameInstance(spot))); + assertThat(spot2, equalTo(spot)); + } +} From cec99a916c0e33dcd1723b972d6940fbfabf45e5 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Thu, 10 May 2012 00:51:54 +0300 Subject: [PATCH 12/32] hibernate integration test added --- ...atePhotoSpotRepositoryIntegrationTest.java | 58 +++++++++++++++++++ ...DBCPhotoSpotRepositoryIntegrationTest.java | 8 ++- .../model/JDBCPhotoSpotRepositoryTest.java | 14 ++--- 3 files changed, 72 insertions(+), 8 deletions(-) create mode 100644 test/ee/devclub/model/HibernatePhotoSpotRepositoryIntegrationTest.java diff --git a/test/ee/devclub/model/HibernatePhotoSpotRepositoryIntegrationTest.java b/test/ee/devclub/model/HibernatePhotoSpotRepositoryIntegrationTest.java new file mode 100644 index 0000000..b8a5d9a --- /dev/null +++ b/test/ee/devclub/model/HibernatePhotoSpotRepositoryIntegrationTest.java @@ -0,0 +1,58 @@ +package ee.devclub.model; + +import org.hibernate.dialect.H2Dialect; +import org.junit.Before; +import org.junit.Test; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.hibernate3.HibernateTemplate; +import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.util.List; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +public class HibernatePhotoSpotRepositoryIntegrationTest { + DataSource dataSource; + HibernatePhotoSpotRepository repo = new HibernatePhotoSpotRepository(); + + @Before + public void setUp() throws Exception { + dataSource = new DriverManagerDataSource("jdbc:h2:mem:hibernate;DB_CLOSE_DELAY=-1", "sa", "sa"); + + System.setProperty("hibernate.dialect", H2Dialect.class.getName()); + System.setProperty("hibernate.hbm2ddl.auto", "create-drop"); + AnnotationSessionFactoryBean sessionFactory = new AnnotationSessionFactoryBean(); + sessionFactory.setDataSource(dataSource); + sessionFactory.setAnnotatedClasses(new Class[] {PhotoSpot.class}); + sessionFactory.afterPropertiesSet(); + + repo.hibernate = new HibernateTemplate(sessionFactory.getObject()); + } + + @Test + public void loading() throws Exception { + dataSource.getConnection().createStatement().execute("insert into PhotoSpot (id, name, description, latitude, longitude) values (1, 'Kohtuotsa', 'Mega place!', 59.437755, 24.74209)"); + + List spots = repo.getAllSpots(); + assertThat(spots.size(), is(1)); + + PhotoSpot spot = spots.get(0); + assertThat(spot.name, is("Kohtuotsa")); + assertThat(spot.description, is("Mega place!")); + assertThat(spot.location, is(new Location(59.437755f, 24.74209f))); + } + + @Test + public void fullCycle() throws Exception { + PhotoSpot spot = new PhotoSpot("Teletorn", "Tallinn TV Tower", new Location(59.47111f, 24.8875f)); + repo.persist(spot); + repo.hibernate.clear(); + + PhotoSpot spot2 = repo.getAllSpots().get(0); + assertThat(spot2, not(sameInstance(spot))); + assertThat(spot2, equalTo(spot)); + } +} diff --git a/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java b/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java index 2612507..9780c68 100644 --- a/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java +++ b/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java @@ -1,5 +1,6 @@ package ee.devclub.model; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.jdbc.datasource.DriverManagerDataSource; @@ -17,7 +18,7 @@ public class JDBCPhotoSpotRepositoryIntegrationTest { JDBCPhotoSpotRepository repo = new JDBCPhotoSpotRepository(); @Before - public void setUp() throws Exception { + public void initMockDB() throws Exception { dataSource = new DriverManagerDataSource("jdbc:h2:mem:test", "sa", "sa"); conn = dataSource.getConnection(); @@ -27,6 +28,11 @@ public void setUp() throws Exception { repo.dataSource = dataSource; } + @After + public void destroyMockDB() throws Exception { + conn.close(); + } + @Test public void loading() throws Exception { List spots = repo.getAllSpots(); diff --git a/test/ee/devclub/model/JDBCPhotoSpotRepositoryTest.java b/test/ee/devclub/model/JDBCPhotoSpotRepositoryTest.java index f6dadf2..7c2da7d 100644 --- a/test/ee/devclub/model/JDBCPhotoSpotRepositoryTest.java +++ b/test/ee/devclub/model/JDBCPhotoSpotRepositoryTest.java @@ -12,16 +12,16 @@ import static org.mockito.Mockito.*; public class JDBCPhotoSpotRepositoryTest { - JDBCPhotoSpotRepository repository = new JDBCPhotoSpotRepository(); + JDBCPhotoSpotRepository repo = new JDBCPhotoSpotRepository(); @Before public void setUp() throws Exception { - repository.dataSource = mock(DataSource.class, RETURNS_DEEP_STUBS); + repo.dataSource = mock(DataSource.class, RETURNS_DEEP_STUBS); } @Test public void spotFieldsAreCorrectlyMappedToDBColumns() throws Exception { - ResultSet rs = repository.dataSource.getConnection().prepareStatement("select * from PhotoSpot").executeQuery(); + ResultSet rs = repo.dataSource.getConnection().prepareStatement("select * from PhotoSpot").executeQuery(); when(rs.next()).thenReturn(true, false); when(rs.getString("name")).thenReturn("Kohtuotsa"); @@ -29,7 +29,7 @@ public void spotFieldsAreCorrectlyMappedToDBColumns() throws Exception { when(rs.getFloat("latitude")).thenReturn(59.437755f); when(rs.getFloat("longitude")).thenReturn(24.74209f); - List spots = repository.getAllSpots(); + List spots = repo.getAllSpots(); assertEquals(1, spots.size()); PhotoSpot spot = spots.get(0); @@ -38,13 +38,13 @@ public void spotFieldsAreCorrectlyMappedToDBColumns() throws Exception { assertThat(spot.description, is("Mega place!")); assertThat(spot.location, is(new Location(59.437755f, 24.74209f))); - verify(repository.dataSource.getConnection()).close(); + verify(repo.dataSource.getConnection()).close(); } @Test public void manySpotFieldsCanBeLoaded() throws Exception { - ResultSet rs = repository.dataSource.getConnection().prepareStatement("select * from PhotoSpot").executeQuery(); + ResultSet rs = repo.dataSource.getConnection().prepareStatement("select * from PhotoSpot").executeQuery(); when(rs.next()).thenReturn(true, true, true, false); - assertThat(repository.getAllSpots().size(), is(3)); + assertThat(repo.getAllSpots().size(), is(3)); } } From 6aa8e8702e3b5420aa197bf0660ac5b49eed1d03 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Thu, 10 May 2012 22:45:39 +0300 Subject: [PATCH 13/32] oracle xe is now used, dbunit package added for pl/sql unit tests --- src/ee/devclub/model/PhotoSpot.java | 7 +++- webapp/WEB-INF/jetty-web.xml | 3 +- webapp/WEB-INF/liquibase-test.xml | 64 +++++++++++++++++++++++++++++ webapp/WEB-INF/liquibase.xml | 11 ++++- 4 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 webapp/WEB-INF/liquibase-test.xml diff --git a/src/ee/devclub/model/PhotoSpot.java b/src/ee/devclub/model/PhotoSpot.java index a7eeabb..82f5197 100644 --- a/src/ee/devclub/model/PhotoSpot.java +++ b/src/ee/devclub/model/PhotoSpot.java @@ -2,9 +2,12 @@ import javax.persistence.*; -@Entity @Access(AccessType.FIELD) +import static javax.persistence.AccessType.FIELD; +import static javax.persistence.GenerationType.AUTO; + +@Entity @Access(FIELD) public class PhotoSpot { - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = AUTO) @Id Long id; String name; diff --git a/webapp/WEB-INF/jetty-web.xml b/webapp/WEB-INF/jetty-web.xml index 48ab23d..963aaf1 100644 --- a/webapp/WEB-INF/jetty-web.xml +++ b/webapp/WEB-INF/jetty-web.xml @@ -8,7 +8,8 @@ true - jdbc:h2:~/.devclub-java/db;AUTO_SERVER=TRUE;USER=devclub;PASSWORD=devclub + + jdbc:oracle:thin:demo/demo@localhost:1521:xe 5 5 true diff --git a/webapp/WEB-INF/liquibase-test.xml b/webapp/WEB-INF/liquibase-test.xml new file mode 100644 index 0000000..6a67d4f --- /dev/null +++ b/webapp/WEB-INF/liquibase-test.xml @@ -0,0 +1,64 @@ + + + + + create or replace package dbunit + is + procedure assert_equals(expected number, actual number); + procedure assert_equals(expected varchar2, actual varchar2); + procedure assert_null(actual varchar2); + procedure assert_not_null(actual varchar2); + end; + / + create or replace public synonym dbunit for dbunit/ + grant execute on dbunit to public/ + + + + + + create or replace package body dbunit + is + procedure fail(message varchar2) is + begin + raise_application_error(-20000, 'Assertion failed' || chr(10) || message); + end; + + procedure fail(expected varchar2, actual varchar2) is + begin + fail('Expected: ' || expected || chr(10) || 'Actual: ' || actual); + end; + + procedure assert_equals(expected number, actual number) is + begin + if expected != actual then + fail(expected, actual); + end if; + end; + + procedure assert_equals(expected varchar2, actual varchar2) is + begin + if expected != actual then + fail(expected, actual); + end if; + end; + + procedure assert_null(actual varchar2) is + begin + if actual is not null then + fail(actual || ' is not null'); + end if; + end; + + procedure assert_not_null(actual varchar2) is + begin + if actual is null then + fail('argument is null'); + end if; + end; + end; + + + + diff --git a/webapp/WEB-INF/liquibase.xml b/webapp/WEB-INF/liquibase.xml index 4aec211..7794438 100644 --- a/webapp/WEB-INF/liquibase.xml +++ b/webapp/WEB-INF/liquibase.xml @@ -1,14 +1,20 @@ + + + + + + - + - + @@ -19,6 +25,7 @@ + From a5791f6708ac05b238a7d31207a9137e7d6f6cc3 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Thu, 10 May 2012 23:04:02 +0300 Subject: [PATCH 14/32] move dbunit pkh & pkb into separate files --- webapp/WEB-INF/db/dbunit.pkb | 40 ++++++++++++++++++++ webapp/WEB-INF/db/dbunit.pkh | 7 ++++ webapp/WEB-INF/liquibase-test.xml | 61 ++++--------------------------- 3 files changed, 55 insertions(+), 53 deletions(-) create mode 100644 webapp/WEB-INF/db/dbunit.pkb create mode 100644 webapp/WEB-INF/db/dbunit.pkh diff --git a/webapp/WEB-INF/db/dbunit.pkb b/webapp/WEB-INF/db/dbunit.pkb new file mode 100644 index 0000000..b256b01 --- /dev/null +++ b/webapp/WEB-INF/db/dbunit.pkb @@ -0,0 +1,40 @@ +create or replace package body dbunit +is + procedure fail(message varchar2) is + begin + raise_application_error(-20000, 'Assertion failed' || chr(10) || message); + end; + + procedure fail(expected varchar2, actual varchar2) is + begin + fail('Expected: ' || expected || chr(10) || 'Actual: ' || actual); + end; + + procedure assert_equals(expected number, actual number) is + begin + if expected != actual then + fail(expected, actual); + end if; + end; + + procedure assert_equals(expected varchar2, actual varchar2) is + begin + if expected != actual then + fail(expected, actual); + end if; + end; + + procedure assert_null(actual varchar2) is + begin + if actual is not null then + fail(actual || ' is not null'); + end if; + end; + + procedure assert_not_null(actual varchar2) is + begin + if actual is null then + fail('argument is null'); + end if; + end; +end; diff --git a/webapp/WEB-INF/db/dbunit.pkh b/webapp/WEB-INF/db/dbunit.pkh new file mode 100644 index 0000000..fd14acd --- /dev/null +++ b/webapp/WEB-INF/db/dbunit.pkh @@ -0,0 +1,7 @@ +create or replace package dbunit +is + procedure assert_equals(expected number, actual number); + procedure assert_equals(expected varchar2, actual varchar2); + procedure assert_null(actual varchar2); + procedure assert_not_null(actual varchar2); +end; diff --git a/webapp/WEB-INF/liquibase-test.xml b/webapp/WEB-INF/liquibase-test.xml index 6a67d4f..e5ffdb5 100644 --- a/webapp/WEB-INF/liquibase-test.xml +++ b/webapp/WEB-INF/liquibase-test.xml @@ -2,63 +2,18 @@ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd"> - - create or replace package dbunit - is - procedure assert_equals(expected number, actual number); - procedure assert_equals(expected varchar2, actual varchar2); - procedure assert_null(actual varchar2); - procedure assert_not_null(actual varchar2); - end; - / - create or replace public synonym dbunit for dbunit/ - grant execute on dbunit to public/ + + + + + + create or replace public synonym dbunit for dbunit; + grant execute on dbunit to public; - - create or replace package body dbunit - is - procedure fail(message varchar2) is - begin - raise_application_error(-20000, 'Assertion failed' || chr(10) || message); - end; - - procedure fail(expected varchar2, actual varchar2) is - begin - fail('Expected: ' || expected || chr(10) || 'Actual: ' || actual); - end; - - procedure assert_equals(expected number, actual number) is - begin - if expected != actual then - fail(expected, actual); - end if; - end; - - procedure assert_equals(expected varchar2, actual varchar2) is - begin - if expected != actual then - fail(expected, actual); - end if; - end; - - procedure assert_null(actual varchar2) is - begin - if actual is not null then - fail(actual || ' is not null'); - end if; - end; - - procedure assert_not_null(actual varchar2) is - begin - if actual is null then - fail('argument is null'); - end if; - end; - end; - + From 999afc9f92e10877765089a8ac32782df2cb3976 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Fri, 11 May 2012 00:34:15 +0300 Subject: [PATCH 15/32] photo spots package introduced with unit test --- webapp/WEB-INF/db-test/photo_spots_test.sql | 14 ++++++++++++++ webapp/WEB-INF/db/dbunit.pkb | 4 ++-- webapp/WEB-INF/db/photo_spots.pkb | 10 ++++++++++ webapp/WEB-INF/db/photo_spots.pkh | 3 +++ webapp/WEB-INF/liquibase-test.xml | 12 ++++++++++++ webapp/WEB-INF/liquibase.xml | 8 ++++---- 6 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 webapp/WEB-INF/db-test/photo_spots_test.sql create mode 100644 webapp/WEB-INF/db/photo_spots.pkb create mode 100644 webapp/WEB-INF/db/photo_spots.pkh diff --git a/webapp/WEB-INF/db-test/photo_spots_test.sql b/webapp/WEB-INF/db-test/photo_spots_test.sql new file mode 100644 index 0000000..ef1c0cd --- /dev/null +++ b/webapp/WEB-INF/db-test/photo_spots_test.sql @@ -0,0 +1,14 @@ +declare + spot_id number; + spot photospot%rowtype; +begin + -- Can add photo spots + spot_id := photo_spots.add('Teletorn', 'Tallinn TV Tower', 59.47111, 24.8875); + dbunit.assert_not_null(spot_id); + + dbunit.assert_equals('Teletorn', spot.name); + dbunit.assert_equals('Tallinn TV Tower', spot.description); + dbunit.assert_equals(59.47111, spot.latitude); + dbunit.assert_equals(24.8875, spot.longitude); + rollback; +end; \ No newline at end of file diff --git a/webapp/WEB-INF/db/dbunit.pkb b/webapp/WEB-INF/db/dbunit.pkb index b256b01..6a3b162 100644 --- a/webapp/WEB-INF/db/dbunit.pkb +++ b/webapp/WEB-INF/db/dbunit.pkb @@ -12,14 +12,14 @@ is procedure assert_equals(expected number, actual number) is begin - if expected != actual then + if expected is null or actual is null or expected != actual then fail(expected, actual); end if; end; procedure assert_equals(expected varchar2, actual varchar2) is begin - if expected != actual then + if expected is null or actual is null or expected != actual then fail(expected, actual); end if; end; diff --git a/webapp/WEB-INF/db/photo_spots.pkb b/webapp/WEB-INF/db/photo_spots.pkb new file mode 100644 index 0000000..c25d24e --- /dev/null +++ b/webapp/WEB-INF/db/photo_spots.pkb @@ -0,0 +1,10 @@ +create or replace package body photo_spots is + function add(name varchar2, description varchar2, lat float, lon float) return number is + id number; + begin + select hibernate_sequence.nextval into id from dual; + insert into photospot (id, name, description, latitude, longitude) + values (id, name, description, lat, lon); + return id; + end; +end; \ No newline at end of file diff --git a/webapp/WEB-INF/db/photo_spots.pkh b/webapp/WEB-INF/db/photo_spots.pkh new file mode 100644 index 0000000..88cad82 --- /dev/null +++ b/webapp/WEB-INF/db/photo_spots.pkh @@ -0,0 +1,3 @@ +create or replace package photo_spots is + function add(name varchar2, description varchar2, lat float, lon float) return number; +end; diff --git a/webapp/WEB-INF/liquibase-test.xml b/webapp/WEB-INF/liquibase-test.xml index e5ffdb5..a228170 100644 --- a/webapp/WEB-INF/liquibase-test.xml +++ b/webapp/WEB-INF/liquibase-test.xml @@ -16,4 +16,16 @@ + + + + + + + + + + + + diff --git a/webapp/WEB-INF/liquibase.xml b/webapp/WEB-INF/liquibase.xml index 7794438..1480d7f 100644 --- a/webapp/WEB-INF/liquibase.xml +++ b/webapp/WEB-INF/liquibase.xml @@ -1,16 +1,14 @@ - - - + - + @@ -32,4 +30,6 @@ + + From bb35d3b2a34baf12b09c9ff529e3f81037da1a9a Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Fri, 11 May 2012 01:09:19 +0300 Subject: [PATCH 16/32] cannot insert illegal coordinates --- webapp/WEB-INF/db-test/photo_spots_test.sql | 20 +++++++++++++++++++- webapp/WEB-INF/db/dbunit.pkb | 5 +++++ webapp/WEB-INF/db/dbunit.pkh | 2 ++ webapp/WEB-INF/db/photo_spots.pkb | 4 ++++ webapp/WEB-INF/db/photo_spots.pkh | 2 ++ webapp/WEB-INF/liquibase-test.xml | 8 +++++++- webapp/WEB-INF/liquibase.xml | 6 +++--- 7 files changed, 42 insertions(+), 5 deletions(-) diff --git a/webapp/WEB-INF/db-test/photo_spots_test.sql b/webapp/WEB-INF/db-test/photo_spots_test.sql index ef1c0cd..a56c78b 100644 --- a/webapp/WEB-INF/db-test/photo_spots_test.sql +++ b/webapp/WEB-INF/db-test/photo_spots_test.sql @@ -6,9 +6,27 @@ begin spot_id := photo_spots.add('Teletorn', 'Tallinn TV Tower', 59.47111, 24.8875); dbunit.assert_not_null(spot_id); + select * into spot from photospot where id = spot_id; dbunit.assert_equals('Teletorn', spot.name); dbunit.assert_equals('Tallinn TV Tower', spot.description); dbunit.assert_equals(59.47111, spot.latitude); dbunit.assert_equals(24.8875, spot.longitude); + + -- Cannot insert illegal latitude + begin + spot_id := photo_spots.add('A', 'B', 91.0, 24.0); + dbunit.expect_exception; + exception + when photo_spots.illegal_coordinates then null; + end; + + -- Cannot insert illegal longitude + begin + spot_id := photo_spots.add('A', 'B', 59.0, -181); + dbunit.expect_exception; + exception + when photo_spots.illegal_coordinates then null; + end; + rollback; -end; \ No newline at end of file +end; diff --git a/webapp/WEB-INF/db/dbunit.pkb b/webapp/WEB-INF/db/dbunit.pkb index 6a3b162..142c99e 100644 --- a/webapp/WEB-INF/db/dbunit.pkb +++ b/webapp/WEB-INF/db/dbunit.pkb @@ -10,6 +10,11 @@ is fail('Expected: ' || expected || chr(10) || 'Actual: ' || actual); end; + procedure expect_exception is + begin + fail('Exception expected'); + end; + procedure assert_equals(expected number, actual number) is begin if expected is null or actual is null or expected != actual then diff --git a/webapp/WEB-INF/db/dbunit.pkh b/webapp/WEB-INF/db/dbunit.pkh index fd14acd..c49373e 100644 --- a/webapp/WEB-INF/db/dbunit.pkh +++ b/webapp/WEB-INF/db/dbunit.pkh @@ -1,5 +1,7 @@ create or replace package dbunit is + procedure fail(message varchar2 default ''); + procedure expect_exception; procedure assert_equals(expected number, actual number); procedure assert_equals(expected varchar2, actual varchar2); procedure assert_null(actual varchar2); diff --git a/webapp/WEB-INF/db/photo_spots.pkb b/webapp/WEB-INF/db/photo_spots.pkb index c25d24e..3205cbb 100644 --- a/webapp/WEB-INF/db/photo_spots.pkb +++ b/webapp/WEB-INF/db/photo_spots.pkb @@ -2,6 +2,10 @@ create or replace package body photo_spots is function add(name varchar2, description varchar2, lat float, lon float) return number is id number; begin + if lat < -90 or lat > 90 or lon < -180 or lon > 180 then + raise illegal_coordinates; + end if; + select hibernate_sequence.nextval into id from dual; insert into photospot (id, name, description, latitude, longitude) values (id, name, description, lat, lon); diff --git a/webapp/WEB-INF/db/photo_spots.pkh b/webapp/WEB-INF/db/photo_spots.pkh index 88cad82..cbb050c 100644 --- a/webapp/WEB-INF/db/photo_spots.pkh +++ b/webapp/WEB-INF/db/photo_spots.pkh @@ -1,3 +1,5 @@ create or replace package photo_spots is + illegal_coordinates exception; + function add(name varchar2, description varchar2, lat float, lon float) return number; end; diff --git a/webapp/WEB-INF/liquibase-test.xml b/webapp/WEB-INF/liquibase-test.xml index a228170..95b6837 100644 --- a/webapp/WEB-INF/liquibase-test.xml +++ b/webapp/WEB-INF/liquibase-test.xml @@ -24,7 +24,13 @@ - + + + begin dbms_utility.compile_schema('demo'); end; + + + + diff --git a/webapp/WEB-INF/liquibase.xml b/webapp/WEB-INF/liquibase.xml index 1480d7f..62469f0 100644 --- a/webapp/WEB-INF/liquibase.xml +++ b/webapp/WEB-INF/liquibase.xml @@ -8,10 +8,10 @@ - + - - + + From b2692a252966c43ddacdb720fbf5050286ddbe89 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sat, 12 May 2012 01:03:13 +0300 Subject: [PATCH 17/32] moved db files to the top-level to be more visible in git-hub :-) --- {webapp/WEB-INF/db => db-test}/dbunit.pkb | 0 {webapp/WEB-INF/db => db-test}/dbunit.pkh | 0 db-test/liquibase.xml | 23 ++++++++++++ .../db-test => db-test}/photo_spots_test.sql | 0 {webapp/WEB-INF => db}/liquibase.xml | 14 ++++++- {webapp/WEB-INF/db => db}/photo_spots.pkb | 0 {webapp/WEB-INF/db => db}/photo_spots.pkh | 0 webapp/WEB-INF/liquibase-test.xml | 37 ------------------- webapp/WEB-INF/spring.xml | 10 +++-- 9 files changed, 43 insertions(+), 41 deletions(-) rename {webapp/WEB-INF/db => db-test}/dbunit.pkb (100%) rename {webapp/WEB-INF/db => db-test}/dbunit.pkh (100%) create mode 100644 db-test/liquibase.xml rename {webapp/WEB-INF/db-test => db-test}/photo_spots_test.sql (100%) rename {webapp/WEB-INF => db}/liquibase.xml (71%) rename {webapp/WEB-INF/db => db}/photo_spots.pkb (100%) rename {webapp/WEB-INF/db => db}/photo_spots.pkh (100%) delete mode 100644 webapp/WEB-INF/liquibase-test.xml diff --git a/webapp/WEB-INF/db/dbunit.pkb b/db-test/dbunit.pkb similarity index 100% rename from webapp/WEB-INF/db/dbunit.pkb rename to db-test/dbunit.pkb diff --git a/webapp/WEB-INF/db/dbunit.pkh b/db-test/dbunit.pkh similarity index 100% rename from webapp/WEB-INF/db/dbunit.pkh rename to db-test/dbunit.pkh diff --git a/db-test/liquibase.xml b/db-test/liquibase.xml new file mode 100644 index 0000000..6c2b590 --- /dev/null +++ b/db-test/liquibase.xml @@ -0,0 +1,23 @@ + + + + + + + + + create or replace public synonym dbunit for dbunit; + grant execute on dbunit to public; + + + + + + + + + + + + diff --git a/webapp/WEB-INF/db-test/photo_spots_test.sql b/db-test/photo_spots_test.sql similarity index 100% rename from webapp/WEB-INF/db-test/photo_spots_test.sql rename to db-test/photo_spots_test.sql diff --git a/webapp/WEB-INF/liquibase.xml b/db/liquibase.xml similarity index 71% rename from webapp/WEB-INF/liquibase.xml rename to db/liquibase.xml index 62469f0..325de95 100644 --- a/webapp/WEB-INF/liquibase.xml +++ b/db/liquibase.xml @@ -30,6 +30,18 @@ - + + + + + + + + + + + begin dbms_utility.compile_schema('demo'); end; + + diff --git a/webapp/WEB-INF/db/photo_spots.pkb b/db/photo_spots.pkb similarity index 100% rename from webapp/WEB-INF/db/photo_spots.pkb rename to db/photo_spots.pkb diff --git a/webapp/WEB-INF/db/photo_spots.pkh b/db/photo_spots.pkh similarity index 100% rename from webapp/WEB-INF/db/photo_spots.pkh rename to db/photo_spots.pkh diff --git a/webapp/WEB-INF/liquibase-test.xml b/webapp/WEB-INF/liquibase-test.xml deleted file mode 100644 index 95b6837..0000000 --- a/webapp/WEB-INF/liquibase-test.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - create or replace public synonym dbunit for dbunit; - grant execute on dbunit to public; - - - - - - - - - - - - - - - - - - begin dbms_utility.compile_schema('demo'); end; - - - - - - - - diff --git a/webapp/WEB-INF/spring.xml b/webapp/WEB-INF/spring.xml index e307ab5..3e3d0f3 100644 --- a/webapp/WEB-INF/spring.xml +++ b/webapp/WEB-INF/spring.xml @@ -13,11 +13,15 @@ - - + - + + + + + + From 03b13ca05a131244491a13a6ec40f853c806b801 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sat, 12 May 2012 01:09:45 +0300 Subject: [PATCH 18/32] use h2 by default --- webapp/WEB-INF/jetty-web.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/WEB-INF/jetty-web.xml b/webapp/WEB-INF/jetty-web.xml index 963aaf1..f4e39b5 100644 --- a/webapp/WEB-INF/jetty-web.xml +++ b/webapp/WEB-INF/jetty-web.xml @@ -8,8 +8,8 @@ true - - jdbc:oracle:thin:demo/demo@localhost:1521:xe + jdbc:h2:.h2db;AUTO_SERVER=TRUE;USER=sa;PASSWORD=sa + 5 5 true From 680c2abb6a923a5c7b65efb25f6695bc3e6a41de Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sat, 12 May 2012 01:17:34 +0300 Subject: [PATCH 19/32] h2 requires auto_increment for id --- db/liquibase.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/db/liquibase.xml b/db/liquibase.xml index 325de95..4b41d97 100644 --- a/db/liquibase.xml +++ b/db/liquibase.xml @@ -16,6 +16,10 @@ + + + + From 6d6e953ea644362c9b3f996a4df30484beda2577 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sat, 12 May 2012 01:19:12 +0300 Subject: [PATCH 20/32] use shorter ids --- db/liquibase.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/db/liquibase.xml b/db/liquibase.xml index 4b41d97..13ee553 100644 --- a/db/liquibase.xml +++ b/db/liquibase.xml @@ -5,7 +5,7 @@ - + @@ -16,16 +16,16 @@ - + - + - + From 46660433c14c7274b6f020df320f7fbb0ab6c576 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sat, 12 May 2012 01:21:05 +0300 Subject: [PATCH 21/32] h2 url fixed --- h2.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/h2.sh b/h2.sh index 96d60ce..8f3550f 100755 --- a/h2.sh +++ b/h2.sh @@ -1,4 +1,4 @@ #!/bin/bash # This script will give you H2 database shell -java -cp lib/default/h2*.jar org.h2.tools.Shell -url 'jdbc:h2:~/.devclub-java/db;AUTO_SERVER=TRUE;USER=devclub;PASSWORD=devclub' -user devclub -password devclub +java -cp lib/default/h2*.jar org.h2.tools.Shell -url 'jdbc:h2:.dbh2;AUTO_SERVER=TRUE' -user sa -password sa From 32b2b07bd3ee5d97052be1e2569233d221cc85fe Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Sat, 12 May 2012 01:36:47 +0300 Subject: [PATCH 22/32] h2 urls changed once again --- h2.sh | 2 +- webapp/WEB-INF/jetty-web.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/h2.sh b/h2.sh index 8f3550f..6af14b2 100755 --- a/h2.sh +++ b/h2.sh @@ -1,4 +1,4 @@ #!/bin/bash # This script will give you H2 database shell -java -cp lib/default/h2*.jar org.h2.tools.Shell -url 'jdbc:h2:.dbh2;AUTO_SERVER=TRUE' -user sa -password sa +java -cp lib/default/h2*.jar org.h2.tools.Shell -url 'jdbc:h2:.simple-java;AUTO_SERVER=TRUE' -user sa -password sa diff --git a/webapp/WEB-INF/jetty-web.xml b/webapp/WEB-INF/jetty-web.xml index f4e39b5..c202260 100644 --- a/webapp/WEB-INF/jetty-web.xml +++ b/webapp/WEB-INF/jetty-web.xml @@ -8,7 +8,7 @@ true - jdbc:h2:.h2db;AUTO_SERVER=TRUE;USER=sa;PASSWORD=sa + jdbc:h2:.simple-java;AUTO_SERVER=TRUE;USER=sa;PASSWORD=sa 5 5 From dea60eee10fd1a12505bbf4020b478ba1a6aba10 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Wed, 23 May 2012 11:53:33 +0300 Subject: [PATCH 23/32] do insert only where needed --- .../model/JDBCPhotoSpotRepositoryIntegrationTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java b/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java index 9780c68..4e8732d 100644 --- a/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java +++ b/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java @@ -23,7 +23,6 @@ public void initMockDB() throws Exception { conn = dataSource.getConnection(); conn.createStatement().execute("create table PhotoSpot (id int auto_increment primary key, name varchar, description varchar, latitude float, longitude float)"); - conn.createStatement().execute("insert into PhotoSpot values (1, 'Kohtuotsa', 'Mega place!', 59.437755, 24.74209)"); repo.dataSource = dataSource; } @@ -35,6 +34,8 @@ public void destroyMockDB() throws Exception { @Test public void loading() throws Exception { + conn.createStatement().execute("insert into PhotoSpot values (1, 'Kohtuotsa', 'Mega place!', 59.437755, 24.74209)"); + List spots = repo.getAllSpots(); assertThat(spots.size(), is(1)); @@ -49,7 +50,7 @@ public void fullCycle() throws Exception { PhotoSpot spot = new PhotoSpot("Teletorn", "Tallinn TV Tower", new Location(59.47111f, 24.8875f)); repo.persist(spot); - PhotoSpot spot2 = repo.getAllSpots().get(1); + PhotoSpot spot2 = repo.getAllSpots().get(0); assertThat(spot2, not(sameInstance(spot))); assertThat(spot2, equalTo(spot)); } From 6bc2aa7bdb224d19d5502f8071b229198593bfab Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Wed, 23 May 2012 12:13:52 +0300 Subject: [PATCH 24/32] use the same connection string as in db-testing branch --- h2.sh | 2 +- webapp/WEB-INF/jetty-web.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/h2.sh b/h2.sh index 96d60ce..6af14b2 100755 --- a/h2.sh +++ b/h2.sh @@ -1,4 +1,4 @@ #!/bin/bash # This script will give you H2 database shell -java -cp lib/default/h2*.jar org.h2.tools.Shell -url 'jdbc:h2:~/.devclub-java/db;AUTO_SERVER=TRUE;USER=devclub;PASSWORD=devclub' -user devclub -password devclub +java -cp lib/default/h2*.jar org.h2.tools.Shell -url 'jdbc:h2:.simple-java;AUTO_SERVER=TRUE' -user sa -password sa diff --git a/webapp/WEB-INF/jetty-web.xml b/webapp/WEB-INF/jetty-web.xml index 48ab23d..4505ef3 100644 --- a/webapp/WEB-INF/jetty-web.xml +++ b/webapp/WEB-INF/jetty-web.xml @@ -8,7 +8,7 @@ true - jdbc:h2:~/.devclub-java/db;AUTO_SERVER=TRUE;USER=devclub;PASSWORD=devclub + jdbc:h2:.simple-java;AUTO_SERVER=TRUE;USER=sa;PASSWORD=sa 5 5 true From c859545c2e223437e5fb6a3348f0bbdfc5e81549 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Thu, 24 May 2012 00:45:48 +0300 Subject: [PATCH 25/32] updated gitignore --- .gitignore | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 29937ae..b879e54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -out -logs -lib -*.iml -.idea +/out +/logs +/lib +/*.iml +/.idea +/.*.db \ No newline at end of file From 25934e6390af66d4eedf30b8042cb9bc031cfb60 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Mon, 29 Oct 2012 21:29:56 +0200 Subject: [PATCH 26/32] resource now supports limiting results + test --- src/ee/devclub/rest/PhotoSpotResource.java | 6 ++++-- ...bernatePhotoSpotRepositoryIntegrationTest.java | 6 +++--- test/ee/devclub/rest/PhotoSpotResourceTest.java | 15 +++++++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ee/devclub/rest/PhotoSpotResource.java b/src/ee/devclub/rest/PhotoSpotResource.java index f54111f..08982a2 100644 --- a/src/ee/devclub/rest/PhotoSpotResource.java +++ b/src/ee/devclub/rest/PhotoSpotResource.java @@ -12,10 +12,12 @@ @Produces("application/json") public class PhotoSpotResource extends SpringAwareResource { @Autowired PhotoSpotRepository repo; + int maxSpots = 1000; - @GET + @GET public List getAllSpots() { - return repo.getAllSpots(); + List allSpots = repo.getAllSpots(); + return allSpots.subList(0, Math.min(maxSpots, allSpots.size())); } @POST diff --git a/test/ee/devclub/model/HibernatePhotoSpotRepositoryIntegrationTest.java b/test/ee/devclub/model/HibernatePhotoSpotRepositoryIntegrationTest.java index b8a5d9a..7ec878a 100644 --- a/test/ee/devclub/model/HibernatePhotoSpotRepositoryIntegrationTest.java +++ b/test/ee/devclub/model/HibernatePhotoSpotRepositoryIntegrationTest.java @@ -8,11 +8,10 @@ import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean; import javax.sql.DataSource; -import java.sql.Connection; import java.util.List; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; public class HibernatePhotoSpotRepositoryIntegrationTest { DataSource dataSource; @@ -34,7 +33,8 @@ public void setUp() throws Exception { @Test public void loading() throws Exception { - dataSource.getConnection().createStatement().execute("insert into PhotoSpot (id, name, description, latitude, longitude) values (1, 'Kohtuotsa', 'Mega place!', 59.437755, 24.74209)"); + dataSource.getConnection().createStatement() + .execute("insert into PhotoSpot (id, name, description, latitude, longitude) values (1, 'Kohtuotsa', 'Mega place!', 59.437755, 24.74209)"); List spots = repo.getAllSpots(); assertThat(spots.size(), is(1)); diff --git a/test/ee/devclub/rest/PhotoSpotResourceTest.java b/test/ee/devclub/rest/PhotoSpotResourceTest.java index d9696d0..039a65b 100644 --- a/test/ee/devclub/rest/PhotoSpotResourceTest.java +++ b/test/ee/devclub/rest/PhotoSpotResourceTest.java @@ -7,7 +7,9 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; +import static java.util.Collections.nCopies; import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.*; @@ -16,10 +18,19 @@ public class PhotoSpotResourceTest { @Before public void initMocks() throws Exception { - resource.repo = mock(PhotoSpotRepository.class); + resource.repo = mock(PhotoSpotRepository.class, RETURNS_DEEP_STUBS); } - @Test + @Test + public void resourceLimitedNumberOfSpotsInRepo() throws Exception { + resource.maxSpots = 10; + PhotoSpot photoSpot = mock(PhotoSpot.class); + when(resource.repo.getAllSpots()).thenReturn(nCopies(15, photoSpot)); + + assertEquals(10, resource.getAllSpots().size()); + } + + @Test public void newPhotoSpotsArePersisted() throws Exception { resource.newPhotoSpot("Aegna island", "WWI defence structures", 59.583771f, 24.749720f); From 0c33d23089049bee24671ca53eb1d73cf816ab4a Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Mon, 29 Oct 2012 23:29:04 +0200 Subject: [PATCH 27/32] db-testing branch in now mentioned --- README | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README b/README index 8a5095c..efbbc76 100644 --- a/README +++ b/README @@ -28,6 +28,10 @@ into your own branch in this order: - db - hibernate - tests +- db-testing -See related talk from GOTOCon and Devclub here: +See related talks from GOTOCon and Devclub here: http://www.slideshare.net/antonkeks/simplicity-8971441 + +And another one about DB testing from Topconf: +http://prezi.com/kq0ghszq6e-j/3-tales-of-testing-db-enabled-apps/ From 1021a91895b4af862e6ad4091f2846de12675c16 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Wed, 31 Oct 2012 00:21:18 +0200 Subject: [PATCH 28/32] added license --- LICENSE | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b698fae --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +Based on Simple Public License (SimPL) 2.0 +The SimPL applies to the software's source and object code and comes with any rights that I have in it (other than trademarks). +You agree to the SimPL by copying, distributing, or making a derivative work of the software. + +You get the royalty free right to: +- Use the software for any purpose; +- Make derivative works of it (this is called a "Derived Work"); +- Copy and distribute it and any Derived Work. + +There are some things that you must shoulder: +- You get NO WARRANTIES. None of any kind; +- If the software damages you in any way, you may only recover direct damages up to the amount you paid for it + (that is zero if you did not pay anything). You may not recover any other damages, including those called + "consequential damages." (The state or country where you live may not allow you to limit your liability in + this way, so this may not apply to you); From b7ce89b93d240520705936168f5aeee53991c751 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Wed, 31 Oct 2012 00:31:36 +0200 Subject: [PATCH 29/32] better README --- README | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/README b/README index efbbc76..a266a9a 100644 --- a/README +++ b/README @@ -16,22 +16,26 @@ which uses google maps to browse and edit them. Or you can follow how this all have been created by pulling various branches into your own branch in this order: -- ivy -- launcher -- map -- web.xml -- logging -- jersey -- map-with-spots -- add-spot -- spring -- db -- hibernate -- tests -- db-testing +- start - empty project +- ivy - add ivy for dependency management +- launcher - adds Jetty launcher to run the app without any external application servers +- map - adds html with Google Map +- web.xml - first step for Java web app +- logging - adds good logging, useful for any web app +- jersey - adds Jersey (JAX-RS implementation) for handling HTTP REST requests +- map-with-spots - adds dummy photo spots to the Google Map +- add-spot - possibility to add new spots to the map +- spring - adds Spring for dependency injection +- db - adds H2 database +- hibernate - adds Hibernate for persisting of objects without hand-coded JDBC code +- tests - adds some unit tests (in real life these should be added on every step) +- db-testing - adds plain JDBC implementation for persistence as well as more tests, see 2nd presentation below -See related talks from GOTOCon and Devclub here: +By following these branches in this order you can also get an idea how to build an app step-by-step, +following Just Enough Design principle. + +See related talk from GOTOCon and Devclub here - "How to get Java back": http://www.slideshare.net/antonkeks/simplicity-8971441 -And another one about DB testing from Topconf: +And another one about DB testing from Topconf - "3 tales of testing of DB-enabled apps": http://prezi.com/kq0ghszq6e-j/3-tales-of-testing-db-enabled-apps/ From 84affb8a2a8049b0bef291120c39837158b9b05f Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Wed, 31 Oct 2012 01:23:58 +0200 Subject: [PATCH 30/32] fix formatting --- src/ee/devclub/rest/PhotoSpotResource.java | 4 ++-- .../devclub/rest/PhotoSpotResourceTest.java | 23 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/ee/devclub/rest/PhotoSpotResource.java b/src/ee/devclub/rest/PhotoSpotResource.java index 08982a2..c499042 100644 --- a/src/ee/devclub/rest/PhotoSpotResource.java +++ b/src/ee/devclub/rest/PhotoSpotResource.java @@ -12,9 +12,9 @@ @Produces("application/json") public class PhotoSpotResource extends SpringAwareResource { @Autowired PhotoSpotRepository repo; - int maxSpots = 1000; + int maxSpots = 1000; - @GET + @GET public List getAllSpots() { List allSpots = repo.getAllSpots(); return allSpots.subList(0, Math.min(maxSpots, allSpots.size())); diff --git a/test/ee/devclub/rest/PhotoSpotResourceTest.java b/test/ee/devclub/rest/PhotoSpotResourceTest.java index 039a65b..3cbef6a 100644 --- a/test/ee/devclub/rest/PhotoSpotResourceTest.java +++ b/test/ee/devclub/rest/PhotoSpotResourceTest.java @@ -7,10 +7,9 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; -import static java.util.Collections.nCopies; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static java.util.Collections.*; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class PhotoSpotResourceTest { @@ -21,16 +20,16 @@ public void initMocks() throws Exception { resource.repo = mock(PhotoSpotRepository.class, RETURNS_DEEP_STUBS); } - @Test - public void resourceLimitedNumberOfSpotsInRepo() throws Exception { - resource.maxSpots = 10; - PhotoSpot photoSpot = mock(PhotoSpot.class); - when(resource.repo.getAllSpots()).thenReturn(nCopies(15, photoSpot)); + @Test + public void resourceLimitedNumberOfSpotsInRepo() throws Exception { + resource.maxSpots = 10; + PhotoSpot photoSpot = mock(PhotoSpot.class); + when(resource.repo.getAllSpots()).thenReturn(nCopies(15, photoSpot)); - assertEquals(10, resource.getAllSpots().size()); - } + assertEquals(10, resource.getAllSpots().size()); + } - @Test + @Test public void newPhotoSpotsArePersisted() throws Exception { resource.newPhotoSpot("Aegna island", "WWI defence structures", 59.583771f, 24.749720f); From 430fe995a9e37710f8020043e4e1949af63d0183 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Wed, 31 Oct 2012 01:30:38 +0200 Subject: [PATCH 31/32] slightly less code --- .../model/JDBCPhotoSpotRepositoryIntegrationTest.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java b/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java index 4e8732d..fc9e2e1 100644 --- a/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java +++ b/test/ee/devclub/model/JDBCPhotoSpotRepositoryIntegrationTest.java @@ -5,26 +5,22 @@ import org.junit.Test; import org.springframework.jdbc.datasource.DriverManagerDataSource; -import javax.sql.DataSource; import java.sql.Connection; import java.util.List; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; public class JDBCPhotoSpotRepositoryIntegrationTest { - DataSource dataSource; Connection conn; JDBCPhotoSpotRepository repo = new JDBCPhotoSpotRepository(); @Before public void initMockDB() throws Exception { - dataSource = new DriverManagerDataSource("jdbc:h2:mem:test", "sa", "sa"); - conn = dataSource.getConnection(); + repo.dataSource = new DriverManagerDataSource("jdbc:h2:mem:test", "sa", "sa"); + conn = repo.dataSource.getConnection(); conn.createStatement().execute("create table PhotoSpot (id int auto_increment primary key, name varchar, description varchar, latitude float, longitude float)"); - - repo.dataSource = dataSource; } @After From ec754907bea2613f783347c3a691813f778e9806 Mon Sep 17 00:00:00 2001 From: Anton Keks Date: Mon, 22 Apr 2013 12:21:30 +0300 Subject: [PATCH 32/32] google maps api update --- webapp/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/index.html b/webapp/index.html index 39765a0..aa239e5 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -45,7 +45,7 @@ function editSpot(spot) { function option(value, name) { return {value: value, name: name} } var form = $('#editTemplate').tmpl(spot); - infoWindow.content = form.html(); + infoWindow.setContent(form.html()); infoWindow.open(map, spot.marker); }