Why is Spring #Transactional unreliable with AspectJ? - java

TLDR: this project reproduces the issue: https://github.com/moreginger/aspectj-no-tx
Edit: Above now reproduced without jooq i.e. using plain JDBC.
Edit: Spring bug - https://github.com/spring-projects/spring-framework/issues/28368
I've been trying to use AspectJ to weave Spring #Transactional for a Kotlin codebase. I've seen it working with both load-time and compile-time weaving (the latter using io.freefair.aspectj.post-compile-weaving). However, when I wrote a test that the transaction would be unwound after an exception something strange happened: the test would sometimes fail. It turned out that it always failed on the first test suite run, then the second time around it would run first (due to failing the first time) and pass. After much investigation, a "minimal" case in my codebase is (in the same suite run):
Runs a test which calls a method marked #Transactional on an #Autowired bean.
Runs a test in a different class that calls another method marked #Transactional on a different #Autowired bean, which will make some changes and then throw an exception. Assert that the changes made aren't visible. This fails.
Then if you run this suite again it runs the second test first due to failing the first time and passes. It continues to run in this order and pass until something else fails or gradle clean is run. (Note that to avoid the order swapping you can make the first test also fail).
The same thing happens whether I use load-time or compile-time weaving.
How can this be? Especially with compile-time weaving the transactions have been incorporated into the class files. How can accessing a different #Transactional method cause one that we access later to stop being #Transactional o_O ? I've verified that simply removing #Transactional from the first "fixes" the issue.

Related

Multiple tests fail when ran on Jenkins after migration

