I'm developing a web app which is based in Spring 2.5 and hibernate 3. Recently I've introduced JUnit tests and I've done some integration tests using DBUnit framework. DBUnit is supposed to update the database with an xml dataset between one test and another, and it's working well, as I've seen.
However, when I update an element in a test, hibernate seems to catch this information and even I load the element in the following test, the information is the one I've modified. If I look the DB when the execution is paused, the Data Base is properly reseted by DBUnit. So I think it can be an Hibernate problem..
Is there a way to make a tearDown between tests saying I want a new hibernate session for my spring context? By the way, I'm not using Spring annotations and I get the Spring context by code:
String[] contextLocations = new String[2];
contextLocations[0] = "WebContent/WEB-INF/applicationContext.xml";
contextLocations[1] = "src/System_V3/test/applicationContext.xml";
context = new FileSystemXmlApplicationContext(contextLocations);
DBUnit setUp:
#Before
public void setUpBeforeClass() throws Exception {
handleSetUpOperation();
}
private static void handleSetUpOperation() throws Exception {
conn = getConnection();
conn.getConnection().setAutoCommit(false);
final IDataSet data = getDataSet();
try {
DatabaseOperation.REFRESH.execute(conn, data);
} finally {
conn.close();
}
}
private static IDatabaseConnection getConnection() throws ClassNotFoundException, SQLException,
DatabaseUnitException {
Class.forName("org.gjt.mm.mysql.Driver");
return new DatabaseConnection(DriverManager.getConnection(
"jdbc:mysql://localhost:3306/web_database", "root", "pass"));
}
private static IDataSet getDataSet() throws IOException, DataSetException {
ClassLoader classLoader = TestPrueba.class.getClassLoader();
return new FlatXmlDataSetBuilder().build(classLoader
.getResourceAsStream("System_V3/test/dataset.xml"));
}
Tests are done in JUnit 4 using only #Test annotations and test class is not extending any library class.
Any suggestion?
Not sure if this is something that can help you - but just in case...
Try to use session.clear() and use it in teardown method.
Please take a look here http://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/Session.html#clear()
According to spec session.clear() ->
Completely clear the session. Evict all loaded instances and cancel all pending saves, updates and deletions. Do not close open iterators or instances of ScrollableResults.
You need to execute your tests within a transaction. This can be achieved by setting the SpringJUnit4ClassRunner for your test. After this is configured you can use #Transactional annotation per test.
With this approach you can #Autowired your beans directly to your test too.
For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context-file.xml" })
public class MyTest {
#Autowired
private MyService myService;
#Transactional
#Test
private void myFirstTest() {
...
myService.executeSomething();
...
}
}
and of course, you can set the default behaviour to RollBack on your test class annotating it with #TransactionConfiguration(defaultRollback = true/false)
Related
I have a small spring boot app with database and rabbitmq usages.
So I would like to test with integration test (H2 + apache qpid).
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = TestSpringConfig.class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
As my app expect database and mq Im using #BeforeAll to start it:
#BeforeAll
public void before() {
startMessageBroker();
startDatabase();
}
The problem is that my web app starts before database/mq defined in #BeforeAll.
org.springframework.test.context.junit.jupiter.SpringExtension:
public class SpringExtension implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor,
BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback,
ParameterResolver {
// ...
#Override
public void beforeAll(ExtensionContext context) throws Exception {
getTestContextManager(context).beforeTestClass();
}
// ...
#Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
getTestContextManager(context).prepareTestInstance(testInstance);
}
// ...
Web app starts in postProcessTestInstance phase and #BeforeAll methods in beforeAll.
org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor:
private void execute(TestDescriptor testDescriptor, C parentContext, ExecutionTracker tracker) {
Node<C> node = asNode(testDescriptor);
tracker.markExecuted(testDescriptor);
C preparedContext;
try {
preparedContext = node.prepare(parentContext); // 1 <<<
SkipResult skipResult = node.shouldBeSkipped(preparedContext);
if (skipResult.isSkipped()) {
this.listener.executionSkipped(testDescriptor, skipResult.getReason().orElse("<unknown>"));
return;
}
}
catch (Throwable throwable) {
rethrowIfBlacklisted(throwable);
// We call executionStarted first to comply with the contract of EngineExecutionListener
this.listener.executionStarted(testDescriptor);
this.listener.executionFinished(testDescriptor, TestExecutionResult.failed(throwable));
return;
}
this.listener.executionStarted(testDescriptor);
TestExecutionResult result = singleTestExecutor.executeSafely(() -> {
C context = preparedContext;
try {
context = node.before(context); // 2 <<<
C contextForDynamicChildren = context;
context = node.execute(context, dynamicTestDescriptor -> {
this.listener.dynamicTestRegistered(dynamicTestDescriptor);
execute(dynamicTestDescriptor, contextForDynamicChildren, tracker);
});
C contextForStaticChildren = context;
// #formatter:off
testDescriptor.getChildren().stream()
.filter(child -> !tracker.wasAlreadyExecuted(child))
.forEach(child -> execute(child, contextForStaticChildren, tracker));
// #formatter:on
}
finally {
node.after(context);
}
});
this.listener.executionFinished(testDescriptor, result);
}
See points 1 and 2. There are executions of 'prepare' and then 'before'.
Im not sure is it issue of junit, SpringExtension or Im doing something wrong.
Any advice?
junit-jupiter: 5.0.1
spring-test: 5.0.0.RELEASE
spring-boot-test: 1.5.8.RELEASE
Checkout https://www.testcontainers.org/ it provides integration with JUnit to launch RabbitMQ and a database in docker containers as part of the JUnit testing. This makes integration tests much realistic because you using the same versions of database and message queue would be using in production.
This is by design, I think. Try to add the Bean post-processor/Context initializer to init/start your DB/rabbitMQ..
Is there any reason to start the DB and the message broker in test class? It seems to me that this is wrong by design. They both should be started along with your application context since they are part of your infrastructure.
It's not a responsibility of your tests to set up your infrastructure!
IMHO, a better way of doing things is the following:
Use H2 dependency with a test scope in maven + configure a starter in a way it starts H2 when the application context is starting
Start apache qpid (preferably embedded) on application start
In #Before just make sure you clean up stuff before running a test case
JUnit 5 [#BeforeAll] annotation is replacement of #BeforeClass annotation in JUnit 4.
It is used to signal that the annotated method should be executed before all tests in the current test class.
#BeforeAll should be used in static method
For More reading:
http://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
I'm trying to write integration test for Spring Boot application. I have Product and GalleryImage domain model. They are in one-to-many relationship.
public class Product {
...
#OneToMany(mappedBy = "product")
private List<GalleryImage> galleryImages;
}
I have a integration test as below:
#Test
public void testProductAndGalleryImageRelationShip() throws Exception {
Product product = productRepository.findOne(1L);
List<GalleryImage> galleryImages = product.getGalleryImages();
assertEquals(1, galleryImages.size());
}
However, this test gives me a LazyInitializationException. I searched on Google and StackOverFlow, it says that the session is closed after productRepository.findOne(1L), since galleryImages are lazily loaded, so galleryImages.size() gives me this exception.
I have tried to add a #Transactional annotation on the test, but it's still not working.
Hibernate Session has been closed after following line productRepository.findOne(1L).
You can try to do Hibernate.initialize(product.getGalleryImages())
public static void initialize(Object proxy)
throws HibernateException
Force initialization of a proxy or persistent collection.
Note: This only ensures intialization of a proxy object or collection; it is not guaranteed that the elements INSIDE the collection will be initialized/materialized.
To avoid Hibernate.initialize you can create a service.
#Service
#Transactional
public class ProductService {
#Transactional(readOnly = true)
public List<GalleryImage> getImages(final long producId) throws Exception {
Product product = productRepository.findOne(producId);
return product.getGalleryImages();
}
}
If you do use Spring Data JPA in you application then dynamic finder is a good alternative.
I had similar issues in the past and the solution was indeed adding the #Transactional annotation, but contrarily to what is proposed by #Anton M answer I believe that in this case we should annotate the test instead as proposed here.
It works both ways but the annotation should be placed if and where necessary, i.e. add it on the service if you need it there and not just for testing purposes.
For example, keep this as it was:
public class Product {
...
#OneToMany(mappedBy = "product")
private List<GalleryImage> galleryImages;
}
And add the annotation here:
#Test
#Transactional
public void testProductAndGalleryImageRelationShip() throws Exception {
Product product = productRepository.findOne(1L);
List<GalleryImage> galleryImages = product.getGalleryImages();
assertEquals(1, galleryImages.size());
}
My project is not Spring based .Its a java with Hibernate.Building tool - Maven.
I am loading data from one database to HSQL DB before running junits.
My DB util class:
public class DatabaseUtil {
SchemaLoad schemaLoad = new SchemaLoad();
DataLoad dataLoad = new DataLoad();
boolean dataLoaded = false;
static final String filename1 = "test1.txt";
static final String filename2 = "text2.txt";
void dbLoad() throws SQLException {
if (!dataLoaded) {
schemaLoad.cloneSchema(filename1);
dataLoad.exportData(filename2);
System.out.println("***********executed**********8");
dataLoaded = true;
}
}
}
First Test Case:
public class TestCase {
TrainRepository trainRepository = new TrainRepositoryImpl();
DatabaseUtil databaseUtil = new DatabaseUtil();
#BeforeClass
private void setUp() throws SQLException {
databaseUtil.dbLoad();
}
#Test
private void positiveTestCaseForTrainRepo() throws Exception {
//TestCases
}
Second Test case:
public class TestCase1 {
AirRepository airRepository = new AirRepositoryImpl();
DatabaseUtil databaseUtil = new DatabaseUtil();
#BeforeClass
private void setUp() throws SQLException {
databaseUtil.dbLoad();
}
#Test
private void positiveTestCaseForAirRepo() throws Exception {
//TestCases
}
Both the test cases are running fine.But Its executing databaseUtil.dbLoad(); method on each junit.
My question is I need to load the database only once ie before start of first junit and need to set some indicator .The further junits need to check the DB instance If DB instance is there it should not load the data ie DatabaseUtil class need to be singleton.
All the junits are running through maven suffire plugin during mvn install phase.
Kindly help me to achieve this.
void dbLoad() will be called each time.
then use a static variable to keep track
static boolean dataLoaded = false;
if you don't use spring you need to implement caching yourself. you have a few option. use static field with some kind of synchronization (in case you use/plan to use threads). other option is to switch to testng that gives you #BeforeGroup functionality so you can mark all your db tests and have your initialization run before.
Essence:
How can I auto-rollback my hibernate transaction in a JUnit Test run with JBehave?
The problem seems to be that JBehave wants the SpringAnnotatedEmbedderRunner but annotating a test as #Transactional requires the SpringJUnit4ClassRunner.
I've tried to find some documentation on how to implement either rollback with SpringAnnotatedEmbedderRunner or to make JBehave work using the SpringJUnit4ClassRunner but I couldn't get either to work.
Does anyone have a (preferably simple) setup that runs JBehave storries with Spring and Hibernate and transaction auto-rollback?
Further infos about my setup so far:
Working JBehave with Spring - but not with auto-rollback:
#RunWith(SpringAnnotatedEmbedderRunner.class)
#Configure(parameterConverters = ParameterConverters.EnumConverter.class)
#UsingEmbedder(embedder = Embedder.class, generateViewAfterStories = true, ignoreFailureInStories = false, ignoreFailureInView = false)
#UsingSpring(resources = { "file:src/main/webapp/WEB-INF/test-context.xml" })
#UsingSteps
#Transactional // << won't work
#TransactionConfiguration(...) // << won't work
// both require the SpringJUnit4ClassRunner
public class DwStoryTests extends JUnitStories {
protected List<String> storyPaths() {
String searchInDirectory = CodeLocations.codeLocationFromPath("src/test/resources").getFile();
return new StoryFinder().findPaths(searchInDirectory, Arrays.asList("**/*.story"), null);
}
}
In my test steps I can #Inject everything nicely:
#Component
#Transactional // << won't work
public class PersonServiceSteps extends AbstractSmockServerTest {
#Inject
private DatabaseSetupHelper databaseSetupHelper;
#Inject
private PersonProvider personProvider;
#Given("a database in default state")
public void setupDatabase() throws SecurityException {
databaseSetupHelper.createTypes();
databaseSetupHelper.createPermission();
}
#When("the service $service is called with message $message")
public void callServiceWithMessage(String service, String message) {
sendRequestTo("/personService", withMessage("requestPersonSave.xml")).andExpect(noFault());
}
#Then("there should be a new person in the database")
public void assertNewPersonInDatabase() {
Assert.assertEquals("Service did not save person: ", personProvider.count(), 1);
}
(yes, the databaseSetupHelper methods are all transactional)
PersonProvider is basicly a wrapper around org.springframework.data.jpa.repository.support.SimpleJpaRepository. So there is access to the entityManager but taking control over the transactions (with begin/rollback) didn't work, I guess because of all the #Transactionals that are done under the hood inside that helper class.
Also I read that JBehave runs in a different context?session?something? which causes loss of controll over the transaction started by the test? Pretty confusing stuff..
edit:
Editet the above rephrasing the post to reflect my current knowledge and shortening the whole thing so that the question becomes more obvious and the setup less obstrusive.
I think you can skip the SpringAnnotatedEmbedderRunner and provide the necessary configuration to JBehave yourself. For example instead of
#UsingEmbedder(embedder = Embedder.class, generateViewAfterStories = true, ignoreFailureInStories = false, ignoreFailureInView = false)
you can do
configuredEmbedder()
.embedderControls()
.doGenerateViewAfterStories(true)
.doIgnoreFailureInStories(false)
.doIgnoreFailureInView(false);
Besides: why do you want to rollback the transaction? Typically you are using JBehave for acceptance tests, which run in a production-like environment. For example you first setup some data in the database, access it via Browser/Selenium and check for the results. For that to work the DB transaction has to be committed. Do you need to clean-up manually after your tests, which you can do in #AfterStories or #AfterScenario annotated methods.
I made it work by controlling transaction scope manually, rolling it back after each scenario. Just follow the official guide how to use Spring with JBehave and then do the trick as shown below.
#Component
public class MySteps
{
#Autowired
MyDao myDao;
#Autowired
PlatformTransactionManager transactionManager;
TransactionStatus transaction;
#BeforeScenario
public void beforeScenario() {
transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
#AfterScenario
public void afterScenario() {
if (transaction != null)
transactionManager.rollback(transaction);
}
#Given("...")
public void persistSomething() {
myDao.persist(new Foo());
}
}
I'm not familiar with JBehave, but it appears you're searching for this annotation.
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true).
You could also set defaultRollback to true in your testContext.
When I run my unit tests in isolation they work fine ie. (omitted the asserts)
#Test
public void testSave()
{
EntityManagerHelper emh = new EntityManagerHelper();
LevelDAO dao = new LevelDAO();
Level l = new Level();
l.setName("aname");
emh.beginTransaction();
dao.save(l);
emh.commit();
}
then running this individual test below no problem
#Test
public void testUpdate()
{
EntityManagerHelper emh = new EntityManagerHelper();
LevelDAO dao = new LevelDAO();
Level l = new Level();
l.setName("bname");
l.setLevelid(1);
emh.beginTransaction();
dao.update(l);
emh.commit();
}
When they run at same time in sequence I recieve that error - Transaction is currently active. Is there a way to allow each unit test to run only after a transaction from previous piece of work is not active? Should I be looking at Spring instead?
Update
The EntityManagerHelper gains access to the persistence context like so
emf = Persistence.createEntityManagerFactory("bw_beta");
threadLocal = new ThreadLocal<EntityManager>();
which looks like the problem
So a hacky workaround was to use define locally ie.
EntityManagerFactory factory = Persistence.createEntityManagerFactory("bw_beta");
EntityManager entityManager = factory.createEntityManager();
entityManager.getTransaction().begin();
dao.save(l);
entityManager.persist(l);
entityManager.getTransaction().commit();
Pretty sure there's a better way - maybe using Spring?
Pretty sure there's a better way - maybe using Spring?
Yes, Spring cleans it up a lot and gives you control on what you'd like to run within a transaction without polluting the actual test.
With Spring, your tests would look something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:META-INF/conf/spring/application-context.xml",
"classpath:META-INF/conf/spring/test-datasource-spring-config.xml" })
#TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
public class LevelDaoTest {
#Resource( name="levelDao" )
LevelDao levelDao;
#Test
public void shouldSaveNewLevels() {
Level l = new Level();
l.setName("aname");
levelDao.save(l);
// assert
}
#Test
public void shouldUpdateExistingLevels() {
Level l = new Level(); // or I would assume, you'd read this level back from DB, or set a proper ID, so the DAO will know to update it.. But that is besides the point
l.setName("bname");
levelDao.update(l);
// assert
}
}
Take a look at Spring Documentation under Testing => Transaction Management to get more details.
P.S. From your example:
dao.save(l);
entityManager.persist(l);
Looks really strange, as usually you would encapsulate entityManager within a DAO, so all you'd need to do is dao.save(l)
For anyone that may be having this issue here is how I resolved it. I was doing multiple saves and I kept getting this error. You do not want to begin multiple transactions without checking if it is active.
if(!entityManager.getTransaction().isActive())
entityManager.getTransaction().begin();
dao.save(l);
entityManager.persist(l);
entityManager.getTransaction().commit();
I implemented a Singleton approach to handle it.