I am trying to do some basic automated testing with a DB and TestNG and it isn't working. First run succeeds as I expect, 2nd one doesn't because the first one never rolled back. I have looked at several places for examples and it seems to be correct. Anyone know what I am missing
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.11</version>
<scope>test</scope>
</dependency>
code:
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.springframework.transaction.annotation.Transactional;
import org.testng.annotations.Test;
#ContextConfiguration(classes = AutomatedTest.Context.class)
public class RollbackTest extends AbstractTransactionalTestNGSpringContextTests {
#Test
#Rollback
#Transactional
public void testThing() throws Exception {
Class<? extends RollbackTest> c = this.getClass();
String path = String.format("/%s.sql", c.getName().replaceAll("\\.", "/"));
super.executeSqlScript(path, false);
}
#Configuration
#PropertySource("db.properties")
static class Context {
#Bean
public DataSource dataSource(
#Value("${datasource.url}") String url,
#Value("${datasource.username}") String user,
#Value("${datasource.password}") String pass,
#Value("${datasource.driver-class-name}") String driver) throws PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser(user);
ds.setPassword(pass);
ds.setJdbcUrl(url);
ds.setDriverClass(driver);
return ds;
}
#Bean
public Connection connection(DataSource dataSource) throws SQLException {
Connection c = dataSource.getConnection();
System.out.println("Connection is " + c);
return c;
}
#Bean
public DataSourceTransactionManager txMan(DataSource ds) {
return new DataSourceTransactionManager(ds);
}
}
}
sql:
CREATE SCHEMA FOO;
CREATE TABLE FOO.BAZ (
ID int PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(256) NOT NULL
);
INSERT INTO FOO.BAZ (name) values('christian');
error:
CREATE SCHEMA FOO
org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of class path resource [automated/RollbackTest.sql]: CREATE SCHEMA FOO; nested exception is java.sql.SQLException: Can't create database 'FOO'; database exists
The #Bean method that returns a Connection looks very suspect. So I'd recommend you delete that.
For executing an SQL script before or after a test method, you should ideally look into Spring's #Sql annotation.
Also, you can safely delete the #Rollback and #Transactional declarations on your test method.
#Rollback is the default behavior.
#Transactional is already declared on AbstractTransactionalTestNGSpringContextTests.
Regards,
Sam (author of the Spring TestContext Framework)
Related
I'm gonna create a Java console application for accessesing a database (MySQL). I'm gonna use Spring Boot/Spring Data JPA. What is the correct way to create a console application using Spring Boot?
I found a few ways to do this:
spring.main.web-application-type=NONE (in application.properties)
spring.main.web-environment = false (in application.properties)
using the Spring Shell project
implementing the CommandLineRunner interface
I suppose that some of them may be obsolete, have pros and cons. Could you please explain how to create a plain console application using Spring Boot/Spring Data?
Recently, I have done a console application, as you require now. I did that by implementing CommandLineRunner interface. When spring boot starts the application, it will invoke the run(String... args) method of CommandLineRunner interface.
So, you can autowire(or using constructor injection) spring data repositories in this implemention class(e.g. AppRunner) & invoke database operations.
Instead of MySql database, I have you used MongoDB along with some caching operations.
Example:
AppRunner.java
package com.cache.caching;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
#Component
public class AppRunner implements CommandLineRunner {
Logger logger = LoggerFactory.getLogger(AppRunner.class);
BookRepository bookRepository;
public AppRunner(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
#Override
public void run(String... args) throws Exception {
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
logger.info("articles fetching..."+bookRepository.getArticles());
}
}
BookRepository.java
package com.cache.caching;
import java.net.UnknownHostException;
import java.util.List;
public interface BookRepository {
List<Article> getArticles() throws UnknownHostException;
}
BookRepositoryImpl.java
package com.cache.caching;
import com.mongodb.*;
import org.bson.codecs.pojo.annotations.BsonId;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.mongodb.MongoCollectionUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
#Component
public class BookRepositoryImpl implements BookRepository{
#Override
#Cacheable("articles")
public List<Article> getArticles() throws UnknownHostException {
MongoClient mongoClient
= new MongoClient(new MongoClientURI("mongodb://localhost:27017"));
DB db = mongoClient.getDB("Mart");
DBCollection collection = db.getCollection("articles");
DBCursor cursor = collection.find();
List<Article> list= new ArrayList<>();
while (cursor.hasNext()) {
Article article = new Article();
DBObject dbObject = cursor.next();
article.setId((Double) dbObject.get("_id"));
article.setSubject((String) dbObject.get("subject"));
list.add(article);
}
return list;
}
}
In your case, you can provide MySQL database connection details here in application.yml / application.properties file.
CachingApplication.java - application starts here, this SpringApplication.run(CachingApplication.class, args); invokes the run(String... args) method of CommandLineRunner interface.
package com.cache.caching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class CachingApplication {
public static void main(String[] args) {
SpringApplication.run(CachingApplication.class, args);
}
}
Sample: Sample Full Example Here
I am using spring boot 1.5.x for application. Database is MONGO
Using MongoRepository for crud operations.
Earlier in our project we got dedicated test database instance so we have added mongodb properties of test db in src\test\resources\application.properties.
Now we dont have test db so we want to use embedded/fake db to test our classes Controller/Services/Repository etc..
Options are - embeded mongodb like flapdoodle and fongo
I tried 1 of solution de.flapdoodle.embed.mongo from SO question to use flapdoodle
de.flapdoodle.embed.mongo trying to download zip outside network : unable-to-download-embedded-mongodb-behind-proxy-using-automatic-configuration
I tried fakemongo but its not working for unit and integration testing of controller. Its picking mongo database details of test db using application.properties present in test/resources
pom.xml
<dependency>
<groupId>com.github.fakemongo</groupId>
<artifactId>fongo</artifactId>
<version>${fongo.version}</version>
<!--<scope>test</scope> -->// not working, if removed it says cannot find mongo
</dependency>
<dependency>
<groupId>com.lordofthejars</groupId>
<artifactId>nosqlunit-mongodb</artifactId>
<version>0.7.6</version>
<scope>test</scope>
</dependency>
FakeMongo Configuration
package com.myapp.config;
import com.github.fakemongo.Fongo;
import com.mongodb.MongoClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
#Configuration
#EnableMongoRepositories
public class FakeMongo extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "mockDB";
}
#Bean
public MongoClient mongo() {
Fongo fongo = new Fongo("mockDB");
return fongo.getMongo();
}
}
TestFakeMongo It works. Test executed properly - Reference http://springboot.gluecoders.com/testing-mongodb-springdata.html
package com.myapp.config;
import com.myapp.domain.entitlement.User;
import com.myapp.repository.UserRepository;
import com.lordofthejars.nosqlunit.annotation.UsingDataSet;
import com.lordofthejars.nosqlunit.core.LoadStrategyEnum;
import com.lordofthejars.nosqlunit.mongodb.MongoDbRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static com.lordofthejars.nosqlunit.mongodb.MongoDbRule.MongoDbRuleBuilder.newMongoDbRule;
import static org.junit.Assert.assertTrue;
#RunWith(SpringRunner.class)
#Import(value = {FakeMongo.class})
public class TestFakeMongo {
#Autowired
private ApplicationContext applicationContext;
#Rule
public MongoDbRule embeddedMongoDbRule = newMongoDbRule().defaultSpringMongoDb("mockDB");
#MockBean
private UserRepository userRepository;
#Test
#UsingDataSet(loadStrategy = LoadStrategyEnum.DELETE_ALL)
public void getAllUsers_NoUsers() {
List<User> users = userRepository.findAll();
assertTrue("users list should be empty", users.isEmpty());
}
}
UserRepositoryTest - Unit testing for Repository also working using fongo Reference - http://dontpanic.42.nl/2015/02/in-memory-mongodb-for-unit-and.html
package com.myapp.repository;
import com.myapp.config.SpringUnitTest;
import com.myapp.domain.User;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
public class UserRepositoryTest extends SpringUnitTest {
#Autowired
private UserRepository userRepository;
#Before
public void setup() {
importJSON("user", "user/user.json");
}
#Test
//#UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT, locations = "/json-data/user/user.json") // test/resources/..
public void findUser_should_return_user() {
User user = userRepository.findByXYZId("XX12345");
assertNotNull(user);
}
#Test
public void findUser_should_return_null() {
User user = userRepository.findByXYZId("XX99999");
assertNull(user);
}
#Test
public void deleteUser_should_return_null() {
userRepository.delete("XX12345");
User user = userRepository.findByXYZId("XX12345");
assertNull(user);
}
#Test
public void saveUser_should_return_user() {
User user = new User();
user.setXYZId("XX12345");
user.setAck(true);
List<DateTime> dateTimeList = new ArrayList<>();
dateTimeList.add(DateTime.now(DateTimeZone.UTC));
user.setAckOn(dateTimeList);
User dbUser = userRepository.save(user);
assertEquals(user, dbUser);
}
#Test
public void findAllUser_should_return_users() {
List<User> userList = userRepository.findAll();
assertEquals(1, userList.size());
}
}
Controller level testing not working.. Reference- https://www.paradigmadigital.com/dev/tests-integrados-spring-boot-fongo/
UserControllerTest failed java.lang.IllegalStateException: Failed to load ApplicationContext - At this point test mongo db details fetech instead of fongo test\resources\application.properties test server db details are loading
package com.myapp.config;
package com.myapp.controllers;
import com.myapp.config.FakeMongo;
import com.myapp.services.persistance.UserService;
import com.lordofthejars.nosqlunit.mongodb.MongoDbRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import static com.lordofthejars.nosqlunit.mongodb.MongoDbRule.MongoDbRuleBuilder.newMongoDbRule;
import static org.hamcrest.CoreMatchers.containsString;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#ActiveProfiles("it")
#RunWith(SpringRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = Application.class
)
#Import(value = {FakeMongo.class})
#AutoConfigureMockMvc
#TestPropertySource(locations = "classpath:application-it.properties")
public class UserControllerTest {
#Rule
public MongoDbRule embeddedMongoDbRule = newMongoDbRule().defaultSpringMongoDb("mockDB");
#Autowired
private MockMvc mockMvc;
#MockBean
private UserService service;
#Before
public void setUp() throws Exception {
}
#Test
public void deleteUser() throws Exception {
Mockito.when(this.service.deleteUser(Mockito.anyString())).thenReturn(true);
this.mockMvc.perform(delete("/User")).andDo(print()).andExpect(status().isOk())
.andExpect((ResultMatcher) content().string(containsString("true")));
}
}
UserControllerTest Not working, failing with error java.lang.IllegalStateException: Failed to load ApplicationContext as its tries to connect to test instance of mongo database using application.properties present in test/resources
Appreciate working example to use fakemongo while running Integration test for Controller level
What changes, I need to do in code level(Controller class or any other class) or application.properties of test\resources ?
I am not using any framework just using maven war module and want to test the DAO layer using Juit 4 + Powermockito (first time).
My idea is when I call CustomerDao to test createCustomer. First statement of this method is as below:
Session session = HibernateManager.getInstance().getSessionFactory().openSession();
I want to mock this call so that I can provide the session object which I constructed in the test class using following code:
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.modules.junit4.PowerMockRunner;
import com.dao.CustomerDao;
#RunWith(PowerMockRunner.class)
public class CustomerDaoTest {
private SessionFactory sessionFactory;
#Mock
CustomerDao customer=new CustomerDao();
#Before
public void setup() {
sessionFactory = createSessionFactory();
}
#Test
public void CustomerCreateAndDeleteTest() throws Exception {
// Want to mock here
int id=customer.createCustomer("Indian Customer", "India", "xyz#pk.com",
"234567890", "AB");
Assert.assertEquals(1, id);
}
private SessionFactory createSessionFactory() {
Configuration configuration = new Configuration().configure("hibernate.cfg.h2.xml");// Using H2 for testing only
sessionFactory = configuration.buildSessionFactory();
return sessionFactory;
}
}
Problem is:
When I run my test class I am getting error:
org.hibernate.internal.util.config.ConfigurationException: Unable to
perform unmarshalling at line number -1 and column -1 in RESOURCE
hibernate.cfg.h2.xml. Message: unexpected element
(uri:"http://www.hibernate.org/xsd/orm/cfg",
local:"hibernate-configuration"). Expected elements are
<{}hibernate-configuration>
But if I remove annotation #RunWith(PowerMockRunner.class)
then I am not getting this error.
How can I mock the method call which is inside the createCustomer() method as below:
Session session = HibernateManager.getInstance().getSessionFactory().openSession();
Please guide me how I can write Unit test case to test the DAO layer which can use a different hibernate.cfg.xml file.
The issue appears to be PowerMocks classloader.
Unable to parse hibernate.cfg.xml
I got PowerMock, JUnit4, and Hibernate to work in JDK11 following the same principal, and adding the following to my Class:
#PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.hibernate.*"})
Full class example:
org.hibernate hibernate-core 5.4.2.Final (compile)
junit junit:4.12 (test)
net.bytebuddy byte-buddy 1.9.10 (compile)
org.powermock powermock-module-junit4 2.0.2 (test)
com.h2database h2 1.4.199 (test)
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.hibernate.*"})
public class PowerMockHibernateTest {
private SessionFactory sessionFactory;
public PowerMockHibernateTest() {
}
#Before
public void setUp() {
sessionFactory = createSessionFactory();
}
#After
public void tearDown() {
sessionFactory.close();
}
private Session getNewSession() {
return sessionFactory.openSession();
}
#Test
public void getQuery() {
Session session = getNewSession();
session.createNamedQuery("PostEntity.All", PostEntity.class);
}
private SessionFactory createSessionFactory() {
Configuration configuration = new Configuration().configure("hibernate.cfg.h2.xml");
configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
configuration.setProperty("hibernate.connection.driver_class", "org.h2.Driver");
configuration.setProperty("hibernate.connection.url", "jdbc:h2:mem:test");
configuration.setProperty("hibernate.hbm2ddl.auto", "update");
return configuration.buildSessionFactory();
}
}
I'm new to all the mentioned technologies so it might be a stupid question.
We have a spring boot application where we need to write to a PostgreSQL-Database via JDBC.
Therefore we need the static DriverManager.getConnection() method to open the connection.
Now in my unit tests I don't want to call this class directly.
Instead I want to check, that the DriverManager.getConnection() is called with the correct String as that is my expected observable external behavior.
I encapsulated this behavior into a ConnectionFactory with the method newConnection(ConnectionType.POSTGRESQL) because we got more than one Database to use in this Application.
Now I can't find a way to verify via Mockito that this external dependency was called with the correct String like you could with an instance:
DriverManager dm = mock(DriverManager);
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verify(dm).getConnection("theConnectionStringToBeExpected");
So how to do this with the static dependency?
I tried the Captor-way but this seems to only work for direct usage like
mockStatic(DriverManager.class);
final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// What I have to do to verify
DriverManager.getConnection("theActualConnectionString");
// What I would like to do to verify
// connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verifyStatic();
StaticService.getConnection(captor.capture());
assertEquals("theExpectedConnectionString", captor.getValue());
Edit:
Here is the nasty little workaround which I currently use for another server...
public void driverManagerIsCorrectlyCalledForAds() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
final Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:extendedsystems:advantage://server:1337/database_name;user=user;password=password;chartype=ansi"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.ADS);
assertEquals(expectedConnection, actualConnection);
}
Edit 2:
TestClass:
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.test.context.junit4.SpringRunner;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.*;
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
#PrepareForTest({ConnectionFactory.class, DriverManager.class, DatabaseDriverInformation.class})
public class ConnectionFactoryTest {
#InjectMocks
ConnectionFactory connectionFactory;
#Mock
DatabaseDriverInformation databaseDriverInformation;
#Mock
DatabaseProperties databaseProperties;
#Mock
DatabaseProperties.Pg pg;
#Mock
DatabaseDriverLoader databaseDriverLoader;
#Before
public void setUp() {
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(ads).getServer();
doReturn(1338).when(ads).getPort();
doReturn("database_name").when(ads).getDatabasename();
doReturn("user").when(ads).getUser();
doReturn("password").when(ads).getPassword();
}
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:postgresql://server:1338/database_name;user=user;password=password"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.POSTGRESQL);
assertEquals(expectedConnection, actualConnection);
}
}
Class under Test:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.*;
#Service()
public class ConnectionFactory {
#Autowired
private DatabaseDriverLoader databaseDriverLoader;
#Autowired
DatabaseProperties databaseProperties;
public Connection newConnection(ConnectionType connectionType) {
databaseDriverLoader.load();
final String connectionString = connectionStringFor(connectionType);
try {
return DriverManager.getConnection(connectionString);
} catch (SQLException sqlException) {
throw new RuntimeException("Couldn't connect to Server");
}
}
private String connectionStringFor(ConnectionType connectionType) {
switch (connectionType) {
case ADS:
return this.adsConnectionString();
case POSTGRESQL:
return this.pgConnectionString();
default:
throw new RuntimeException("Invalid connection Type requested!");
}
}
private String adsConnectionString() {
return new StringBuilder()
.append("jdbc:extendedsystems:advantage://")
.append(databaseProperties.getAds().getServer())
.append(":")
.append(databaseProperties.getAds().getPort())
.append("/")
.append(databaseProperties.getAds().getDatabasename())
.append(";user=")
.append(databaseProperties.getAds().getUser())
.append(";password=")
.append(databaseProperties.getAds().getPassword())
.append(";chartype=ansi")
.toString();
}
private String pgConnectionString() {
return new StringBuilder()
.append("jdbc:postgresql://")
.append(databaseProperties.getPg().getServer())
.append(":")
.append(databaseProperties.getPg().getPort())
.append("/")
.append(databaseProperties.getPg().getDatabasename())
.append("?user=")
.append(databaseProperties.getPg().getUser())
.append("&password=")
.append(databaseProperties.getPg().getPassword())
.toString();
}
}
I removed the package-names, some specific imports and some unnecessary tests which are working.
After some search I found this: How to verify static void method has been called with power mockito
In Essence:
first:
mockStatic(ClassWithStaticFunctionToVerify.class)
second:
Execute the Code that will call the function you want to verify later
third:
verifyStatic();
ClassWithStaticFunctionToVerify.functionYouWantToVerify("ParameterValueYouExpect");
With it's help I got the following Solution which works fine:
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
// Arrange
mockStatic(DatabaseProperties.Pg.class);
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(pg).getServer();
doReturn(1338).when(pg).getPort();
doReturn("database_name").when(pg).getDatabasename();
doReturn("user").when(pg).getUser();
doReturn("password").when(pg).getPassword();
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).loadAdsDriverClass();
doNothing().when(databaseDriverLoader).loadPgDriverClass();
when(DriverManager.getConnection(anyString())).thenReturn(expectedConnection);
// Act
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
// Assert
verifyStatic();
DriverManager.getConnection("jdbc:postgresql://server:1338/database_name?user=user&password=password");
}
How to programmatically control transaction boundaries within single #Test method? Spring 4.x documentation has some clues but I think I missing something since the test throws error:
java.lang.IllegalStateException:
Cannot start a new transaction without ending the existing transaction first.
Test
import com.hibernate.query.performance.config.ApplicationConfig;
import com.hibernate.query.performance.config.CachingConfig;
import com.hibernate.query.performance.persistence.model.LanguageEntity;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.transaction.TestTransaction;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.PersistenceContext;
import java.util.List;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { ApplicationConfig.class, CachingConfig.class }, loader = AnnotationConfigContextLoader.class)
#PersistenceContext
#Transactional(transactionManager = "hibernateTransactionManager")
#TestExecutionListeners({})
public class EHCacheTest extends AbstractTransactionalJUnit4SpringContextTests {
private static Logger logger = LoggerFactory.getLogger(EHCacheTest.class);
#BeforeClass
public static void setUpBeforeClass() throws Exception {
logger.info("setUpBeforeClass()");
}
#AfterClass
public static void tearDownAfterClass() throws Exception {
logger.info("tearDownAfterClass()");
}
#Autowired
private SessionFactory sessionFactory;
#Test
public void testTransactionCaching(){
TestTransaction.start();
Session session = sessionFactory.getCurrentSession();
System.out.println(session.get(LanguageEntity.class, 1));
Query query = session.createQuery("from LanguageEntity le where le.languageId < 10").setCacheable(true).setCacheRegion("language");
#SuppressWarnings("unchecked")
List<LanguageEntity> customerEntities = query.list();
System.out.println(customerEntities);
session.getTransaction().commit();
TestTransaction.flagForCommit();
TestTransaction.end();
// Second Transaction
TestTransaction.start();
Session sessionNew = sessionFactory.getCurrentSession();
System.out.println(sessionNew.get(LanguageEntity.class, 1));
Query anotherQuery = sessionNew.createQuery("from LanguageEntity le where le.languageId < 10");
anotherQuery.setCacheable(true).setCacheRegion("language");
#SuppressWarnings("unchecked")
List<LanguageEntity> languagesFromCache = anotherQuery.list();
System.out.println(languagesFromCache);
sessionNew.getTransaction().commit();
TestTransaction.flagForCommit();
TestTransaction.end();
}
}
UPDATE
One more detail:
All occurences session.getTransaction().commit(); must be removed since they interrupt transaction workflow.
TL;DR
In order to avoid this problem, just remove the first line of the test method and use the already available transaction:
#Test
public void testTransactionCaching() {
// Remove this => TestTransaction.start();
// Same as before
}
Detailed Answer
When you annotate your test class with #Transactional or extending the AbstractTransactionalJUnit4SpringContextTests:
// Other annotations
#Transactional(transactionManager = "hibernateTransactionManager")
public class EHCacheTest extends AbstractTransactionalJUnit4SpringContextTests { ... }
Each test method within that class will be run within a transaction. To be more precise, Spring Test Context (By using TransactionalTestExecutionListener) would open a transaction in the beginning of each test method and roll it back after completion of the test.
So, your testTransactionCaching test method:
#Test
public void testTransactionCaching() { ... }
Would have an open transaction in the beginning and you're trying to open another one manually by:
TestTransaction.start();
Hence the error:
Cannot start a new transaction without ending the existing transaction
first.
In order to avoid this problem, just remove the first line of the test method and use that already available transaction. The other TestTransaction.* method calls are OK but just remove the first one.