Integration test values are inserted to database - spring-boot

I am trying to test a JpaRepository with spring boot integration tests. I'm using the #DataJPATest annotation without the embedded db. I'm using the same MySQL db that I use for development to run integration tests.
#DataJpaTest
#AutoConfigureTestDatabase(replace = NONE)
class UserRepositoryTest {
#Autowired
UserRepository userRepository;
#Test
void findByUsername() {
User u = new User();
u.setUsername("ML");
u.setFirstname("Micheal");
u.setLastname("Lane");
u.setActive(true);
userRepository.save(u);
assertThat(userRepository.findByUsername("M21")).isNotNull();
}
}
The value I inserted in the test is inserted into the actual data base. But I thought with #DataJpaTest the transaction is rolled back at the end of the test.
By default, tests annotated with #DataJpaTest are transactional and roll back at the end of each test. They also use an embedded in-memory database (replacing any explicit or usually auto-configured DataSource). The #AutoConfigureTestDatabase annotation can be used to override these settings.
So why is my data getting saved to the database? Is it because I'm not using the embedded database?
Dialect used : spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
**********************************************************8
It started to rollback once I remove findByUsername() part form the test method
User u = new User();
u.setUsername("ML");
u.setFirstname("Micheal");
u.setLastname("Lane");
u.setActive(true);
userRepository.save(u);
//assertThat(userRepository.findByUsername("M21")).isNotNull();
So I guess It doesn't work because there are 2 queries. I will have to use an already saved data for findByUsername() test

I believe it is due to the auto-wire. When you auto-wire it instantiates the interface via dependency injections. If you don't want data to be stored when running the test I suggest you look at mockito. There you can mock the repos so you can test all the methods used when storing data but never actually saves it

Can you try by using this dialect.
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
Hope this should work.

Related

Clean database with Flyway after each test class

