Clean database with Flyway after each test class - spring

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.

Related

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.

DataSource per test with Cucumber and SpringBoot

I have a cucumber test setup with spring boot. There are a large number of integration tests which take a while to run. Because they share a database, the new threaded mode in cucumber 4+ does not work (as expected).
Ideally this would work in Junit also.
For each test, I would like to create a dynamic datasource with a new database/datasource instance that the test can use independently of others, allowing it to run multithreaded (and use the 12 cores I have available).
I have tried #Scope("cucumber-glue") and #Scope("prototype") on the DataSource bean, but this results in org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?.
If I use the prototype scope, the bean creation method gets called each time, but gives this error, as does the glue scope.
I have also added #DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) to classes and methods, without the scope, but this seems to do nothing.
Is there a way I can either:
1. create a new datasource for each test and have hibernate create the tables?
2. pool a set of datasources that could be used by tests?
3. Populate/reinitialize the context accordingly?
A side consequence of this is that I don't believe my context is getting recreated properly between tests for other instances of the scoping.
#Configuration
public class H2DynamicDataSource {
#Autowired
private Environment env;
#Bean
#Scope("prototype")
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
Cheers
R
Hopefully you've solved this.
I just went through something similar and felt like cruising around SlackOverflow to see if anyone was doing something similar (or perhaps asking about how to do something like this) and saw this post. Figured this would be a good place to drop this information in case anyone else tries to go down this road.
I think your asking two questions:
How to get multithreading working with Cucumber and Junit Cucumber?
multithreading works great but you are constrained to a single lane
of test execution by junit if your using either the Cucumber or
SpringJUnit4ClassRunner runner junit runner classes. (I suspect this is why #CucumberOptions doesnt contain a thread arg. It's useless with the current Cucumber runner implementation.)
In order to get this working I had to write my own test runner that
would defer a the entire test execution to Cucumbers Runtime
object. I used a custom cucumber plugin to cache the results and
then relay those results back to junit when asked to 'run' the
individual tests. With Cucumber behind the wheel I was able to allow
any arbitrary number of threads for my tests.
How to ensure a new spring datasource for each test?
If you're unfamiliar with how Spring cache's contexts for tests you should have a look through the documentation here https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-ctx-management-caching but the tl;dr: Spring's TestContextManager(this is whats under the hood of Springs junit runner) will create and store a spring context in a map keyed by your test configuration. DirtiesContext will force the context to reload.. but otherwise two tests with the same parent class are effectively guaranteed to execute in the same test context. If your datasource is a spring datasource that is initialized during boot.. you definitely need to refresh this context between tests.
Putting those two concepts together.. Cucumber-Spring provides a way to create a new context (consistent with cucumbers new 'world' per test design) for every test but it does so by disregarding any existing spring contexts or data contained therein. This may actually be helpful to you if you can trust Cucumber-Spring to correctly stand up your datasource for you.. but in my situation we had a bunch of issues using this fake context and really needed to pull objects from the default spring context. In my case I had to incorporate Springs TestContextManager into my custom plugin AND write my own Cucumber BackendSupplierimplementation to hijack Cucumbers dependency injection mechanism (effectively replacing Cucumber-Spring)
All in all, what your trying to do is a major PITA and I hope the Cucumber folks make this easier at some point.. but what your trying to do is definitely possible. Hope you got it working!

Spring Context Test With Just One Bean

What's the recommended way to run a spring boot test where only the one subject under test is configured in the context.
If I annotate the test with
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.profiles.active=test")
#ContextConfiguration(classes = MyTestBean.class)
Then it seems to work - the test passes, the context starts quickly and seems to only contain the bean that I want. However, this seems like an incorrect use of the #ContextConfiguration(classes = MyTestBean.class) annotation. If I understand correctly the class that I reference is supposed to be a Configuration class, not a regular spring service bean or component for example.
Is that right? Or is this indeed a valid way to achieve this goal? I know there are more complex examples like org.springframework.boot.test.autoconfigure.json.JsonTest which use #TypeExcludeFilters(JsonExcludeFilter.class) to control the context - but this seems overkill for my use case. I just want a context with my one bean.
Clarification
I know that I can just construct the one bean I am testing as a POJO without a spring context test and remove the three annotations above. But in my precise use case I am actually reliant on some of the configuration applied to the context by settings in the application-test.properties file - which is why I've made this a Spring Boot test with a profile set. From my perspective this isn't a plain unit test of a single class in isolation of the spring context configuration - the test is reliant on certain configuration being applied (which is currently provided by the spring boot app properties). I can indeed just test the components as a POJO by creating a new instance outside of a spring context, I'm using constructor injection making the providing of necessary dependencies simple but the test does rely on things like the log level (the test actually makes assertions on certain logs being produced) which requires that the log level is set correctly (which is currently being done via logging.level.com.example=DEBUG in a properties file which sets up the spring context).
For starters, reading the documentation first (e.g., the JavaDoc linked below in this answer) is a recommend best practice since it already answers your question.
If I understand correctly the class that I reference is supposed to be
a Configuration class, not a regular spring service bean or
component for example.
Is that right?
No, that's not completely correct.
Classes provided to #ContextConfiguration are typically #Configuration classes, but that is not required.
Here is an excerpt from the JavaDoc for #ContextConfiguration:
Annotated Classes
The term annotated class can refer to any of the following.
A class annotated with #Configuration
A component (i.e., a class annotated with #Component, #Service, #Repository, etc.)
A JSR-330 compliant class that is annotated with javax.inject annotations
Any other class that contains #Bean-methods
Thus you can pass any "annotated class" to #ContextConfiguration.
Or is this indeed a valid way to achieve this goal?
It is in fact a valid way to achieve that goal; however, it is also a bit unusual to load an ApplicationContext that contains a single user bean.
Regards,
Sam (author of the Spring TestContext Framework)
It is definitely a reasonable and normal thing to only test a single class in a unit test.
There is no problem including just one single bean in your test context. Really, a #Configuration is (typically) just a collection of beans. You could hypothetically create a #Configuration class just with MyTestBean, but that would really be unnecessary, as you can accomplish doing the same thing listing your contextual beans with #ContextConfiguration#classes.
However, I do want to point out that for only testing a single bean in a true unit test, best practice ideally leans towards setting up the bean via the constructor and testing the class that way. This is a key reason why the Spring guys recommend using constructor vs. property injection. See the section entitled Constructor-based or setter-based DI of this article, Oliver Gierke's comment (i.e. head of Spring Data project), and google for more information. This is probably the reason you're getting a weird feeling about setting up the context for the one bean!
You can also use ApplicationContextRunner to create your context using a test configuration of your choice (even with one bean if you like, but as other people have already mentioned for one bean it's more reasonable to use the constructor the classical way without using any spring magic).
What I like this way of testing is the fact that test run very fast since you don't load all the context. This method is best used when the tested bean doesn't have any Autowired dependencies otherwise it's more convenient to use #SpringBootTest.
Below is an example that illustrates the way you can use it to achieve your goal:
class MyTest {
#Test
void test_configuration_should_contains_my_bean() {
new ApplicationContextRunner()
.withUserConfiguration(TestConfiguration.class)
.run(context -> {
assertThat(context.getBean(MyTestBean.class)).isNotNull();
});
}
#Configuraiton
public static class TestConfiguration {
#Bean
public MyTestBean myTestBean(){
new MyTestBean();
}
}
}

#Transactional with static method

Why cant we use #Transactional for static methods to manage the transactions in my spring Project ?
#Transactional works well for non static method but not for static methods any specific reason ?
In order to understand why something like what you are proposing does not work you have to first understand at a high level how Spring handles beans that use #Transactional.
When you annotate a method or the class as #Transactional and make it a Spring Bean, Spring effectively creates a proxy for that class (using JDK Dynamic proxies or CGLIB proxies). That means that whenever your class is used (from Spring managed code that is), it's not your code that gets called immediately, but the proxy which first does whatever is needed, and then your code is called (in the case of caching support your code would perhaps not even be called at all).
A key thing to remember here is that the invoking code (the call site if you will) does not change at all, and the invocation of to the required target method (the proxy method) is performed by the JVM using the same bytecode (invokevirtual or invokeinterface).
With that in mind, the reason that static is not supported becomes clear.
You can't create a proxy for static method! Of course Java Dynamic Proxies cannot do this, and neither can CGLIB.
Supporting such a feature would require changing the bytecode of the invoking code, since calling a static method is implemented via invokestatic in bytecode, which hard-codes the target method.
This part of the Spring documentation explains Spring AOP in details
If you're using AspectJ, here's a simple, albeit ugly, workaround:
public static void doWhatever(final String param) {
new Runnable() {
#Transactional
public void run() {
// do whatever in transaction...
}
}.run();
}
This is feasible using:
#EnableTransactionManagement(mode = ASPECTJ) (or its XML equivalent)
all the configuration required to make AspectJ work
registering a custom aspect to handle #Transactional over static methods like this one I just stumbled upon (did not test it)
NB: in this post I asked about how to make package-private methods #Transactional.

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