Junit Ignores my #Transactional annotation in services - java

We have spring services in our project. for example UserService.
These services use the #Transactional annotation.
We just tried to execute junit tests that involves these services and for some reason the #Transactional annotations are ignored. (For example, i use a #Test without #Transactional , it goes to a service that is indeed anotated #Transactional, in that service it saves a new object(entity) the entity is being saved in the DB before the end of the transaction (Commit).)
I would like to point out that we don't want our junit tests to be annotated with #Transactional themselves.
Is it possible to enforce junit to not ignore the #Transactional annotations in our services?
Thanks,

You should explicitly tell JUnit to use transactions (example from reference):
#ContextConfiguration(locations={"example/test-context.xml"})
#TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
public class CustomConfiguredTransactionalTests {
// class body...
}
Then you can use #NonTransactional/#Rollback/etc annotations in order to control the behavior for particular methods.

Manual configuration is required as previously mentioned, as transactions are not persisted in Spring test contexts.
Spring reference for this topic can be found here

Related

How to save entities along with their related entities in spring boot integration testing?

I have an integration test on a endpoint of creating a user with its related entities. It turns out that the related entities were not persisted with the user entity.
However, it is working fine when running the normal spring boot application. Is it possible to achieve this during testing?
This is the log when running the integration test of the endpoint
And this is the log when calling from Postman to the normal application
as you can notice the roles are not inserted to the rel_mi_user__mi_user_role table during integration testing.
The setup source code for the integration test is shown below
#Autowired
private PasswordEncoder passwordEncoder;
#Autowired
private MockMvc mockMvc;
#Autowired
private MiMiUserRepository miMiUserRepository;
private static Logger log = LoggerFactory.getLogger(MiMiUserResourceIT.class);
#Test
#Transactional
void testRegisterBackOfficeUser() throws Exception {
MiMiUserRegistrationDTO userRegistrationDTO = new MiMiUserRegistrationDTO();
userRegistrationDTO.setPassword("test12345");
userRegistrationDTO.setEmail("john#gmail.com");
userRegistrationDTO.setContactNo("0188991122");
userRegistrationDTO.setUserRoles(List.of("BACKOFFICE"));
MiMiUserProfileRegistrationDTO profile = new MiMiUserProfileRegistrationDTO();
profile.setSalutation("Mr");
profile.setFirstName("John");
profile.setLastName("Lee");
userRegistrationDTO.setProfile(profile);
mockMvc
.perform(
post("/v1/p/user/register/back-office")
.contentType(MediaType.APPLICATION_JSON)
.content(TestUtil.convertObjectToJsonBytes(userRegistrationDTO))
)
.andExpect(status().isCreated());
MiUser u = miMiUserRepository.findOneWithMiUserRolesByEmailIgnoreCase(userRegistrationDTO.getEmail()).get();
assertThat(u.getUserStatus()).isEqualTo(UserStatus.NEW);
}
I guess it has to do with the fact that your whole test is annotated with #Transactional. When you annotate your test method with #Transactional, everything that happens during that test happens within one single transaction. As you might know, changes are only flushed to the database at the end of a transaction unless you call the flush method manually which is generally discouraged unless you know what you are doing because you interfere with your JPA provider's flushing optimization.
Only when the changes are flushed to the DB, auto-generated properties like an ID is set on your entity and DB constraints are evaluated. Yes, you're reading it right, you might even violate a DB constraint within your test without the test failing!
Here you can find more reasons why you should not annotate your tests with #Transactional.
If you need to save several entities in the arrange part of your test, you can autowire the Spring TransactionTemplate and create all your entities within a manual transaction. The same might apply to asserts where you load an entity from the DB and want to evaluate a lazy-loaded collection of said entity. As far as I can tell, neither of said scenarios apply to your test, just try omitting the #Transactional and your test should work.
Side note: if you write #DataJpaTests you always need to call the saveAndFlush method of the repository instead of the save method because #DataJpaTests are always transactional.

Rollback Transactions when testing with Spring MockMvc

