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.
Related
When I try using the below code to test the controllers no errors happen, it just says terminated with no logged messages or anything.
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import ....controllers.UserController;
import ....data.response.UserResponse;
import ....models.user.User;
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
public class UserWebMvc {
#Autowired
private MockMvc mvc;
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(), "hello");
// userService.createUser(
// new UserRequest(alex.getInfo().getName(), alex.getInfo().getEmail(), alex.getInfo().getPassword()));
//
}
}
However my test to check that junit is working runs fine, so I'm thinking its something to do with SpringRunner or WebMvc
#SpringBootTest
class BackendApplicationTests {
#Test
void contextLoads() {
assertTrue(false);
}
}
Isn't assertTrue(false) always going to be false? You are basically checking whether false equals true, which will always fail.
Also, from the Spring documentation regarding #WebMvcTest:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
Using this annotation, it will not scan any beans from the service layer. So if you have a UserService which is injected into the UserController, it will not be found by Spring.
When I run tests like this, I would do something like:
class UserControllerTest {
#InjectMocks
private UserController userController;
private MockMvc mockMvc;
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
mockMvc =
MockMvcBuilders.standaloneSetup(userController).build();
}
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new
UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(),
"hello");
}
}
I was using the wrong import. Changing to this fixed it
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
When I call and run this method from a java test class, I get a javax.persistence.TransactionRequiredException: Executing an update/delete queryerror. What could I have done wrong? I dug online and found a popular suggestion to add #Transactional annotation to the method. Unfortunately, this action did not resolve my query.
I imported import javax.transaction.Transactional;
#Transactional
public List<TestEntity> testUpdate() {
return execute("punit", entityManager -> {
Query query = entityManager.createNamedQuery("myDate").setMaxResults(20);
LocalDateTime dayBefore = LocalDateTime.now().minusDays(1);
query.setParameter("previousDate", Timestamp.valueOf(dayBefore)).executeUpdate();
return query.getResultList();
});
}
as form the configuration xml it is clear that you are going to use spring based transaction.
Please make sure that the import which you are using for #Transactional "org.springframework.transaction.annotation.Transactional"
in your case it might be "javax.transaction.Transactional"
Simply try below code. It's working fine for me.
package com.sample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
#Service
#Transactional
#Slf4j
public class Sample {
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE)
public Optional<Model> updateRecord() {
return null;
}
}
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 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)
Just wanna ask as I am stuck in the test cases and getting error as "Actually, there were zero interactions with this mock".
I have created an Dao Implementation class which is doing CRUD operation.
public class EmployeeDaoImpl implements EmployeeDao {
#Override
public void saveEmployee(EmployeeDetails employee) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
session.save(employee);
transaction.commit();
session.close();
}
}
And for this above class I am building the test using Mockito. So for my above saveEmployee method Session, TRansaction I have made it as Mock object and now I need to check session , save method and transaction as well.
So I have written the Mockito code below:
/**
*
*/
package sandeep.test;
import static org.junit.Assert.*;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import junit.framework.Assert;
import org.hibernate.Session;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import sandeep.DAO.EmployeeDao;
import sandeep.DAOImpl.EmployeeDaoImpl;
import sandeep.DAOImpl.HibernateUtil;
import sandeep.pojo.EmployeeDetails;
import static org.mockito.Mockito.*;
/**
* #author sandeep
*
*/
#RunWith(MockitoJUnitRunner.class)
public class EmployeeDaoImplTest {
#Mock
EmployeeDetails edt;
#Mock
Session session ;
#Mock
Transaction transaction;
#InjectMocks
EmployeeDaoImpl edi = new EmployeeDaoImpl();
#Before
public void setUp() throws Exception {
//eimpl = new EmployeeDaoImpl();
//emp= mock(EmployeeDao.class);
}
#After
public void tearDown() throws Exception {
}
#Test
public void testSaveEmployee(){
edi.saveEmployee(getEmployeeDetails());
// But here i am getting the error as zero interactions
verify(session, times(1)).save(EmployeeDetails.class);
}
public EmployeeDetails getEmployeeDetails(){
edt = new EmployeeDetails();
edt.setEname("sandeep");
edt.setId(2);
edt.setEnumber("hoi");
return edt;
}
}
I have debugged the code and the code is passing onto all the breakpoints in my IDE and when I execute this the 3 values it will be added to the database but my test case will fail as there are zero interactions.
The Session mock in your test is not the same object used in EmployeeDaoImpl#saveEmployee
Implement a constructor for EmployeeDaoImpl with a Session argument and use that argument in the saveEmployee() method. This allows your #InjectMocks to work as intended.
#RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
#Mock
Session session;
#InjectMocks
EmployeeDaoImpl edi;
#Test
public void testSaveEmployee(){
edi.saveEmployee();
verify(session, times(1)).save();
}
}
class Session {
void save() {
System.out.println("saving");
}
}
interface EmployeeDao {
void saveEmployee();
}
class EmployeeDaoImpl implements EmployeeDao {
private Session session;
public EmployeeDaoImpl(Session session) {
this.session = session;
}
#Override
public void saveEmployee() {
session.save();
}
}