How to rollback transaction after class (using TestNg and Spring)? - java

Let's say we have the next test:
#ContextConfiguration(classes = {MyDaoContext.class})
public class MyDaoTest extends AbstractTransactionalTestNGSpringContextTests {
#Autowired
private MyDao dao;
#Test
public void insertDataTest() {
// insert data here and test something
}
#Test(dependsOnMethods = "insertDataTest")
public void modifyPreviouslyInsertedDataTest() {
// get previously inserted data and test something
}
}
Second test will fall because when we have finished first test the inserted data are gone.
Is there a way to rollback a transaction after all tests have finished their work?

Each test runs in its own transaction and rollbacks at the end. You can tune that by adding #Rollback(false) to your first test.
The problem now is that the transaction of insertDataTest has completed so you should remove what it has created manually. To do that, the class you extends from has several utility methods such as deleteFromTables or deleteFromTableWhere.
This should go ideally in an #AfterClass or something.
But that's not what I would do. I would factor out the data that are inserted by insertDataTest in a shared utility. That way, you could call it again in your second test and remove the dependsOnMethods. Having such dependency is not recommended as you can't run that test in isolation.

Try the following (which would work in JUnit, I'm not sure about TestBG)
#ContextConfiguration(classes = {MyDaoContext.class})
public class MyDaoTest extends AbstractTransactionalTestNGSpringContextTests {
#Autowired
private MyDao dao;
#Test
#Rollback(false)
public void insertDataTest() {
// insert data here and test something
}
#Test(dependsOnMethods = "insertDataTest")
public void modifyPreviouslyInsertedDataTest() {
// get previously inserted data and test something
}
}
In that case of course you would have to delete the data from the first method manually

Related

How to write non-encapsulated unit tests?

I have an autowired variable
#Autowired
private DocumentConfig documentConfig;
I want to make tests for the DocumentService with various states of this configuration object. What are my options? What is the best option?
The first idea is this:
#Test
public void save_failure() {
documentConfig.setNameRequired(true);
/*
testing code goes here
*/
documentConfig.setNameRequired(false);
}
But I want to be somewhat more sure that the variable is reset after the test to not interfere with the other tests, to make sure only this test gets an error if it's the source of a problem.
My new idea was this:
#Before
public void after() { documentConfig.setNameRequired(true); }
#Test
public void save_failure() {
/*
testing code goes here
*/
}
#After
public void after() { documentConfig.setNameRequired(false); }
However, this doesn't work at all because Before and After execute for the whole file and not this single test. I would prefer not to make a new file just for one test.
I've now settled on a compromise:
#Test
public void save_failure() {
documentConfig.setNameRequired(true);
/*
testing code goes here
*/
}
#After
public void after() { documentConfig.setNameRequired(false); }
It seems to do everything I want but I have a few questions.
Assuming nameRequired starts as false, is this guaranteed not to interfere with the other tests?
Is there any way I can make this more clear? Both for my future self and for others.
You can create it before each test. Smth like
private DocumentConfig documentConfig;
#Before
public void createConfig() {
documentConfig = new DocumentConfig(mockedParams);
}
An often used approach is to set up a dummy DocumentConfig and inject it within the setUp() method (annotated with #Before) so that the entire context is reset within each test, for example:
#Before
public void setUp() {
this.documentConfig = new DocumentConfig();
this.documentConfig.setNameRequired(false);
this.service = new DocumentService(this.documentConfig);
}
In this case, I've set up a simple object with nameRequired being false. I could probably delete that statement, because a boolean field defaults to false anyways.
If you don't use constructor injection, and you don't have a setter for documentConfig, you'll have to use reflection to inject the field, for example:
ReflectionTestUtils.setField(this.service, "documentConfig", this.documentConfig);
Within your test you could now write something like this:
#Test
public void save_failure() {
this.documentConfig.setNameRequired(true);
// TODO: Implement test
}
Alternatively, you could mock DocumentConfig, so that you don't rely on its implementation to test DocumentService. I assume that you're calling isNameRequired() somewhere in the code of DocumentService, so you could mock it like this:
#Before
public void setUp() {
// Use a static import for Mockito.mock()
this.documentConfig = mock(DocumentConfig.class);
this.service = new DocumentService(this.documentConfig);
}
#Test
public void save_failure() {
// Use a static import for Mockito.when()
when(this.documentConfig.isNameRequired()).thenReturn(true);
// TODO: Implement test
}
Since this mocking/injection setup happens quite often, Mockito also has its own runner that allows you to get rid of the setUp() method, for example:
#RunWith(MockitoJUnitRunner.class)
public class DocumentServiceTest {
#InjectMocks
private DocumentService documentService;
#Mock
private DocumentConfig documentConfig;
#Test
public void save_failure() {
when(this.documentConfig.isNameRequired()).thenReturn(true);
// TODO: Implement test
}
}
It is not yet clear, which testing framework you use. For plain unit tests, make the value injectable by either a setter or constructor injection. Whatever suits your specific situation best.
If there's a lot (more than three ;-) ) of such values to be injected, you may consider introducing a configuration class to inject all those values as a single parameter.

Using #PostConstruct in a test class causes it to be called more than once

I am writing integration tests to test my endpoints and need to setup a User in the database right after construct so the Spring Security Test annotation #WithUserDetails has a user to collect from the database.
My class setup is like this:
#RunWith(value = SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#WithUserDetails(value = "email#address.com")
public abstract class IntegrationTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private Service aService;
#PostConstruct
private void postConstruct() throws UserCreationException {
// Setup and save user data to the db using autowired service "aService"
RestAssuredMockMvc.mockMvc(mockMvc);
}
#Test
public void testA() {
// Some test
}
#Test
public void testB() {
// Some test
}
#Test
public void testC() {
// Some test
}
}
However the #PostConstruct method is called for every annotated #Test, even though we are not instantiating the main class again.
Because we use Spring Security Test (#WithUserDetails) we need the user persisted to the database BEFORE we can use the JUnit annotation #Before. We cannot use #BeforeClass either because we rely on the #Autowired service: aService.
A solution I found would be to use a variable to determine if we have already setup the data (see below) but this feels dirty and that there would be a better way.
#PostConstruct
private void postConstruct() throws UserCreationException {
if (!setupData) {
// Setup and save user data to the db using autowired service "aService"
RestAssuredMockMvc.mockMvc(mockMvc);
setupData = true;
}
}
TLDR : Keep your way for the moment. If later the boolean flag is repeated in multiple test classes create your own TestExecutionListener.
In JUnit, the test class constructor is invoked at each test method executed.
So it makes sense that #PostConstruct be invoked for each test method.
According to JUnit and Spring functioning, your workaround is not bad. Specifically because you do it in the base test class.
As less dirty way, you could annotate your test class with #TestExecutionListeners and provide a custom TestExecutionListener but it seem overkill here as you use it once.
In a context where you don't have/want the base class and you want to add your boolean flag in multiple classes, using a custom TestExecutionListener can make sense.
Here is an example.
Custom TestExecutionListener :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
public class MyMockUserTestExecutionListener extends AbstractTestExecutionListener{
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
MyService myService = testContext.getApplicationContext().getBean(MyService.class);
// ... do my init
}
}
Test class updated :
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#WithUserDetails(value = "email#address.com")
#TestExecutionListeners(mergeMode = MergeMode.MERGE_WITH_DEFAULTS,
value=MyMockUserTestExecutionListener.class)
public abstract class IntegrationTests {
...
}
Note that MergeMode.MERGE_WITH_DEFAULTS matters if you want to merge TestExecutionListeners coming from the Spring Boot test class with TestExecutionListeners defined in the #TestExecutionListeners of the current class.
The default value is MergeMode.REPLACE_DEFAULTS.

