Unit testing code inside TransactionSynchronizationManager - java

I couldn't find a working solution, how to unit test something that happened inside TransactionSynchronizationManager.registerSynchronization(). I have code like this:
pubclic class MyService {
public void method() {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter()
{
#Override
public void afterCommit()
{
myService.callSmth(params);
}
}
}
}
Here I call method():
public class MyClass {
private MyService myService;
public void method2() {
myService.method();
}
}
In my unit test I need to verify that myService.callSmth. But seems like test code doesn't go into afterCommit() method.
That's my test class:
#Configurable
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:test-applicationContext.xml" })
public class ServiceTest extends TestHelper
{
#Mock
MyService myService;
#InjectMocks
MyClass myClass;
#Test
public void allowWriteOffGlobal()
{
myClass.method();
veryfy(myService).method();
}
}
I simplified my code and please, don't pay attention if there could be any mistakes in mocking and smth like this. In a debugger I see how the flow comes to line TransactionSynchronizationManager.registerSynchronization and skips the afterCommit() method.
P.s.: not sure I provided enough info. Please, write in the comment if there is a need of smth extra. Thanks.

From Spring Test documentation (emphasis mine):
Annotating a test method with #Transactional causes the test to be run within a transaction that will, by default, be automatically rolled back after completion of the test. If a test class is annotated with #Transactional, each test method within that class hierarchy will be run within a transaction. Test methods that are not annotated with #Transactional (at the class or method level) will not be run within a transaction. Furthermore, tests that are annotated with #Transactional but have the propagation type set to NOT_SUPPORTED will not be run within a transaction.
and
By default, test transactions will be automatically rolled back after completion of the test; however, transactional commit and rollback behavior can be configured declaratively via the #Commit and #Rollback annotations at the class level and at the method level.
So I suggest putting a #Commit annotation in your test method. (By the behaviour you describe I suppose that you are using Spring Test, this advice does not apply otherwise).

Try mocking TransactionSynchronizationAdapter and call afterCommit()

Related

Spring issue with #PostConstruct and #PreDestroy