we are in situation where we cannot simple rollback data after test, so we decide to use Flyway java API like this:
#Autowired
protected Flyway flyway;
#AfterEach
public void restoreDatabase() {
flyway.clean();
flyway.migrate();
}
Is possible execute clean and migrate after each test class instead of test method? I need call this in #AfterAll annotated static method, but this type of methods have to be static so I cannot use autowired component Flyway. Can you advice me any workaround? Thank you.
The following solution may help you.
Besides the #Rollback annotation there is also the possibility to mark a class (or method) as "dirty" with the annotation org.springframework.test.annotation.DirtiesContext. This will provide the test cases a fresh context. From the Java Docs:
Test annotation which indicates that the ApplicationContext associated with a test is dirty and should therefore be closed and removed from the context cache.
Use this annotation if a test has modified the context — for example, by modifying the state of a singleton bean, modifying the state of an embedded database, etc. Subsequent tests that request the same context will be supplied a new context.
#DirtiesContext may be used as a class-level and method-level annotation within the same class or class hierarchy. In such scenarios, the ApplicationContext will be marked as dirty before or after any such annotated method as well as before or after the current test class, depending on the configured methodMode and classMode.
Let me show you an example:
#RunWith(SpringRunner.class)
#SpringBootTest
#DirtiesContext(classMode = ClassMode.BEFORE_CLASS)
public class SomeTestClass {
#Autowired
private ExampleController controller;
#Test
public void testSomething() {
//Do some testing here
}
Now in this case, with an embedded DB (like H2), a fresh DB will be started containing no changes from previous transactions.
Please note that this will most probably slow down your test cases because creating a new context can be time consuming.
Edit:
If you watch the log output you'll see that Spring creates a new application context with everything included. So, when using an embedded DB for the test cases, Spring will drop the current DB and creates a new one and runs all specified migrations to it. It's like restarting the server which also creates a new embedded DB.
A new DB doesn't contain any commits from previous actions. That's why it works. It's actually not hacky imo but a proper set up for integration tests as integration tests mess up the DB and need the same clean setup. However, there are most probably other solutions as well because creating new contexts for every test class may slow down the execution time. So, I would recommend to annotate only classes (or methods) which really needs it. On the other hand, unit tests are in most cases not very time critical and with newer Spring versions lazy loading will speed up startup time.

How can I replicate the rollback from Entity Manager with JPA Repository?

I am converting a project from Spring framework to Spring boot, so I am no longer using persistence files and other config files.
Also, I gave up using Entity Manager, instead I created repositories which extend JPA Repository, so that I can use the functions from there. The only thing is that I have some unit tests and in the Spring framework, at the end of each test there is a finally clause which has a rollback, so that the data from the database to be specific to each test.
How can I do that without Entity Manager? I tried using the flush() method, but no result...
By default, data JPA tests are transactional and roll back at the end of each test.
You can refer to Enabling and Disabling Transactions in spring test documentation which indicate :
Annotating a test method with #Transactional causes the test to be run within a transaction that is, by default, automatically rolled back after completion of the test. If a test class is annotated with #Transactional, each test method within that class hierarchy runs within a transaction. Test methods that are not annotated with #Transactional (at the class or method level) are not run within a transaction. Furthermore, tests that are annotated with #Transactional but have the propagation type set to NOT_SUPPORTED are not run within a transaction.
Tips :
If you want to commit the transaction for a reason or another, you can use the #Commit annotation or less properly #Rollback(false) on method or class level.
If you want to execute code outside of a transaction ( before or after ) you can use #BeforeTransaction and #AfterTransaction on public methods that have no return ( return void ) or on default method of interfaces.

Integration testing with spring declarative caching

I'm trying to write integration tests for a Spring Boot 2 Application.
One test should test updating a value via REST.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureTestEntityManager
#Transactional
public class TenantEndpointIT {
#Autowired
private TestRestTemplate template;
#Autowired
private TestEntityManager entityManager;
#Test
public void nok_updateValueForbidden() {
}
}
Now, I thought the cleanest way was to create the value with the TestEntityManager in the #Before method, then test the REST endpoint in the actual test.
But the service called by the REST Endpoint is annotated with Spring Caching annotations. So the test fails if I do that. I could use the service directly or make a second REST call. That creates problems with other tests using the same Value, because even if the DB is rolled-back, the cache seems to contain the value. (Now I'm using #DirtiesContext).
My question is, how do you correctly integration test services with #Cachable?
Is there a way to get the Cache and explicitly put/remove?
I tried autowiring a CacheManager, but it won't find one and fails.
If you add #AutoConfigureCache on your test, it will override whatever cache strategies you've defined in your app by a CacheManager that noops. That's pretty useful if you want to make sure that cache doesn't interfere with your tests.

Initializing Mongo DB for Spring Boot Test

I'm building a Spring Boot application that uses Spring Data Repositories with MongoDB. I'm attempting to create a Spock functional Spec to test my repository but I can't figure out the appropriate way to initialize the Mongo DB in preparation for testing. So far I have tried the following:
Do Nothing - This resulted in the same database being used from test to test with my tests failing after.
Drop the database before testing - This resulted in the indexes being lost and me being unable to test my unique indexes.
Here's what I was doing with dropping the database:
#ContextConfiguration(classes = MyApp, loader = SpringApplicationContextLoader)
#ActiveProfiles('test')
class UserRepositoryTest extends Specification {
#Shared
boolean mongoReset = false
#Autowired
MongoTemplate mongoTemplate
#Autowired
UserRepository userRepository
void setup() {
if (!mongoReset) {
mongoTemplate.getDb().dropDatabase()
mongoReset = true
}
}
}
Ideally I'd like to be able to use something similar to the data.sql method provided with JPA repositories.
We usually recommend to rather use the repository to wipe the database (i.e. calling userRepository.deleteAll()). Dropping the database has the downside of wiping all the indexes that might have been created during context bootstrap time.

Spring JUnit Cannot Run TransactionSynchronizationManager.bindResource, while Normal Spring Env Can

I have a working Spring/Hibernate based web application. Now I need to use Spring JUnit 4 to write an integration test for it.
Here is my test code:
#RunWith(SpringJUnit4ClassRunner.class)
#TransactionConfiguration(transactionManager = "hibernateTransactionManager", defaultRollback = true)
#ContextConfiguration(locations = {"classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext-xxx.xml", "classpath:applicationContext.xml"})
public class TestXXX extends AbstractTransactionalJUnit4SpringContextTests {
#Test
public void testXXXExecute(){...}
}
With this setting of the test environment, I can access all the beans and use the sessionFactory bean to get data from database.
The problem happens with one test, with calls a production code using TransactionSynchronizationManager to implement two-phase commit.
The code looks like this:
TransactionSynchronizationManager.bindResource(sessionFactoryA, new SessionHolder(sessionA));
TransactionSynchronizationManager.bindResource(sessionFactoryB, new SessionHolder(sessionB));
The code performs well in the dev and production environment, where the full Spring Framework is running. During the JUnit run, the exception is:
[junit] java.lang.IllegalStateException: Already value [org.springframework.orm.hibernate3.SessionHolder#6311e359] for key [org.hibernate.impl.SessionFactoryImpl#56d47236] bound to thread [main]
I cannot use 2 lines of #TransactionConfiguration in the test class to define the two transaction managers that corresponds to the two data sources and two sessionFactory objects. I wonder if AbstractTransactionalJUnit4SpringContextTests cannot duplicate the transaction environment of the real Spring Framework.
Without seeing more of your code, it is difficult to tell exactly what is wrong. In instances where I've seen this error in the past, it was because files named in the #ContextConfiguration included each other. For example, you might have file
applicationContext-bean-cfg.xml
that includes
applicationContext-hibernate-cfg.xml, but then have
#ContextConfiguration(locations = {"classpath:/applicationContext-bean-cfg.xml", "classpath:/applicationContext-hibernate-cfg.xml"}).
The other thing to check is that one of the files doesn't already have a transaction manager defined.

Resources