I have seen multiple threads about it. However, no one really solves my problem.
I have a Spring Boot 2.3 application with the three traditional layers: Controller, Service and DAO. The transactions are declared in my Service layer.
I would like to test my Controller layer using MockMvc, and I want the transactions to rollback at the end of the tests so that they all remain independent. However, I don't want the tests to give the Controller classes an access to the transactional context in order to have the same configuration as in runtime.
I created the following class:
#SpringBootTest
#AutoConfigureMockMvc
public class ApiIT {
#Autowired
private MockMvc mvc;
#Test
void restEndpointTest() {
...
This configuration doesn't rollback the transactions at the end of the tests.
When I annotate the class with #Transactional, they rollback, but the Controller classes can access the transactional context.

Should I use #Transactional or #Rollback for DAO test classes?

I have a dao layer for my database. Now, I am writing some integration tests for it. I wonder if #Transactional or #Rollback should be used in a test class, as they both revert the changes to the database. Which one would be a good practice and in what conditions?
I tried using both of them and they both work in my case. I have a #Before annotated method in my class.
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace = NONE)
#DataJpaTest
// #Transactional or #Rollback?
public class TestDao {
#Autowired
private ConcreteDao concreteDao;
#Before
public void cleanUp(){ . . . }
#Test
public void testSaveAllEntries(){ . . . }
// and other tests
}
agree with #michael Don't make your tests #transactional at all (but your service layer)This means that all your service layer/persistence layer methods invoked through the tests would start their own transactions (you made them transactional, did you?) and flush the changes upon commit. So you are guaranteed to notice if something blows up on flush, and quite possible that after a while, a database full of junk test data
Running tests with databases usually will be done with integration tests. To keep it simple you could setup a h2 with the dialect you need. Prepare the database structures and run the service calls you need. assert the expecting results and mark the test method as dirties context (as annotation), or resetup the database with each test, otherwise saved results of a test could have impact of another test. In this way you can also test the transaction handling of your services.
Adding transactional to your tests will change your behaviour of your business logic. keep those out of your tests.

Spring Data CrudRepository and Transactions

I'm trying to implement transactions on a CrudRepository Interface. I'm a beginner with this and my current problem is that when receiving a lot of requests from different clients, I'm sometimes getting a duplicate.
To avoid that I wanted to use SQL Transactions and their implementation with Spring but I'm unable to get it working.
Here is how I've tried to do it :
#Repository
#EnableTransactionManagement
#Transactional
public interface ApplicationPackageDao extends CrudRepository<ApplicationPackage, Long> {
/**
* Find if a record exists for this package name ,
* #param packageName
* #return
*/
#Transactional
ApplicationPackage findByPackageName(String packageName);
}
However it doesn't seem to work.
I tried to add the #Transactionnal annotations earlier in the Java methods I'm calling but I can't get it working either.
How am I supposed to work with transactions on CrudRepository ?
Or am I using completely the wrong thing?
In addition to crm86's answer some more notes to the #Transactional annotation:
It seems to be best practice to annotate the entry points into your application (e.g. your web controller methods or the main method of a scheduled batch). By using the annotation attribute TxType you can ensure constraints/conditions in methods which are located deeper in your application (e.g. TxType.MANDATORY would throw if no trx-context is running, etc.).
The #Transactional annotation has only an effect if the class is loaded as spring bean (e.g. #Component annotation at class level).
Remember that only RuntimeException's lead to a rollback. If you want a checked Exception leading to a rollback you have to enumerate each such Exception by using the attribute rollbackOn.
The annotation at class level is valid for all public methods of this class. Method level annotations override those at the class level. The repeated annotation in your example above (first at class level, then at method level) has no effect.
What I suggest:
Check your context and configuration classes with #Configuration annotation. From the documentation:
The #EnableTransactionManagement annotation provides equivalent
support if you are using Java based configuration. Simply add the
annotation to a #Configuration class
#EnableTransactionManagement and only looks
for #Transactional on beans in the same application context they are
defined in
Then you could use #Transactional in your service even in a method
Hope it helps

JUnit multiple transaction managers for spring tests

I'm using Spring 3.0.5 and Junit 4.8.2
Is it possible to use multiple transaction managers during tests?
Basically I'm try for something like this. I need to add and remove content from two separate databases during the tests.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/applicationContext-test.xml" })
#TransactionConfiguration(transactionManager = "txMgrA", defaultRollback = true)
#TransactionConfiguration(transactionManager = "txMgrB", defaultRollback = true)
#Transactional
public class SampleTest {
...
}
Looking at Spring 5 TransactionalTestExecutionListener implementation, it looks it only supports one TransactionManager per thread, what seems to be a design flaw of this listener, probably the same as you came across in 2011 :)
However, currently it's possible to workaround this using ChainedTransactionManager. If you have multiple transaction managers, you can define yet another transaction manager in your test context:
#Configuration
class TestTransactionConfig {
#Bean("testTransactionManager")
public PlatformTransactionManager chainedTransactionManager(
#Qualifier("transactionManager1") PlatformTransactionManager transactionManager1,
#Qualifier("transactionManager2") PlatformTransactionManager transactionManager2
) {
return new ChainedTransactionManager(transactionManager1, transactionManager2);
}
}
Now, you can define your base class for tests using this transaction manager:
#RunWith(SpringRunner::class)
#Transactional("testTransactionManager")
class BaseTransactionalTest {
}
For all derived classes all test methods will now be wrapped in both transactions, which will be finally rolled back by TransactionalTestExecutionListener.
Since Java will not allow multiple annotations of the same type per element, you must find another way to configure it. #TransactionConfiguration is interpreted by TransactionalTestExecutionListener, whose getTransactionManager method only returns a single PlatformTransactionManager. It looks at #Transactional but seems to ignore the value qualifier that was added in Seam 3.0.
#Transactional itself only supports a single transaction manager. How is the real application configured? You must be using #Transactional("<qualifier>") (as in the docs), right?
If you just use #Transactional with different tx managers on different methods, then the simplest solution is to just split your test class.
Are you nesting the transactions? That is, you have #Transactional("tm1") on one method, which calls a nested method that has #Transactional("tm2")? Sounds a little unusual. You could try to set up your test in the same way -- have two test #Services, each with the appropriate #Transactional annotations, that are proxied with tx:advice as usual. The outer service sets up the outer txn; the inner service sets up the inner txn and contains the actual test code. You can't use #Rollback, but hey, hacks ain't pretty.
Another option would be to create your own PlatformTransactionManager that delegates to the two other managers (for testing purposes only).
Maybe better would be to just give up and manually manage the two transactions in the test's #Before/#After methods.
Best would be to use JTA global transactions. Hopefully you're not actually nesting separate transactions and this is all moot ;)

Categories