I recently migrated a Unit Testing suite to Junit 5.8.2 and Mockito 4.5.1 + Mockito Inline to allow static mocking. Powermock was removed.
2000+ tests were migrated and they all run successfully when ran inside the IDE (IntelliJ). Both with the IDEA and Gradle runner.
However, when Jenkins attempts to run them there are over 900 failed tests. Some of the exceptions thrown.
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
Boolean cannot be returned by someMethod()
someMethod() should return Date`
I understand what causes these errors as I've seen them multiple times during the migration so this is not a duplicate asking for the solution.(Unless there's something different with the Jenkins environment) The code that throws such exceptions should not be throwing them. And it does not in the IDE. It's thrown exclusively in Jenkins.
An additional exception which I have never seen before is thrown as well.
org.mockito.exceptions.misusing.UnfinishedMockingSessionException:
Unfinished mocking session detected.
Previous MockitoSession was not concluded with 'finishMocking()'.
For examples of correct usage see javadoc for MockitoSession class.
Most of the exceptions are of this type.
However, the MockitoSession inteface is not used anywhere in the test suite. All mocks are initialized with #ExtendWith(MockitoExtension.class)
I have no idea what could be causing this.
Jenkins is running the same versions of Java/Junit/Mockito/Spring as the code in the IDE.
It seems clear to me that the different environments are causing the issue. However, what could be the difference and how would I go about finding it?
I attempted to reproduce the results locally but was unable to. Any ideas towards that are also welcome.

Why does Java Debugger steps into internal methods randomly?

It's been some time since I started noticing that while debugging the application I'm working on, the java debugger randomly steps into some Spring Framework internal methods.
For example, when I add a breakpoint in a RestController endpoint method that calls another service method, the debugger steps into BeanFactoryTransactionAttributeSourceAdvisor.getPointcut() method.
This behavior makes me upset because the only way to avoid this is to add a breakpoint in the service method.
Does anyone know why this happens?
I'm using Intellij IDEA 2020.3 , Java 8 and Spring Boot 2.X if this helps.
Some classes in Spring are enhanced with aspects to provide orthogonal functionality (in this case: transactions, which probably means you annotated your method or class with #Transactional).
At run time (or compile time) additional code is weaved around your own code to provide that functionality (start transaction before running your code, committing on successful completion, or rolling back for (some) exceptions).
In other words, when your code runs, there is more code than just your own, and when you step through the debugger, IntelliJ will step through that code as well and show it to you.

Integration tests randomly fail or throw error when run by maven

I am running a suite of integration tests using maven and about 10% of the tests would fail or throw an error. However, when I start the server and run the individual failed tests manually from my IDE(intellij idea), they all pass with no problem. What could be the cause of this issue?
This is almost always caused by the unit tests running in an inconsistent order, or a race condition between two tests running in parallel via forked tests. If Test #1 finishes first, it passes. But if Test #2 finishes first, it leaves a test resource, such as a test database, in an alternate state causing Test #1 to fail. It is very common with database tests, esepecially when one or more alter the database. Even in IDEA, you may find all the tests in the com.example.FooTest class always pass when you run that class. But if you run all the tests in the com.example package or all tests in the project, sometimes (or even always) a test in FooTest fails.
The fix is to ensure your tests are always guaranteed a consistent state when run. (That is a guiding principle for good unit tests.) You need to pay attention to test setup and tear-down via the #Before, #BeforeClass, #After, and #AfterClass annotations (or TestNG equivalents). I recommend Googling database unit testing best practices. For database tests, running tests in a Transaction can prevent these type of issues. That way the database is rolled back to its starting state whether the test passes or fails. Spring has some great support for JDBC dtaabase tests. (Even if your project is not a Spring project, the classes can be very useful.) Read section 11.2.2 Unit Testing support Classes and take a look at the AbstractTransactionalJUnit4SpringContextTests / AbstractTransactionalTestNGSpringContextTests classes and the #TransactionConfiguration annotation (this latter one being from running Spring Contexts). There are also other Database testing tools out there such as DbUnit.

CacheManager.clearAll throws CacheManager has been shut down for Junit Program

I am working on a project whose most of the Junit are failing. My job is to fix them and make them run. I have fixed around 200 Junit classes but there are around 136 Junits still failing, reason no Idea, some times they get fail and some they work. Try to drill down the problem and it the ehCache. It is being shut-down.
Can any body please explain me why this exception occur in Junt testing and that to not all the time.
Please Note we have test cases for "Action" classes as well(Which deal with Servlet Context)
But interesting point all action test classes are getting passed.
Error Message is :
java.lang.IllegalStateException: The CacheManager has been shut down. It can no longer be used.
at net.sf.ehcache.CacheManager.checkStatus(CacheManager.java:1504)
at net.sf.ehcache.CacheManager.getCacheNames(CacheManager.java:1491)
at net.sf.ehcache.CacheManager.clearAll(CacheManager.java:1526)
Some part of your code is shutting down the cache manager (probably in the tear down of the unit test) and then trying to clear the cache. You can see this in the stack trace:
at net.sf.ehcache.CacheManager.clearAll(CacheManager.java:1526)
But once the manager is shut-down you cannot invoke operations on it. Without seeing the code of one of the unit tests, its hard to be more specific.
I figured out what went wrong. One of the Junit is taking time to complete and it is a DAO. That method is taking around 40 to 50 Minutes to complete AND and session timed out for Cache Manager happens and when other Junit try to access I am getting that error.
I fixed the Junit means basically the Query of the DAO to run quicker and all works good.

Spring context dirty after each integration test

I recently started as a freelancer on my current project. One of the thing I threw myself on, was the failing Jenkins build (it was failing starting from April 8th, a week before I started here).
Generally speaking, you could see a buttload of DI issues in the log. First thing I did, was get all tests to work in the same way, starting from the same application context.
They also implemented their own "mocking" thing, which seemed to fail to work correctly. After a discussion with the lead dev, I suggested to start using Springockito. (for a certain module, they needed mocking for their integration testing -- legacy reasons, which can't be changed)
Anyway, stuff started failing badly after that. A lot of beans which were mocked in the test, simply weren't mocked, or weren't found or whatever. Typically, it would fail on the loading of the application context, stating that one or another bean was missing.
I tried different stuff and different approaches, but in the end, only the thing I most feared would work: add #DirtiesContext to every single test. Now, the maven build is starting to turn green again, tests start doing what they are supposed to do. But I am reloading the Spring context each and every time, which takes time - which is all relative, since the context is loaded in about 1 - 2 seconds.
A side note to this story is that they've upgraded to Hibernate 4, and thus to Spring 3.2. Previously, they were using an older version of Spring 3. All tests were working back then, and the #DirtiesContext thing was not necessary.
Now, what worries me the most, is that I can't immediately think of an explanation for this weird behaviour. It almost seems that Springs context is dirtied, simply by launching a test which uses #Autowired beans. Not all tests are using Mocks, so it can't be that.
Does this sound familiar to anyone? Has anyone had the same experiences with integration testing with (the latest version of) Spring?
On Stackoverflow, I've found this ticket: How can a test 'dirty' a spring application context?
It seems to pretty much sum up the behaviour I'm seeing, but the point is that we're autowiring services/repositories/..., and that we don't have any setters on those classes whatsoever.
Any thoughts?
Thanks!
To answer my own question, the secret was in the Spring version. We were using Spring 3.1.3, whereas I presumed they were using Spring 3.2 (they were constantly speaking about a recent upgrade of the Spring version).
The explanation was here, a blog post I stumbled over in my hunt to get it fixed: Spring Framework 3.2 RC1: New Testing Features
And a copy paste of the relevant piece:
The use of generic factory methods in Spring configuration is by no means specific to testing, but generic factory methods such as EasyMock.createMock(MyService.class) or Mockito.mock(MyService.class) are often used to create dynamic mocks for Spring beans in a test application context. For example, prior to Spring Framework 3.2 the following configuration could fail to autowire the OrderRepository into the OrderService. The reason is that, depending on the order in which beans are initialized in the application context, Spring would potentially infer the type of the orderRepository bean to be java.lang.Object instead of com.example.repository.OrderRepository.
So, how did I solve this problem? Well, I did the following steps:
create a new maven module
filter out the tests which needed mocking. All the non-mocked test would run normallly in a Spring build, in a separate Failsafe run (I created a base-package "clean", and sorted them out like that)
Put all the mocked tests in a base package called "mocked", and make an additional run in Failsafe for the mocked tests.
Each mocked test is using Springockito, to create the mocks. I'm also using the Springockito annotations, to easily do a #ReplaceWithMock in place. Every mocked test is then annotated with #DirtiesContext, so the context is dirtied after each test, and the Spring context is reintroduced with each test.
The only reasonable explanation that I could give, is that the context is effectively being dirtied, because there is a framework (Springockito) which is taking over the management of the Spring beans from the Spring framework. I don't know if that's correct, but it's the best explanation I could come up with. That, in fact, is the definition of a dirty context, which is why we need to flag it as dirty.
Using this strategy, I got the build up and running again, and all tests are running ok. It's not perfect, but it's working, and it's consistent.

Categories

Resources