Closed spring-projects-issues closed 5 years ago
Sam Brannen commented
I'm afraid the supplied stack trace does not provide much to go on, other than the fact that you are using TestNG.
Can you please provide the details of your setup?
Are tests #1 and #2 different test classes or different test methods within the same class?
Have you tried a different version of Spring, for example 4.1.0?
Where is @DirtiesContext
applied, at the method level or class level and on which method/class?
Exactly which TestExecutionListener
classes are configured for your test class (and all of its parent classes)? In other words, please provide every declaration of @TestExecutionListeners
in the affected test class hierarchy.
Thanks for any feedback you can provide!
Sam
Sam Brannen commented
In any case, without knowing the details of your configuration, it sounds like you might be closing the ApplicationContext
programmatically which is not supported.
The reason I suspect this is that a NoSuchBeanDefinitionException
with the message "No bean named 'environment' is defined" is thrown whenever an attempt is made to autowire an object (e.g., your test instance) using a closed ApplicationContext
.
See my comments here for details.
Of course, I cannot verify this assumption without seeing your test code. So please confirm whether you are closing the context programmatically.
Thanks,
Sam
David Blake commented
The Test #1 and test #2 are different test classes in separate source files. Test class #1 is annotated with @DirtiesContext
at the class level. I need that due to using mokito mocks in test class #1 and therefore want Test class #2 context to be regenerated.
I am not explicitly closing the ApplicationContext programmaticall, just using @DirtyContext
in Test class #1.
David Blake commented
I can try going back from 4.1.6 until I see which version the issue came in on if that is helpful. I will try the 4.1.0 you suggested first.
Sam Brannen commented
OK. Thanks for the feedback. I'll see if I can reproduce the issue.
In the meantime, if you could answer my question above regarding TestExecutionListener
configuration, I'd appreciate it.
David Blake commented
Thanks for your assistance Sam. I will get you the requested information..
Sam Brannen commented
This is potentially related to #15172, #17012, and INT-3543.
David Blake commented
I don't have any TestExecutionListener
config items in the test code, but I do extend AbstractTestNGSpringContextTests
which is authored by you I noticed.
@TestExecutionListeners({
ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class})
public abstract class AbstractTestNGSpringContextTests implements IHookable, ApplicationContextAware {
@ContextConfiguration(classes = ServicesUnitTestConfig.class)
@ActiveProfiles("unit")
public abstract class HPUnitTest extends AbstractTestNGSpringContextTests {
@DirtiesContext
@Test(groups = { "L1", "Service Tests", "Queue" })
public class AccountSummaryServiceTest extends HPUnitTest {
@Test(groups = { "L1", "Service Tests" })
public class SchedulerServiceTest extends HPUnitTest {
Sam Brannen commented
This issue is potentially related to #10800.
Sam Brannen commented
Out of curiosity, what happens if you configure your three test classes as follows?
@ContextConfiguration(classes = ServicesUnitTestConfig.class)
@ActiveProfiles("unit")
public abstract class HPUnitTest extends AbstractTestNGSpringContextTests {
@DirtiesContext
@Test(suiteName = "AccountSummaryService", groups = {"L1", "Service Tests", "Queue"})
public class AccountSummaryServiceTest extends HPUnitTest {
@Test(suiteName = "SchedulerService", groups = {"L1", "Service Tests"})
public class SchedulerServiceTest extends HPUnitTest {
Note the use of unique values for suiteName
.
Please let me know the outcome of your tests with the above configuration.
Cheers,
Sam
David Blake commented
Tried the suggested changes and it failed the same way.
David Blake commented
Rebuilt with 4.1.0 and it fails with a similar call stack though the 4.1.6 call stack has extra details logged so it looks like the trace logging was improved over time. Overall it is the same illegalStateException - Failed to load ApplicationContext error.
Sam Brannen commented
Thanks for helping investigate.
Since those attempts failed as well, I think this issue may not be directly related to spring-test
especially since there were no (or not many) changes to Spring's TestNG and @DirtiesContext
support from 4.0.5 to 4.1. In any case, we will continue to investigate the cause.
David Blake commented
Based on what we've seen so far with testing this, I'm curious what changes have happened in org.springframework.beans.factory between 4.0.5 and 4.1.0. I'm currently stepping through the working code with 4.0.5 to try and better understand what makes that work for my case. I'll also try going back to 4.1.0 and do the same there for the failing case. I'll post what I find if anything.
David Blake commented
Here is what I'm seeing as a difference between the working 4.0.5 case and the 4.1.0 failing case.
In the stack trace in this issue's Description, notice the call to the method deletePriorBuildTriggersAndJobs().
That is ours and is annotated with @Transactional(propagation = REQUIRED)
. When I put a breakpoint on the method definition and step into code there is a difference for the working and non-working case.
Stepping into the call stack results in going into Spring's org.springframework.transaction
package and eventually into Spring's org.springframework.beans.factory
package and eventually fails.
Stepping into the call stack does not go into Spring's org.springframework.transaction
package but instead goes to the first line of the method deletePriorBuildTriggersAndJobs()
which is ours.
It appears that 4.0.5 to 4.1+ introduces extra processing or at least different processing for @Transactional
annotations that fail after a test is run with @DirtiesContext
at the class level.
Sam Brannen commented
Thank you for taking the time to perform additional analysis!
We will take your findings into account.
Sam Brannen commented
Interim testing work has been committed in GitHub commit 3c5a9b4:
Introduce class-level
@DirtiesContext
tests for TestNGPrior to this commit, ClassLevelDirtiesContextTests existed for verifying the expected behavior of
@DirtiesContext
declared at the class level in conjunction with JUnit and SpringJUnit4ClassRunner.This commit introduces analogous tests for TestNG in the new ClassLevelDirtiesContextTestNGTests class.
Furthermore, ContextCacheTestUtils and TrackingTestNGTestListener have been introduced to reduce code duplication across the test suite.
Sam Brannen commented
This issue is potentially related to #15166, in the sense that parallel construction of an application context results in a null
Environment
being autowired into an @Configuration
class.
David Blake commented
I see that version 4.1.7 is due around May 20th. Do we currently know if a fix for this issue will be possible by that date?
David Blake commented
Update to previous comment. Version 4.1.7 has been pushed out to mid July according to the dashboard. I am still interested in a fix for this issue and currently don't have a way to work around this. Let me know if I can help assist with anything.
Juergen Hoeller commented
So with 4.0.5, it looks like your @Transactional
rule on deletePriorBuildTriggersAndJobs()
hasn't kicked in at all before, despite your use of the AspectJ transaction aspect. That actually seems like a bug in its own right... As of 4.1, for some reason, the transaction processing does kick in as requested. So for that part, I'd rather declare 4.0.5 at fault and 4.1 to be correct. From that perspective, does it work for you if you remove the @Transactional
declaration completely?
Now, with the transaction actually kicking in there, it tries to resolve a transaction manager and apparently encounters an incompletely initialized ApplicationContext
. This may happen on startup as well as on shutdown, but it's more likely to happen for access after shutdown... Is there possibly some asynchronous task involved, trying to access the context after its shutdown? Or some concurrent test runs that interfere in terms of the state of a shared ApplicationContext
?
Juergen
David Blake commented
From that perspective, does it work for you if you remove the
@Transactional
declaration completely?
Yes, if I remove the @Transactional
it works. It also works if I remove the @DirtiesContext
from around Test class #1.
Is there possibly some asynchronous task involved, trying to access the context after its shutdown? Or some concurrent test runs that interfere in terms of the state of a shared ApplicationContext?
There is nothing, I know of, that our code is doing to either close the ApplicationContext or asynchronously access the context. The failure is not happening during a specific Test but instead during what appears to be the initialization of the second Test class in preparation for running the second group of Test. The first Test class has the @DirtiesContext
. When it completes doesn't that annotation cause the ApplicationContext
to be reset? Then when it starts the second test class processing it causes the ApplicationContext
to be reloaded, I believe, even before starting to execute our tests that are part of the second class.
Does this answer your questions completely? I'd be happy to expand further.
David Blake commented
Removing the reference to the bean, Environment, that is the cause of the failure to load the ApplicationContext
also fixes the issue and could be a possible workaround for us if we can replace that functionality with something else.
Seems the failure of the Environment has been an issue in the past and is curiously the only bean of around 300 that get loaded in our system that is a problem currently. Just an observation.
import org.springframework.core.env.Environment; @Autowired
Environment env;
Sam Brannen commented
There is nothing, I know of, that our code is doing to either close the ApplicationContext or asynchronously access the context.
That's odd, because it really sounds like that would be the case based on the error you're encountering.
The failure is not happening during a specific Test but instead during what appears to be the initialization of the second Test class in preparation for running the second group of Test.
Again, this sounds to me like there is some overlap in the execution of the tests in terms of test execution lifecycle callbacks.
Have you tried out my unique suite name per test class proposal (see comments above) on all of your TestNG-based tests?
The first Test class has the
@DirtiesContext
. When it completes doesn't that annotation cause theApplicationContext
to be reset?
Yes, the corresponding ApplicationContext
should be closed after such a test class has completed.
Then when it starts the second test class processing it causes the
ApplicationContext
to be reloaded, I believe, even before starting to execute our tests that are part of the second class.
That is correct, but still... it sounds like the ordering is not correct.
Could you please execute your tests with DEBUG
logging enabled for the org.springframework.test.context
logging category and supply us the output?
See my detailed comments in #10800 for the kind of problems that can occur if the TestNG test execution order is incorrectly configured.
Regards,
Sam
David Blake commented
Could you please execute your tests with DEBUG logging enabled for the org.springframework.test.context logging category and supply us the output? Could you provide instructions or a link on how to enable DEBUG logging in springframework code? Is there a log4J property file or something for that?
Have you tried out my unique suite name per test class proposal (see comments above) on all of your TestNG-based tests? Yes I documented that in a previous comment. Fails with the same stack trace. I have minimized the failure case down to two test classes each with 8 tests. I am going to further minimize it down to just two test classes and two test methods. Would this help with your concern about order? Anyway, less is better for failure reproduction imho.
One thing further. I can see in the spring tools UI while I'm running the tests that the first test class completes all 8 tests successfully and the call stack shows it going into testing code to initialize the context. During this the beans are reloaded, there are ~300 in my projects, and it always fails trying to lookup the Environment
springframework bean which is autowired into one of our classes. The failure occurs "before" test class two methods are executed. I removed this autowired reference from our code and hardcoded the values we were getting from that just for testing and everything works fine without that.
I will 1. turn on debug when I know how. and 2. will further simplify the test run needed to show the failure.
thanks for your inputs and assistance.
David Blake
Sam Brannen commented
To enable DEBUG
logging for the org.springframework.test.context
category, you could create a log4j.properties
(in the root of the classpath for your tests) similar to the following:
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%-5p] [%c] - %m%n
log4j.rootCategory=ERROR, console
log4j.logger.org.springframework.beans=INFO
log4j.logger.org.springframework.context=INFO
log4j.logger.org.springframework.test.context=DEBUG
If you already have log4j.properties
or log4j.xml
configuration in place, just adapt it accordingly.
Sam Brannen commented
Yes I documented that in a previous comment. Fails with the same stack trace.
Yes, I recall that. ;)
This time I was asking if you declared a unique suiteName
for every concrete TestNG-based test class in your application.
I have minimized the failure case down to two test classes each with 8 tests. I am going to further minimize it down to just two test classes and two test methods. Would this help with your concern about order?
YES. That would be very beneficial, especially if you can provide us a scaled down project that reproduces the issue. That way we could get our hands on something that is actually breaking and debug it ourselves.
Thanks in advance!
Sam
David Blake commented
Our code base was modified which resulted in the removal of the Environment
bean being autowired which fixes the problem we were having. Due to the complexity of our code base I was unable to get any further minimal test to duplicate the issue.
Now that we are past this we have run into another issue that follows a similar theme in that Spring Framework 4.0.5 works with TestNG and @DirtiesContext
but after updating to 4.1.6 we are getting some detached entity persisting object errors with tests that worked fine in 4.0.5 and work with 4.1.6 if I remove the @DirtiesContext
annotation from around the previous test class that executed.
I will submit this as a different defect unless you want it to remain part of this one.
Sam Brannen commented
Please note that #19400 may be duplicate of this issue.
Bulk closing outdated, unresolved issues. Please, reopen if still relevant.
David Blake opened SPR-12918 and commented
Upgrading from Spring Framework 4.0.5 to 4.1.6 (or 4.1.0) causes Test class #2 to fail with the following exception if Test class #1 is annotated with
@DirtiesContext
at the class level (see example code in the comments section for details).Affects: 4.1 GA
Issue Links:
10800 AbstractTransactionalTestNGSpringContextTests not working as expected when an EJB with TransactionAttribute.REQUIRES_NEW is encountered
15172 getBean(Object.class) fails when introspecting Environment bean
15166 Do not serialize ApplicationContext creation in the TestContext framework
17012 DefaultListableBeanFactory should allow efficient access to current bean names
19400 No bean of type ConfigurableEnvironment in AbstractTestNGSpringContextTests
17525 Autowiring against a closed ApplicationContext should consistently fail
Referenced from: commits https://github.com/spring-projects/spring-framework/commit/3c5a9b4e1d302b789fcad45a1f75715618dff0e7