Reputation: 149
I have a test class which should be structured with inner/nested classes inside it. A guy recommended me to use @RunWith(HierarchicalContextRunner.class)
to run it. From what runners I've
already tried I like this more, and I'd like to keep using it. I've built my class based on this advice. It works nicely in outer class, all tests inside it work fine. But everything inside inner class throws this:
IllegalStateException: Failed to load ApplicationContext
...
Caused by: java.lang.IllegalStateException: Neither GenericXmlContextLoader nor AnnotationConfigContextLoader was able to load an ApplicationContext from [MergedContextConfiguration@4aa22cc2 testClass = AbstractBaseEntityGenericDao_Test.Find_Method, locations = '{}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]].
MyTestClass:
@ActiveProfiles(profiles = "test")
@RunWith(HierarchicalContextRunner.class)
@ContextConfiguration(classes = {
PersistenceConfig.class,
RootConfig.class })
@WebAppConfiguration
@Transactional
@Rollback
public class AbstractBaseEntityGenericDao_Test {
private static final String[] CUSTOMER_NAMES = {"Veronica", "Hanna", "Eric"};
@ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Autowired
private ApplicationContext applicationContext;
@Autowired
private SessionFactory sessionFactory;
@Rule
public ExpectedException thrown = ExpectedException.none();
private AbstractClassStub abstractClassStub;
private class AbstractClassStub extends AbstractBaseEntityGenericDao<NamedStubEntity> {
public AbstractClassStub(SessionFactory sessionFactory) {
setClassInstance(NamedStubEntity.class);
this.sessionFactory = sessionFactory;
}
@Override
public void create(NamedStubEntity entity) {
super.create(entity);
}
@Override
public Optional find(Long id) {
return super.find(id);
}
@Override
public void update(NamedStubEntity entity) {
super.update(entity);
}
@Override
public void remove(@NonNull Long id) throws EntityNotFoundException {
super.remove(id);
}
@Override
public void remove(NamedStubEntity entity) {
super.remove(entity);
}
}
@Before
public void setUp() throws Exception {
abstractClassStub = new AbstractClassStub(sessionFactory);
DbTestUtil.resetAutoIncrementColumns(applicationContext, "base_stub_entity");
//...
}
@After
public void tearDown() throws Exception {
//...
}
@Test
public void find_outer_test() {
//Successful
}
public class Find_Method {
@Test
public void find_must_return_entity_in_database_by_id() {
//Unsuccessful
}
@Test
public void find_must_throw_NullPointerException() {
//Unsuccessful
}
@Test
public void find_should_return_BaseStubEntity() {
//Unsuccessful
}
} }
Well... my question is - how to run everything with spring-context, not only the outer class?
UPD: Modified classes:
AbstractSpringTest
@ContextConfiguration(classes = {
PersistenceConfig.class,
RootConfig.class
})
@WebAppConfiguration
@Transactional
@Rollback
public class AbstractSpringTest {
@ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Autowired
protected ApplicationContext applicationContext;
@Autowired
protected SessionFactory sessionFactory;
}
AbstractBaseEntityGenericDao_Test
@ActiveProfiles(profiles = "test")
@RunWith(HierarchicalContextRunner.class)
public class AbstractBaseEntityGenericDao_Test extends AbstractSpringTest {
private AbstractClassStub abstractClassStub;
private class AbstractClassStub extends AbstractBaseEntityGenericDao<NamedStubEntity> {
public AbstractClassStub(SessionFactory sessionFactory) {
setClassInstance(NamedStubEntity.class);
this.sessionFactory = sessionFactory;
}
@Override
public void create(NamedStubEntity entity) {
super.create(entity);
}
//...
}
@Before
public void setUp() throws Exception {
abstractClassStub = new AbstractClassStub(sessionFactory);
DbTestUtil.resetAutoIncrementColumns(applicationContext, "base_stub_entity");
//...
}
@After
public void tearDown() throws Exception {
//...
}
@Test
public void find_must_find() {
//Successful
}
public class Find_Method extends AbstractSpringTest {
@Test
public void find_must_throw_NullPointerException() {
//Unsuccessful
}
@Test
public void find_should_return_BaseStubEntity() {
//Unsuccessful
}
}
}
How it looks in the debugger. And the debugger doesn't even go into the inner methods. It stops on class level
I've tried to make the inner class as static to make it visible outside, but
HierarchicalContextRunner
stops seeing the test methods inside a nested class.
Upvotes: 0
Views: 1205
Reputation: 1410
You're very close. In fact, your approach may even be correct with the problem being related to configuration files rather than the test itself.
Broadly speaking, the simplest way is to put the related annotations also on the nested classes, which I assume is what you want to avoid.
A way to make it cleaner is to have your test classes all inherit from a common class. The class annotated will then be the common class. This works particularly well for me as I don't even have to write all the various Spring annotations in each and every test.
This will require you to use the @ClassRule
and @Rule
annotations in conjunction with SpringClassRule
and SpringMethodRule
and @RunWith(Enclosed.class)
or similar... like you are already doing.
In your case, the Find_Method
class would have to inherit from an AbstractSpringTest
class which in turn would be annotated with your @WebAppConfiguration
and associated annotations. Note however that the Find_Method
class should be declared as static
.
Here follows a partial extract of how such setup would look like:
package mypackage;
import org.junit.Rule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
import org.junit.ClassRule;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import ...
@WebAppConfiguration
@ContextConfiguration(classes = { TestConfigurationClass1.class, TestConfigurationClass2.class })
public class AbstractSpringTest {
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
}
And here's how your test classes would look like:
@RunWith(Enclosed.class)
public class MyTestClass extends AbstractSpringTest {
...
public static class MyNestedTestClass extends AbstractSpringTest {
...
}
public static class MyOtherNestedTestClass extends AbstractSpringTest {
...
}
}
Needless to say, using "rules" allows you to have whatever @RunWith
annotation you see fit for your purpose. In my case I find very useful the possibility to use this approach to run @Parameterized
tests.
The error you described, however, seems to point to problems in loading the associated configuration files (application.properties
, application-context.xml
and similar). The general advice is to double-check that all the configuration files are available on the right path of the classpath
for use by the tests. Note that this means for example checking the contents of the target
directory as well as excluding that the IDE is having a "temper tantrum" and refusing manage specific resources appropriately, the best way being to run a mvn clean install
.
Upvotes: 1