├── .gitignore ├── README.md ├── examples └── my-batis │ ├── pom.xml │ └── src │ ├── main │ └── java │ │ └── br │ │ └── eng │ │ └── rafaelsouza │ │ └── imdb │ │ ├── Application.java │ │ ├── domain │ │ ├── Blog.java │ │ └── BlogRepository.java │ │ └── persistence │ │ ├── BlogMapper.java │ │ └── MyBatisUtil.java │ └── test │ └── java │ └── br │ └── eng │ └── rafaelsouza │ └── imdb │ └── domain │ └── BlogRepositoryTest.java ├── pom.xml └── src ├── main └── java │ └── br │ └── eng │ └── rafaelsouza │ └── imdb │ ├── DatabaseConfig.java │ ├── DatabaseStatus.java │ ├── DockerDatabaseManager.java │ ├── DockerPostgresql.java │ ├── InMemoryDatabase.java │ ├── Migration.java │ └── junit │ ├── DockerDatabaseConfig.java │ ├── DockerDatabaseRunner.java │ └── TestExecutionListener.java └── test └── java └── br └── eng └── rafaelsouza ├── AppTest.java ├── SomeTest.java └── imdb └── DockerPostgresqlTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Java ### 4 | *.class 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | 15 | ### NetBeans ### 16 | nbproject/private/ 17 | build/ 18 | nbbuild/ 19 | dist/ 20 | nbdist/ 21 | nbactions.xml 22 | nb-configuration.xml 23 | .nb-gradle/ 24 | 25 | 26 | ### Maven ### 27 | target/ 28 | pom.xml.tag 29 | pom.xml.releaseBackup 30 | pom.xml.versionsBackup 31 | pom.xml.next 32 | release.properties 33 | dependency-reduced-pom.xml 34 | buildNumber.properties 35 | 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # In Memory Docker Database 2 | 3 | 4 | In the Java world the most popular approach to test with in memory databases is using H2 or HSQLDB. These databases work well and can emulate some database syntax and features, but like most emulations, this is superficial, imperfect and doesn't cover important database features. 5 | 6 | In Memory Docker Database is a Java library that allow you to easily launch a docker container with a real database instance (like postgres, mysql and so on). This database will be running [in memory][1] and the process to do that is lightweight and quick, what is really useful for test purposes. 7 | 8 | 9 | ## Overview 10 | 11 | ![Alt text](https://cacoo.com/diagrams/KJIYGq2xh7iCL33h-D6350.png) 12 | 13 | 14 | ## Setup 15 | 16 | #### Pull the database images 17 | ```sh 18 | sudo docker pull postgres 19 | ``` 20 | 21 | 22 | #### Enable Docker remote REST API: 23 | ```sh 24 | $ echo "DOCKER_OPTS='-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock'" > /etc/default/docker 25 | $ service docker restart 26 | ``` 27 | [Read here more about it!][2] 28 | 29 | 30 | #### Maven dependencies: 31 | 32 | ```xml 33 | 34 | 35 | in-memory-docker-database-mvn-repo 36 | https://raw.github.com/rafaelpsouza/in-memory-docker-database/mvn-repo/ 37 | 38 | true 39 | always 40 | 41 | 42 | 43 | ``` 44 | 45 | 46 | ```xml 47 | 48 | br.eng.rafaelsouza 49 | in-memory-docker-database 50 | 1.0-SNAPSHOT 51 | test 52 | 53 | ``` 54 | 55 | 56 | ## How to use 57 | 58 | Basically you need to add two annotations in your tests: 59 | 60 | ```java 61 | @RunWith(DockerDatabaseRunner.class) 62 | @DockerDatabaseConfig(type = DatabaseType.POSTGRES, port = 5432) 63 | public class BlogRepositoryTest { 64 | ``` 65 | 66 | * **@RunWith(DockerDatabaseRunner.class)**: The test runner that will enable test listeners (start database before start tests and stop afterwards) 67 | * **@DockerDatabaseConfig(type = DatabaseType.POSTGRES, port = 5432)**: The database configuration. **type** is the database that you want to launch (only Postgres 9.4 is supported at this monent). **port** the port that the database you listen for connections. 68 | 69 | You can also see the example projects here: 70 | [examples][3] 71 | 72 | 73 | 74 | 75 | ### How to contribute 76 | 77 | Fell free to open issues and pull requests or send me an [e-mail][4] if you want to code it. 78 | 79 | 80 | 81 | [1]: http://www.martinfowler.com/bliki/InMemoryTestDatabase.html 82 | [2]: http://infoslack.com/devops/exploring-docker-remote-api/ 83 | [3]: https://github.com/rafaelpsouza/in-memory-docker-database/tree/master/examples/ 84 | [4]: mailto:rafael.bnc@gmail.com 85 | -------------------------------------------------------------------------------- /examples/my-batis/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | br.eng.rafaelsouza 5 | my-batis 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | UTF-8 10 | 1.8 11 | 1.8 12 | 13 | 14 | 15 | 16 | org.mybatis 17 | mybatis 18 | 3.3.0 19 | 20 | 21 | org.postgresql 22 | postgresql 23 | 9.4-1201-jdbc41 24 | 25 | 26 | junit 27 | junit 28 | 4.10 29 | test 30 | 31 | 32 | br.eng.rafaelsouza 33 | in-memory-docker-database 34 | 1.0-SNAPSHOT 35 | test 36 | jar 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/my-batis/src/main/java/br/eng/rafaelsouza/imdb/Application.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb; 2 | 3 | import br.eng.rafaelsouza.imdb.persistence.MyBatisUtil; 4 | import br.eng.rafaelsouza.imdb.persistence.BlogMapper; 5 | import br.eng.rafaelsouza.imdb.domain.BlogRepository; 6 | import org.apache.ibatis.session.SqlSession; 7 | 8 | /** 9 | * 10 | * @author Rafael Souza 11 | */ 12 | public class Application { 13 | 14 | public static void main(String[] args) { 15 | SqlSession session = MyBatisUtil.buildSqlSessionFactory().openSession(); 16 | BlogRepository repository = new BlogRepository(session.getMapper(BlogMapper.class)); 17 | System.out.println("Blog: " + repository.findById(1)); 18 | repository.listAll().forEach(blog -> System.out.println("Blog: " + blog)); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /examples/my-batis/src/main/java/br/eng/rafaelsouza/imdb/domain/Blog.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb.domain; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * 7 | * @author Rafael Souza 8 | */ 9 | public class Blog { 10 | 11 | private int id; 12 | private String name; 13 | private String author; 14 | 15 | public Blog() { 16 | } 17 | 18 | public Blog(int id, String name, String author) { 19 | this.id = id; 20 | this.name = name; 21 | this.author = author; 22 | } 23 | 24 | public int getId() { 25 | return id; 26 | } 27 | 28 | public void setId(int id) { 29 | this.id = id; 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public void setName(String name) { 37 | this.name = name; 38 | } 39 | 40 | public String getAuthor() { 41 | return author; 42 | } 43 | 44 | public void setAuthor(String author) { 45 | this.author = author; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "Blog{" + "id=" + id + ", name=" + name + ", author=" + author + '}'; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | int hash = 7; 56 | hash = 79 * hash + this.id; 57 | return hash; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object obj) { 62 | if (obj == null) { 63 | return false; 64 | } 65 | if (getClass() != obj.getClass()) { 66 | return false; 67 | } 68 | final Blog other = (Blog) obj; 69 | if (this.id != other.id) { 70 | return false; 71 | } 72 | if (!Objects.equals(this.name, other.name)) { 73 | return false; 74 | } 75 | if (!Objects.equals(this.author, other.author)) { 76 | return false; 77 | } 78 | return true; 79 | } 80 | 81 | 82 | } 83 | -------------------------------------------------------------------------------- /examples/my-batis/src/main/java/br/eng/rafaelsouza/imdb/domain/BlogRepository.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb.domain; 2 | 3 | import br.eng.rafaelsouza.imdb.persistence.BlogMapper; 4 | import br.eng.rafaelsouza.imdb.domain.Blog; 5 | import java.util.List; 6 | 7 | /** 8 | * 9 | * @author Rafael Souza 10 | */ 11 | public class BlogRepository { 12 | 13 | BlogMapper blogMapper; 14 | 15 | public BlogRepository(BlogMapper blogMapper) { 16 | this.blogMapper = blogMapper; 17 | } 18 | 19 | 20 | public Blog findById(int id){ 21 | return blogMapper.selectBlog(id); 22 | } 23 | 24 | 25 | public List listAll(){ 26 | return blogMapper.select(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /examples/my-batis/src/main/java/br/eng/rafaelsouza/imdb/persistence/BlogMapper.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb.persistence; 2 | 3 | import br.eng.rafaelsouza.imdb.domain.Blog; 4 | import java.util.List; 5 | import org.apache.ibatis.annotations.Select; 6 | 7 | /** 8 | * 9 | * @author Rafael Souza 10 | */ 11 | public interface BlogMapper { 12 | 13 | @Select("SELECT * FROM blog WHERE id = #{id}") 14 | Blog selectBlog(int id); 15 | 16 | @Select("SELECT * FROM blog") 17 | List select(); 18 | } 19 | -------------------------------------------------------------------------------- /examples/my-batis/src/main/java/br/eng/rafaelsouza/imdb/persistence/MyBatisUtil.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb.persistence; 2 | 3 | import javax.sql.DataSource; 4 | import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; 5 | import org.apache.ibatis.mapping.Environment; 6 | import org.apache.ibatis.session.Configuration; 7 | import org.apache.ibatis.session.SqlSessionFactory; 8 | import org.apache.ibatis.session.SqlSessionFactoryBuilder; 9 | import org.apache.ibatis.transaction.TransactionFactory; 10 | import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; 11 | 12 | /** 13 | * 14 | * @author Rafael Souza 15 | */ 16 | public class MyBatisUtil { 17 | 18 | public static SqlSessionFactory buildSqlSessionFactory() { 19 | DataSource dataSource = new UnpooledDataSource("org.postgresql.Driver", "jdbc:postgresql://localhost:5432/postgres", "postgres", ""); 20 | TransactionFactory transactionFactory = new JdbcTransactionFactory(); 21 | Environment environment = new Environment("development", transactionFactory, dataSource); 22 | Configuration configuration = new Configuration(environment); 23 | configuration.addMapper(BlogMapper.class); 24 | SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration); 25 | return sqlSessionFactory; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /examples/my-batis/src/test/java/br/eng/rafaelsouza/imdb/domain/BlogRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb.domain; 2 | 3 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseConfig; 4 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseConfig.DatabaseType; 5 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseRunner; 6 | import br.eng.rafaelsouza.imdb.persistence.BlogMapper; 7 | import br.eng.rafaelsouza.imdb.persistence.MyBatisUtil; 8 | import java.sql.SQLException; 9 | import java.util.List; 10 | import org.apache.ibatis.session.SqlSession; 11 | import org.junit.After; 12 | import org.junit.Test; 13 | import static org.junit.Assert.*; 14 | import org.junit.Before; 15 | import org.junit.BeforeClass; 16 | import org.junit.runner.RunWith; 17 | 18 | /** 19 | * 20 | * @author Rafael Souza 21 | */ 22 | @RunWith(DockerDatabaseRunner.class) 23 | @DockerDatabaseConfig(type = DatabaseType.POSTGRES, port = 5432) 24 | public class BlogRepositoryTest { 25 | 26 | SqlSession sqlSession; 27 | 28 | @BeforeClass 29 | public static void migrate() throws SQLException { 30 | String createTable = "CREATE TABLE blog(id serial NOT NULL, name character varying(50), author character varying(50), CONSTRAINT blog_pkey PRIMARY KEY (id));"; 31 | String blog1 = "INSERT INTO blog (name, author) VALUES ('Blog1', 'Rafael');"; 32 | String blog2 = "INSERT INTO blog (name, author) VALUES ('Blog2', 'ilegra');"; 33 | SqlSession session = MyBatisUtil.buildSqlSessionFactory().openSession(); 34 | session.getConnection().prepareCall(createTable).execute(); 35 | session.getConnection().prepareCall(blog1).executeUpdate(); 36 | session.getConnection().prepareCall(blog2).executeUpdate(); 37 | session.commit(); 38 | session.close(); 39 | } 40 | 41 | @Before 42 | public void setUp() throws SQLException { 43 | sqlSession = MyBatisUtil.buildSqlSessionFactory().openSession(); 44 | } 45 | 46 | @After 47 | public void after() { 48 | sqlSession.close(); 49 | } 50 | 51 | @Test 52 | public void testFindById() throws SQLException, InterruptedException { 53 | BlogRepository blogRepository = new BlogRepository(sqlSession.getMapper(BlogMapper.class)); 54 | Blog result = blogRepository.findById(1); 55 | assertEquals(new Blog(1, "Blog1", "Rafael"), result); 56 | } 57 | 58 | @Test 59 | public void testListAll() { 60 | BlogRepository blogRepository = new BlogRepository(sqlSession.getMapper(BlogMapper.class)); 61 | List result = blogRepository.listAll(); 62 | assertEquals(2, result.size()); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | br.eng.rafaelsouza 5 | in-memory-docker-database 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | UTF-8 10 | 1.8 11 | 1.8 12 | github 13 | 14 | 15 | 16 | 17 | 18 | maven-deploy-plugin 19 | 2.8.1 20 | 21 | internal.repo::default::file://${project.build.directory}/mvn-repo 22 | 23 | 24 | 25 | com.github.github 26 | site-maven-plugin 27 | 0.11 28 | 29 | Maven artifacts for ${project.version} 30 | true 31 | ${project.build.directory}/mvn-repo 32 | refs/heads/mvn-repo 33 | 34 | **/* 35 | 36 | in-memory-docker-database 37 | rafaelpsouza 38 | 39 | 40 | 41 | 42 | 43 | site 44 | 45 | deploy 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | internal.repo 55 | Temporary Staging Repository 56 | file://${project.build.directory}/mvn-repo 57 | 58 | 59 | 60 | 61 | 62 | com.github.docker-java 63 | docker-java 64 | 1.3.0 65 | 66 | 67 | junit 68 | junit 69 | 4.10 70 | compile 71 | 72 | 73 | org.mockito 74 | mockito-core 75 | 1.10.19 76 | test 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/DatabaseConfig.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb; 2 | 3 | import java.util.Optional; 4 | 5 | /** 6 | * 7 | * @author Rafael Souza 8 | */ 9 | public class DatabaseConfig { 10 | 11 | public enum DatabaseType { 12 | POSTGRES 13 | } 14 | 15 | private DatabaseType dbType; 16 | private Optional dbName; 17 | private Optional user; 18 | private Optional password; 19 | private Optional port; 20 | 21 | public DatabaseConfig(DatabaseType databaseType) { 22 | this.dbType = databaseType; 23 | this.dbName = Optional.empty(); 24 | this.user = Optional.empty(); 25 | this.password = Optional.empty(); 26 | this.port = Optional.empty();; 27 | } 28 | 29 | public Optional getDbName() { 30 | return dbName; 31 | } 32 | 33 | public DatabaseConfig withDbName(String dbName) { 34 | if (!isEmpty(dbName)) { 35 | this.dbName = Optional.of(dbName); 36 | } 37 | return this; 38 | } 39 | 40 | public Optional getUser() { 41 | return user; 42 | } 43 | 44 | public DatabaseConfig withUser(String user) { 45 | if (!isEmpty(user)) { 46 | this.user = Optional.of(user); 47 | } 48 | return this; 49 | } 50 | 51 | public Optional getPassword() { 52 | return password; 53 | } 54 | 55 | public DatabaseConfig withPassword(String password) { 56 | if (!isEmpty(password)) { 57 | this.password = Optional.of(password); 58 | } 59 | return this; 60 | } 61 | 62 | public Optional getPort() { 63 | return port; 64 | } 65 | 66 | public DatabaseConfig withPort(Integer port) { 67 | if (port > 0) 68 | this.port = Optional.of(port); 69 | return this; 70 | } 71 | 72 | public DatabaseType getDbType() { 73 | return dbType; 74 | } 75 | 76 | private boolean isEmpty(String value) { 77 | return value == null || value.isEmpty(); 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/DatabaseStatus.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * 7 | * @author Rafael Souza 8 | */ 9 | public class DatabaseStatus { 10 | 11 | private Boolean started; 12 | private String containerId; 13 | private int port; 14 | 15 | public DatabaseStatus(Boolean started) { 16 | this.started = started; 17 | } 18 | 19 | public DatabaseStatus(Boolean started, String containerId, int port) { 20 | this.started = started; 21 | this.containerId = containerId; 22 | this.port = port; 23 | } 24 | 25 | public Boolean getStarted() { 26 | return started; 27 | } 28 | 29 | public void setStarted(Boolean started) { 30 | this.started = started; 31 | } 32 | 33 | public String getContainerId() { 34 | return containerId; 35 | } 36 | 37 | public void setContainerId(String containerId) { 38 | this.containerId = containerId; 39 | } 40 | 41 | public int getExposedPort() { 42 | return port; 43 | } 44 | 45 | public void setExposedPort(int exposedPort) { 46 | this.port = exposedPort; 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | int hash = 5; 52 | hash = 59 * hash + Objects.hashCode(this.containerId); 53 | return hash; 54 | } 55 | 56 | @Override 57 | public boolean equals(Object obj) { 58 | if (obj == null) { 59 | return false; 60 | } 61 | if (getClass() != obj.getClass()) { 62 | return false; 63 | } 64 | final DatabaseStatus other = (DatabaseStatus) obj; 65 | if (!Objects.equals(this.started, other.started)) { 66 | return false; 67 | } 68 | if (!Objects.equals(this.containerId, other.containerId)) { 69 | return false; 70 | } 71 | if (this.port != other.port) { 72 | return false; 73 | } 74 | return true; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/DockerDatabaseManager.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb; 2 | 3 | import com.github.dockerjava.core.DockerClientBuilder; 4 | 5 | /** 6 | * 7 | * @author Rafael Souza 8 | */ 9 | public class DockerDatabaseManager { 10 | 11 | private static InMemoryDatabase dbInstance; 12 | 13 | private DockerDatabaseManager() {} 14 | 15 | public static void startDb(DatabaseConfig databaseConfig) { 16 | if (dbInstance == null) { 17 | System.out.println("#################### startign database"); 18 | dbInstance = new DockerPostgresql(DockerClientBuilder.getInstance("http://localhost:2375").build(), databaseConfig); 19 | dbInstance.start(); 20 | } 21 | } 22 | 23 | public static void stop() { 24 | if (dbInstance != null) { 25 | System.out.println("#################### stoping database"); 26 | dbInstance.stop(); 27 | dbInstance = null; 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/DockerPostgresql.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb; 2 | 3 | import com.github.dockerjava.api.DockerClient; 4 | import com.github.dockerjava.api.command.CreateContainerResponse; 5 | import com.github.dockerjava.api.model.ExposedPort; 6 | import com.github.dockerjava.api.model.Ports; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | /** 11 | * 12 | * @author Rafael Souza 13 | */ 14 | public class DockerPostgresql implements InMemoryDatabase { 15 | 16 | private final DockerClient dockerClient; 17 | private String containerId; 18 | private final DatabaseConfig config; 19 | 20 | public DockerPostgresql(DockerClient dockerClient, DatabaseConfig config) { 21 | this.dockerClient = dockerClient; 22 | this.config = config; 23 | } 24 | 25 | @Override 26 | public DatabaseStatus start() { 27 | try { 28 | int databasePort = config.getPort().orElse(5432); 29 | Ports portBindings = new Ports(); 30 | portBindings.bind(ExposedPort.tcp(5432), Ports.Binding(databasePort)); 31 | CreateContainerResponse createContainerResponse = dockerClient.createContainerCmd("postgres") 32 | .withPortBindings(portBindings) 33 | .exec(); 34 | 35 | containerId = createContainerResponse.getId(); 36 | dockerClient.startContainerCmd(containerId).exec(); 37 | Thread.sleep(4000); //Time waiting database, change fora a database check 38 | return new DatabaseStatus(true, containerId, databasePort); 39 | } catch (InterruptedException ex) { 40 | //TODO Log 41 | //Logger.getLogger(DockerPostgresql.class.getName()).log(Level.SEVERE, null, ex); 42 | } 43 | return new DatabaseStatus(false); 44 | } 45 | 46 | @Override 47 | public void migrate(Migration migration) { 48 | throw new UnsupportedOperationException("Not supported yet."); 49 | } 50 | 51 | @Override 52 | public void stop() { 53 | dockerClient.stopContainerCmd(containerId).exec(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/InMemoryDatabase.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb; 2 | 3 | /** 4 | * 5 | * @author Rafael Souza 6 | */ 7 | public interface InMemoryDatabase { 8 | 9 | public DatabaseStatus start(); 10 | public void migrate(Migration migration); 11 | public void stop(); 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/Migration.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb; 2 | 3 | /** 4 | * 5 | * @author Rafael Souza 6 | */ 7 | public interface Migration { 8 | 9 | public void apply(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/junit/DockerDatabaseConfig.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb.junit; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Inherited; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * 11 | * @author Rafael Souza 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.TYPE) 15 | @Inherited 16 | public @interface DockerDatabaseConfig { 17 | 18 | public enum DatabaseType { 19 | POSTGRES 20 | } 21 | 22 | DatabaseType type(); 23 | String database() default ""; 24 | String user() default ""; 25 | String password() default ""; 26 | int port() default -1; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/junit/DockerDatabaseRunner.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb.junit; 2 | 3 | import br.eng.rafaelsouza.imdb.DatabaseConfig; 4 | import br.eng.rafaelsouza.imdb.DockerDatabaseManager; 5 | import org.junit.runner.notification.RunNotifier; 6 | import org.junit.runners.BlockJUnit4ClassRunner; 7 | import org.junit.runners.model.InitializationError; 8 | 9 | /** 10 | * 11 | * @author Rafael Souza 12 | */ 13 | public class DockerDatabaseRunner extends BlockJUnit4ClassRunner { 14 | 15 | public DockerDatabaseRunner(Class klass) throws InitializationError { 16 | super(klass); 17 | } 18 | 19 | @Override 20 | public void run(RunNotifier notifier) { 21 | notifier.addFirstListener(new TestExecutionListener()); 22 | DockerDatabaseManager.startDb(getConfigByAnnotation(this.getDescription().getTestClass().getAnnotationsByType(DockerDatabaseConfig.class))); 23 | super.run(notifier); 24 | } 25 | 26 | private DatabaseConfig getConfigByAnnotation(DockerDatabaseConfig[] configAnnotations) { 27 | if (configAnnotations.length > 0) { 28 | DockerDatabaseConfig annotation = configAnnotations[0]; 29 | return new DatabaseConfig(DatabaseConfig.DatabaseType.POSTGRES) 30 | .withDbName(annotation.database()) 31 | .withUser(annotation.user()) 32 | .withPassword(annotation.password()) 33 | .withPort(annotation.port()); 34 | } 35 | return new DatabaseConfig(DatabaseConfig.DatabaseType.POSTGRES); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/br/eng/rafaelsouza/imdb/junit/TestExecutionListener.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza.imdb.junit; 2 | 3 | import br.eng.rafaelsouza.imdb.DockerDatabaseManager; 4 | import org.junit.runner.Result; 5 | import org.junit.runner.notification.RunListener; 6 | 7 | /** 8 | * 9 | * @author Rafael Souza 10 | */ 11 | class TestExecutionListener extends RunListener { 12 | 13 | @Override 14 | public void testRunFinished(Result result) throws Exception { 15 | DockerDatabaseManager.stop(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/br/eng/rafaelsouza/AppTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package br.eng.rafaelsouza; 7 | 8 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseConfig; 9 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseConfig.DatabaseType; 10 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseRunner; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | /** 15 | * 16 | * @author Rafael Souza 17 | */ 18 | @RunWith(DockerDatabaseRunner.class) 19 | @DockerDatabaseConfig(type = DatabaseType.POSTGRES, port = 5432) 20 | public class AppTest { 21 | 22 | 23 | @Test 24 | public void test1() { 25 | System.out.println("RUNNIN TEST: AppTest.test1()"); 26 | } 27 | 28 | @Test 29 | public void test2() { 30 | System.out.println("RUNNIN TEST: AppTest.test2()"); 31 | } 32 | 33 | @Test 34 | public void test3() { 35 | System.out.println("RUNNIN TEST: AppTest.test3()"); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/br/eng/rafaelsouza/SomeTest.java: -------------------------------------------------------------------------------- 1 | package br.eng.rafaelsouza; 2 | 3 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseConfig; 4 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseConfig.DatabaseType; 5 | import br.eng.rafaelsouza.imdb.junit.DockerDatabaseRunner; 6 | import org.junit.BeforeClass; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | /** 11 | * 12 | * @author Rafael Souza 13 | */ 14 | @RunWith(DockerDatabaseRunner.class) 15 | @DockerDatabaseConfig(type = DatabaseType.POSTGRES, port = 5432) 16 | public class SomeTest { 17 | 18 | @BeforeClass 19 | public static void before(){ 20 | System.out.println("BEFORE CLASS"); 21 | } 22 | 23 | @Test 24 | public void test1() { 25 | System.out.println("RUNNIN TEST: SomeTest.test1()"); 26 | } 27 | 28 | @Test 29 | public void test2() { 30 | System.out.println("RUNNIN TEST: SomeTest.test1()"); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/br/eng/rafaelsouza/imdb/DockerPostgresqlTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package br.eng.rafaelsouza.imdb; 7 | 8 | import com.github.dockerjava.api.ConflictException; 9 | import com.github.dockerjava.api.DockerClient; 10 | import com.github.dockerjava.api.NotFoundException; 11 | import com.github.dockerjava.api.command.CreateContainerCmd; 12 | import com.github.dockerjava.api.command.CreateContainerResponse; 13 | import com.github.dockerjava.api.command.StartContainerCmd; 14 | import com.github.dockerjava.api.command.StopContainerCmd; 15 | import com.github.dockerjava.api.model.Ports; 16 | import org.junit.Test; 17 | import static org.junit.Assert.*; 18 | import static org.mockito.Mockito.*; 19 | 20 | /** 21 | * 22 | * @author Rafael Souza 23 | */ 24 | public class DockerPostgresqlTest { 25 | 26 | @Test 27 | public void testStartDefaultConfig() { 28 | DockerClient client = mockedClient("aabb11"); 29 | DockerPostgresql postgresInstance = new DockerPostgresql(client, new DatabaseConfig(DatabaseConfig.DatabaseType.POSTGRES)); 30 | DatabaseStatus status = postgresInstance.start(); 31 | assertEquals(new DatabaseStatus(true, "aabb11", 5432), status); 32 | } 33 | 34 | @Test 35 | public void testStartCustonPort() { 36 | DockerClient client = mockedClient("1111"); 37 | DockerPostgresql postgresInstance = new DockerPostgresql(client, new DatabaseConfig(DatabaseConfig.DatabaseType.POSTGRES).withPort(1000)); 38 | DatabaseStatus status = postgresInstance.start(); 39 | assertEquals(new DatabaseStatus(true, "1111", 1000), status); 40 | } 41 | 42 | @Test 43 | public void testStop() { 44 | DockerClient client = mockedClient("2222"); 45 | DockerPostgresql postgresInstance = new DockerPostgresql(client, new DatabaseConfig(DatabaseConfig.DatabaseType.POSTGRES).withPort(1000)); 46 | DatabaseStatus status = postgresInstance.start(); 47 | postgresInstance.stop(); 48 | verify(client, times(1)).stopContainerCmd(status.getContainerId()); 49 | } 50 | 51 | private DockerClient mockedClient(String createdId) { 52 | DockerClient client = mock(DockerClient.class); 53 | CreateContainerCmd containerCmd = mockedCreateContainerCmd(createdId); 54 | when(client.createContainerCmd("postgres")).thenReturn(containerCmd); 55 | StartContainerCmd startContainerCmd = mockedStartContainerCmd(createdId); 56 | when(client.startContainerCmd(createdId)).thenReturn(startContainerCmd); 57 | when(client.stopContainerCmd(any())).thenReturn(mock(StopContainerCmd.class)); 58 | return client; 59 | } 60 | 61 | private CreateContainerCmd mockedCreateContainerCmd(String createdId) throws NotFoundException, ConflictException { 62 | CreateContainerResponse containerResponse = mockedCreateContainerResponse(createdId); 63 | CreateContainerCmd containerCmd = mock(CreateContainerCmd.class); 64 | when(containerCmd.withPortBindings(any(Ports.class))).thenReturn(containerCmd); 65 | when(containerCmd.exec()).thenReturn(containerResponse); 66 | return containerCmd; 67 | } 68 | 69 | private CreateContainerResponse mockedCreateContainerResponse(String createdId) { 70 | CreateContainerResponse containerResponse = mock(CreateContainerResponse.class); 71 | when(containerResponse.getId()).thenReturn(createdId); 72 | return containerResponse; 73 | } 74 | 75 | private StartContainerCmd mockedStartContainerCmd(String createdId) { 76 | StartContainerCmd startContainerCmd = mock(StartContainerCmd.class); 77 | when(startContainerCmd.getContainerId()).thenReturn(createdId); 78 | return startContainerCmd; 79 | } 80 | 81 | } 82 | --------------------------------------------------------------------------------