testing HQL query on hbm2dll with Junit

How can I test HQL query with JUnit tests?
I have mapped Entity Class:
#Entity
#Table(name = "DOE")
public DomainObjectEntity {
//some attributes
}
which represents domain objects:
public class DomainObject {
//some attributes
}
I also have repository interface for my domain objects:
public interface DomainObjectRepository {
/**
* Gets entity by some attribute
*
*/
DomainObject getDomainObjectByObjectId(String objectId);
}
this interface is implemented in
#Repository
public class DomainObjectRepositoryImpl implements DomainObjectRepository {
#Inject
private DomainObjectEntityJPARepository entityRepository;
#Override
public DomainObjectgetDomainObjectById(String parcelId) {
//this converts my entity to domain object
return entityRepository.findByDomainObjectId(DomainObjectId.getStr()).getDomainObject();
}
}
my JPA Entity repository looks like this:
public interface DomainObjectEntityJPARepository extends JpaRepository<DomainObjectEntity, String> {
/**
* get DomainObject with requested id
*
* #param objectId - objects id
* #return DomainObject
*/
#Query("some query to be tested")
DomainObjectEntity findByDomainObjectId(#Param("objectId") String objectId);
}
and finally i want to write a simple JUnit test to check if query to findByDomainObjectId returns the correct object. I think my test should look like this:
create test object to put into DB
retrieve object from DB
compare objects
i have setted up a hbm2dll with import.sql to create and populate my DB but how can i access it from Junit test by calling method findByDomainObjectId?
Unit vs integration
First of all there is one thing you need to ask:
Do you want an Unit test or an Integration test
An unit test is fast (ie, milliseconds) to run and is, well, unitary - meaning it doesn't touch the database.
An integration test is heavier and, in case of a database test, will touch the db.
While it is usually preferable to create unit tests, in your case it seems you want an integration test - you actually want to test the call to the database and check if the query does what you think it does.
When to load data
I would suggest that in your integration test you don't preload data with import.sql (or at least think very carefully if this is what you want). The reason for this is that, as your test suite grows, you'll need the database in one state for test A, another state for test B, and so on, and you'll quickly end up in a situation where you'll have incompatible states.
You'll also end up with slower tests if you load too much data via SQL.
My advise would be: try to create/manipulate the data in your test.
How to test
How to test it depends on how you load your hibernate in production. Judging by the code you've shown, I believe you might be using a Dependency injection mechanism to start up hibernate's EntityManager (or SessionFactory), like Spring.
If this is the case, you can use spring to configure you integration test.
The main suggestions here would be:
Use rollback so that data is not stored between tests (guarantees test independence)
As your test suite grows, create an abstract class that holds the configuration and, e.g, exposes the EntityManager/sessionFactory. All your integration tests just need to extend this class, have the repository injected (in your example, inject DomainObjectRepositoryImpl into the test) and run the method you need to test.
Below is a very simple abstract test I have (in my case I'm using a SessionFactory):
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={
"/spring-SF-tests.xml",
"/spring-transaction.xml"
})
#TransactionConfiguration(transactionManager="txManager", defaultRollback=true)
public abstract class HibernateAbstractTest extends AbstractTransactionalJUnit4SpringContextTests {
#Autowired
protected SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public Session getSession(){
return sessionFactory.getCurrentSession();
}
public void save(Object object){
getSession().save(object);
}
public void update(Object object){
getSession().update(object);
}
}
Since I wanted to test only query, proper solution was to #autowire JPArepository and then in setup, populate it with data, this way tests were bound only with DB structure and not the data inside
public class EntityObjectJPARepositoryTest {
#Autowired
DomainObjectEntityJPARepository domainObjectRepo;
DomainObjectEntity entity;
#Before
public void Setup(){
entity = new DomainObjectEntity ()
//setup entity
domainObjectRepo.save(entity);
}
#Test
public void testfindByDomainObjectId(){
DomainObjectEntity res = domainObjectRepo.findByDomainObjectId(objectid);
Assert.assertEquals(entity.getId(), res.getId());
}
//everything else
}

JUnit: Setting Transaction boundry for Test Class

I want to start the database transactions before start of any test method and rollback all transactions at the end of running all tests.
How to do thing?What annotations should I use ?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/testApplicationContext.xml"})
public class MyTests{
public void setUp(){
//Insert temporary data to Database
}
#Test
public void testOne(){
//Do some DB transactions
}
#Test void testTwo(){
//Do some more DB transactions
}
public void tearDown(){
//Need to rollback all transactions
}
}
In Spring just add #Transactional annotation over your test case class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/testApplicationContext.xml"})
#Transactional //CRUCIAL!
public class MyTests{
Check out official documentation for very in-depth details, including #TransactionConfiguration, #BeforeTransaction, #AfterTransaction and other features.
Use #Before to launch method before any test and #After to launch method after every test. Use #Transactional spring's annotation over a method or over a class to start transaction and #Rollback to rollback everything done in transaction.
#Before
public void setUp(){
//set up, before every test method
}
#Transactional
#Test
public void test(){
}
#Rollback
#After
public void tearDown(){
//tear down after every test method
}
Also there is same issue solved in another way.
Use the annotation #Before for methods that have to run before every testmethod and #After to run after every testmethod.
You can take this article as a reference.

What class should I inherit to create a database integration test in case of Spring?

I want to create a unit test for integration testing. What class should I inherit to be able to commit/rollback transactions? AbstractTransactionalSpringContextTests is deprecated. It's recommended to use AbstractJUnit38SpringContextTests instead, but I cannot find how to control transactions there.
Check this and this
In short, you need:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="classpath:/applicationContext.xml")
public class YourTest {
#Transactional
public void someTest() {
}
}
That would mean you need JUnit 4.x
No need to put the #Transactional in the method level you can directly place it in class level
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="classpath:applicationContext.xml")
#TransactionConfiguration(defaultRollback=true, transactionManager="transactionManager")
#Transactional
public class YourTest {
#Rollback(true)
public void someTest() {
}
}

Categories