I have a Spring application that I am trying to test with EmbededRedis. So I created a component like below to Initialize and kill redis after test.
#Component
public class EmbededRedis {
#Value("${spring.redis.port}")
private int redisPort;
private RedisServer redisServer;
#PostConstruct
public void startRedis() throws IOException {
redisServer = new RedisServer(redisPort);
redisServer.start();
}
#PreDestroy
public void stopRedis() {
redisServer.stop();
}
}
But now I am facing a weird issue. Because spring caches the context, PreDestroy doesnt get called everytime after my test is executed, but for some reason, #PostConstruct gets called, and EmbededRedis tries to start the running redis server again and again, which is creatimg issues in the execution.
Is there a way to handle this situation by any mean?
Update
This is how I am primarily defining my tests.
#SpringBootTest(classes = {SpringApplication.class})
#ActiveProfiles("test")
public class RedisApplicationTest {
Ditch the class and write an #Configuration class which exposed RedisServer as a bean.
#Configuration
public void EmbeddedRedisConfiguration {
#Bean(initMethod="start", destroyMethod="stop")
public RedisServer embeddedRedisServer(#Value("${spring.redis.port}") int port) {
return new RedisServer(port);
}
}
So I debuged the ContextInitialization as suggested by #M. Deinum.
For me, the porblem was, Our application was mocking different classes in order to mix mocking with Spring context.
Now, when you use mocks, MockitoContextInitializer also becomes part of your cache key, which results in cache miss. Reason is, The classes under mock are obviously different for different test classes.
Looking at the situation, I preferred to go ahead with #DirtiesContext to invalidate the contest after the test is done, so that I can reinitialize the context later on for different test.
Note #DirtiesContext is in a way recommended to be avoided as it slows down your tests.

#BeforeAll and #Transaction are not working - changes on db side are not rollbacked

I'm trying to test my application using #Transactional to rollback after the tests and #BeforeAll to set up the objects for tests:
#EnableJpaRepositories
#SpringBootTest
#Transactional
public class UserScoreTest {
#BeforeAll
public static void init() {
userRepository.save(user);
}
#Test
...
}
The problem seems to be that once #BeforeAll is executed, the changes are "committed".
Does anybody know if it's desired behavior or it's a bug?
I solved my problems using #BeforeEach and then saving during in the #Test and it works (after tests, everything is rollbacked) but I would prefer to set up once with #BeforeAll.
I believe that your issue is that #Transactional only works on instance methods of the Spring "Bean", not on static methods. That is why it works for #BeforeEach instance method but not on #BeforeAll static method.
One way around this would be to use #BeforeEach but have a boolean field that stores if it has already been done once. Don't forget to address threading issue if your test runs in a multi-threaded manner.
This answer explains it better.
I think that your concept is wrong.
In common test scenario, any #Autowired dependency, like your userRepository, should not be static. I suppose it is static as you are referencing it from the static method.
You should consider #Before annotation only and thanks to the #Transactional annotation, each test will be automatically rollbacked at the end.
The main scenario is this:
Run #BeforeAll
For each #Test annotation:
Begin transaction
Run #Before
Run #Test
Run #After
Rollback transaction
The purpose of #BeforeAll is to do some work only once for entire test class. I can think of one possible scenario for setting values for RequestContextHolder or SecurityContextHolder but I would be aware of loosing those values in other threads (if #Test creates some). I rarely use #BeforeAll. Same principle is for #Parameterized tests which have to have #Parameters annotation above the static method. This is because the runner in #RunWith has to know how many tests and which parameters to use before actual initialization of the test.
#BeforeAll is out of transaction scope.
#BeforeEach executes for every #Test, #BeforeAll executes only once at start for all the #Test

Spring dependencies not being injected into BeforeSuite method?

I am running a spring boot application that uses TestNG as the testing framework. My tests are set up like this:
A parent class, which is in charge of setup logic and takes care of all of the configuration stuffs:
#ContextConfiguration(classes = {TestingConfig.class}, initializers = ConfigFileApplicationContextInitializer.class)
#ContextConfiguration(classes = TestConfig.class)
#TestPropertySource(locations = "classpath:application.yml")
public abstract ParentTestClass extends AbstractTestNGSpringContextTests {
#Autowired
private ServiceClient serviceClient;
#BeforeSuite
public void beforeClass() {
Assert.assertNotNull(serviceClient);
serviceClient.doSomeSetupWork();
}
}
There are multiple child test classes. Each on inherits form the parent test class so that they share the same setup logic.
public ChildTestClass1 extends ParentTestClass {
#Test
public void someTest() {
...
}
// More tests not shown
}
public ChildTestClass2 extends ParentTestClass {
#Test
public void anotherTest() {
...
}
// More tests not shown
}
The serviceClient is a client for one of the web services that the test suite depends on. I am making calls with the service client to set up the data in the other service before running the test cases.
The problem is this: previously I was using the #BeforeClass annotation, which meant that the parent class's setup method was being run once for every child test class. This was ok, but it was really slow waiting for the same setup to be run multiple times.
So I thought to myself: I'll just change the #BeforeClass annotations in the ParentTestClass to be #BeforeSuite instead! That will solve all of my problems!
Wrong.
Now when I run it, the Assert.assertNotNull(serviceClient); line in the beforeClass() method of the parent class fails. In short, Spring dependencies aren't being injected into the #BeforeSuite annotated method, even tho they were being injected in the method when it was annotated with #BeforeClass.
Any thoughts here? I'd really appreciate it!
I believe this is working as designed. From looking at how the implementation is built within org.springframework.test.context.testng.AbstractTestNGSpringContextTests (from which you extend), the dependencies are injected into your test class via the org.springframework.test.context.support.DependencyInjectionTestExecutionListener#injectDependencies (this is a listener).
All the listeners including the DependencyInjectionTestExecutionListener is invoked only via org.springframework.test.context.testng.AbstractTestNGSpringContextTests#springTestContextPrepareTestInstance which is a #BeforeClass(alwaysRun=true) classified method.
So your dependencies aren't available to you until and unless this #BeforeClass annotated method runs to completion. So you would have to move out your #BeforeSuite method and have it work with a #BeforeClass annotation only.
If you don't need your service setup to be done multiple times, then you would need to add an edit check in your test code that does the setup ONLY IF ITS NOT DONE.
Here is the solution to all your problem.
#Override
#BeforeSuite
protected void springTestContextPrepareTestInstance() throws Exception {
super.springTestContextPrepareTestInstance();
}
Hope this helps you to inject the dependencies.

Arquillian + TestNG: How to access container managed objects in #Before/#After methods?

I couldn't find any satisfying solution for this problem, though other people have encountered it before...
I'd like to test a business bean which modifies persistent data using a dao.
The dao can be injected into the test methods as it is an ejb.
How to make it available in typical #Before/#After methods, for example to clean up the db.
Brief Example:
#PersistenceTest
public class MyTestClass extends Arquillian {
#Inject private Dao dao;
#Inject private MyBean myBean;
#BeforeMethod
public void cleanDB () {
dao.remove(foo); // Currently throws NPE as dao is not injected.
}
#Test
public void someTest () {
// In a Test-method dao is available and calling cleanDB from here also
// works as intended....
}
}
As far as I know only the Test-methods are executed in the container. Most information that I found seems to be outdated.
Is there any nice way to achieve this?
Thank you!
I'm using (managed) Wildfly 8 as app server.
Arquillan invokes the #Before** and #After** methods twice.
Once in client mode, once in container mode.
The only solution I found so far is, that you must verify that the dao was injected before you use it like :
#BeforeMethod
public void cleanDB () {
if (dao != null) {
dao.remove(foo);
}
}
So if you make a breakpoint and runs your code you should have 2 invokations of this method:
1st: dao is null
2nd: dao is injected
Hope that helps.
See also http://jayshaughnessy.blogspot.de/2012/11/arquillian-and-testng.html for more information.

Reload Spring application context after every test

I have a test Class which contains 2 test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:applicationContextTest.xml" })
#Transactional
#TransactionConfiguration(defaultRollback = true)
public class MyITest extends implements BeanFactoryAware {
private BeanFactory beanFactory;
#Test
public void test1() throws Exception {}
#Test
public void test2() throws Exception {}
}
When I run tests individually I get no errors, but when I run all tests together there is a failure. This failure is due to some tests modifying the application context:
b = beanFactory.getBean("logDataSource", BasicDataSource.class);
b.set ...
Is there an option to run this test separately? I just want when test1 start it read all necessary things then run test and then close all necessary things. And then start test2.
You can use the #DirtiesContext annotation on the test class that modifies the application context.
Java Doc
Spring documentation
By default, this will mark the application context as dirty after the entire test class is run. If you would like to mark the context as dirty after a single test method, then you can either annotate the test method instead or set the classMode property to AFTER_EACH_TEST_METHOD at your class level annotation.
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)

Categories

Resources