I have code that can be run properly as a JUnit test case. However, when I put the same test code inside a main class, spring configuration do not properly load the objects.
Spring code looks like this:
#ContextConfiguration(locations = { "classpath:/fileonly-sens-services.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
public class AppTest extends ContextBuilder {
#BeforeClass
public static void setup() {
System.setProperty("app-
init.properties","classpath:test.app.properties");
#Test
someTestMethod()
}
I think it is a very simple problem but I cannot get it working outside of JUnit! Thanks for the help!
In your application you will have to create an ApplicationContext. The specifics of how to do it, depends on what kind of application you are building.
If you are building a command-line application, you can instantiate ClassPathXmlApplicationContext and use it to instantiate needed beans.
If you are building a web application, you can use ContextLoaderListener to load context during your application initialization.
Related
I have two Maven projects A and B, where project B is nested in project A. The structure looks like the following:
Project A:
src/test/java:
MyTest.java
Project B:
src/test/java:
MyNewTest.java
pom.xml
pom.xml
My goal is to let MyNewTest.java act as a wrapper of MyTest.java, and be able to invoke the test methods declared in MyTest.java from MyNewTest.java. MyTest has some injected dependencies.
My question is: how to initialize MyTest in MyNewTest to make sure that all the dependencies of MyTest are injected properly?
MyTest looks like the following:
public class MyTest {
#Autowired
Service service;
#Autowired
TestUtil util;
Info info;
#PostConstruct
void init() {
info = service.getStuff();
}
#Test
public test1() {
service.getMoreStuff();
// more code omitted
}
}
I have tried adding #Component to MyTest, and then in MyNewTest.java, use #Autowired like the following:
public class MyNewTest {
#Autowired
private MyTest baseTest;
#Test
public void runTest() {
// run the test in MyTest.java
baseTest.test1();
}
}
But this doesn't seem to work - baseTest is null. I also tried initializing MyTest by calling its default constructor, but the dependencies failed to be injected as expected. Can anyone suggest a proper way to initialize MyTest.java so that all its dependencies are injected as well? Thanks
The problem with your test is the #Autowired annotations as the Spring wiring isn't being triggered. There are two different paths you can take to fix this.
The first option is to manually instantiate baseTest in MyNewTest at which point it will no longer be null. However, tests will still fail because your two #Autowired dependencies will be null. You can either add setters or inject them via your constructor. Note- these classes should be mocked if you are performing Unit Tests. Here's how it would look if you chose to add these classes via the constructor -
private Service service;
private TestUtil util;
private MyTest baseTest;
#BeforeEach
void setUp() {
service = mock(Service.class);
util = mock(TestUtil.class);
baseTest = new MyTest(service, util);
}
The second option is to add configuration to your Test class to support the Spring wiring. I am not familiar with taking this route as I always choose the first option when possible. There are multiple ways to add the Spring wiring but the easiest is-
#SpringBootTest
#RunWith(SpringRunner.class)
public class MyNewTest {
However, this does not cover all use cases so you may need more specific configurations. You can find some of the possible options here - How to write JUnit test with Spring Autowire?
Edit- As I immediately recognized a problem, I didn't read the rest very carefully and I missed that these were Test classes you were trying to wire together. I am not sure if this is possible.
Only Spring beans can be #Autowired so the first step would be to attempt to add configuration to make your Test class into a Spring bean. I've never heard of this being done before but it would be easy to try. If not, you can get around this problem by using the first option.
The second problem is that tests are not included in the artifact. I'd imagine you could circumvent this issue by mixing your Test classes in with your regular classes but this is considered a bad practice. I've never heard of tests being dependent on other tests but I'd guess this is also a bad practice. What's the reason for wanting to create your Tests this way?
I am trying to write a scanner for custom annotations based on the answer in Scanning Java annotations at runtime.
However, to speed up the process, I only want to scan the classes under the main package (package which has the main class and its sub-packages). Figured that the easiest way to determine the package name would be from the main class.
The code I am writing would end up in a library which will be used by Spring Boot applications. So I have no way of knowing the name of the main class. Is there a way to determine the name of the main class at runtime in Spring Boot application?
Regards,
Anoop
Assuming your main class is annotated with #SpringBootApplication, it can be done using ApplicationContext::getBeansWithAnnotation():
#Service
public class MainClassFinder {
#Autowired
private ApplicationContext context;
public String findBootClass() {
Map<String, Object> annotatedBeans = context.getBeansWithAnnotation(SpringBootApplication.class);
return annotatedBeans.isEmpty() ? null : annotatedBeans.values().toArray()[0].getClass().getName();
}
}
In my application I initialize a property before spring application startup as follows:
MapLookup.setMainArguments(new String[] {"logging.profile", profile}); //from args
SpringApplication.run(source, args);
(just for reference: it is used for log4j2 logging, which must be set before spring starts to initialize).
Now I want to run an #IntegrationTest, but use the same logging configuration. Obviously I cannot use the code above, as a JUnit test is not executed using SpringApplication.run.
So, how could I initialize code before a #RunWith(SpringJUnit4ClassRunner.class) starts?
Note: BeforeClass does not work as this is executed after spring context startup.
You can run the initialization in a static initializer. Static initializer will run after JUnit loads the test class and before JUnit reads any annotations on it.
Alternatively you can extend SpringJUnit4ClassRunner with your own Runner initialize in it first and then run SpringJUnit4ClassRunner
I had a slightly different problem. I need to deploy something to my service after the Spring context is loaded. Solution use a custom config class for the test and run the deployment within a #PostConstruct Method.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class, loader = AnnotationConfigContextLoader.class)
public class JunitTest {
#Configuration
#ComponentScan(basePackages = { "de.foo })
public static class TestMConfig {
#Autowired
private DeploymentService service;
#PostConstruct
public void init() {
service.deploy(...);
}
}
#Test
public void test() {
...
}
}
Maybe this helps, someone, sometime, somewhere ;)
I have a bean that describes my session factory. I only want to load this bean once throughout my entire test suite. I wanted to know, is this possible? And I would like to localize to my context.xml if that is possible too. I can post any source you want, just ask.
Thanks in advance!
In Spring test framework, if all test case classes use the same locations attribute, the context is preserved between runs. In the example below context defined in context.xml will be loaded only once (before first test case) and will be closed when JVM exits:
#ContextConfiguration(locations = "classpath:context.xml")
#Transactional
public class FooTest {
//...
}
#ContextConfiguration(locations = "classpath:context.xml")
#Transactional
public class BarTest {
//...
}
You cannot preserve only one bean, but you can load the whole context once. See my article Speeding up Spring integration tests.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
#Transactional
public class MyServiceTest {
#Resource(name="myService")
public MyService myService;
#Test
public void testSeomthing() {
//do some asserts using myService.whatever()
}
}
However the tests are based on data I migrate in, so every time I run my suite of tests I want to execute my unrelated migration code. I don't want to run a #Before in each test class. I want to run it once at beginning of complete test process, where can I put this ?
I would advice you to create a test bean somewhere with startup logic invoked in #PostConstruct:
#Service
public class TestBean {
#PostConstruct
public void init() {
//startup logic here
}
}
Obviously this bean should only be created for tests, the easiest way to achieve this is to place it in src/test/java in a package that is component-scanned by Spring for #Service-annotated classes.
Note: you must remember that #PostConstruct is not running in a transaction! See How to call method on spring proxy once initialised.
JUnit also offers a #BeforeClass annotation which you can place on a static method to initialize resources just once.