I have the following configuration bean for a non web app
#Configuration
public class MyBeans {
#Bean
#Scope(value="prototype")
MyObject myObject() {
return new MyObjectImpl();
}
}
On the other side I have my class
public class MyCommand implements Command {
#Autowired
private MyObject myObject;
[...]
}
How can I make myCommand be autowired with the configuration in MyBeans without using XML so I can inject mocks in my other test classes?
Thanks a lot in advance.
With XML-based configuration you'd use the ContextConfiguration annotation. However, the ContextConfiguration annotation doesn't appear to work with Java Config. That means that you have to fall back on configuring your application context in the test initialization.
Assuming JUnit4:
#RunWith(SpringJUnit4ClassRunner.class)
public class MyTest{
private ApplicationContext applicationContext;
#Before
public void init(){
this.applicationContext =
new AnnotationConfigApplicationContext(MyBeans.class);
//not necessary if MyBeans defines a bean for MyCommand
//necessary if you need MyCommand - must be annotated #Component
this.applicationContext.scan("package.where.mycommand.is.located");
this.applicationContext.refresh();
//get any beans you need for your tests here
//and set them to private fields
}
#Test
public void fooTest(){
assertTrue(true);
}
}
Related
I Have a bean defined in a configuration class:
#Configuration
public class Config {
#Value("${some-property}")
private String someProperty;
#Bean
public SomeBean someBean() {
return new SomeBean(someProperty);
}
}
How do I define a cucumber step definition to set the property some-property and let SomeBean to use it?
The problem I'm facing is that my beans are initialized before any steps happen. How do I reinitialize a bean, or refresh using the #RefreshScope maybe? Or can I start/restart the spring context in a later step?
Cucumber step:
#Given("I have some property set to (.*)")
public void someProperty(String someProperty) {
// Answer
}
And here's the empty step definition class to start Spring Context:
#SpringBootTest(classes = Application.class)
#DirtiesContext
#ActiveProfiles("it")
#AutoConfigureCache
#AutoConfigureTestEntityManager
#AutoConfigureWebMvc
#AutoConfigureMockMvc(secure = false)
#ImportAutoConfiguration
public class CucumberSpringContextBootstrapper implements En {
}
The question seems extremely simple, but strangely enough I didn't find a solution.
My question is about adding/declaring a bean in a SpringBootTest, not overriding one, nor mocking one using mockito.
Here is what I got when trying the simplest implementation of my real need (but it doesn't work):
Some service, bean, and config:
#Value // lombok
public class MyService {
private String name;
}
#Value // lombok
public class MyClass {
private MyService monitoring;
}
#Configuration
public class SomeSpringConfig {
#Bean
public MyClass makeMyClass(MyService monitoring){
return new MyClass(monitoring);
}
}
The test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { SomeSpringConfig.class })
public class SomeSpringConfigTest {
private String testValue = "testServiceName";
// this bean is not used
#Bean
public MyService monitoringService(){ return new MyService(testValue); }
// thus this bean cannot be constructed using SomeSpringConfig
#Autowired
public MyClass myClass;
#Test
public void theTest(){
assert(myClass.getMonitoring().getName() == testValue);
}
}
Now, if I replace the #Bean public MyService monitoring(){ ... } by #MockBean public MyService monitoring;, it works. I find it strange that I can easily mock a bean, but not simply provide it.
=> So how should I add a bean of my own for one test?
Edit:
I think ThreeDots's answer (create a config test class) is the general recommendation.
However, Danylo's answer (use #ContextConfiguration) fit better to what I asked, i.e. add #Bean directly in the test class.
Spring Test needs to know what configuration you are using (and hence where to scan for beans that it loads). To achieve what you want you have more options, the most basic ones are these two:
Create configuration class outside the test class that includes your bean
#Configuration
public class TestConfig {
#Bean
public MyService monitoringService() {
return new MyService();
}
}
and then add it to to test as configuration class #SpringBootTest(classes = { SomeSpringConfig.class, TestConfig.class })
or
If you only need to use this configuration in this particular test, you can define it in static inner class
public class SomeSpringConfigTest {
#Configuration
static class ContextConfiguration {
#Bean
public MyService monitoringService() {
return new MyService();
}
}
}
this will be automatically recognized and loaded by spring boot test
Simply add the config as
#ContextHierarchy({
#ContextConfiguration(classes = SomeSpringConfig.class)
})
What i am using in this cases is #Import:
#DataJpaTest(showSql = false)
//tests against the real data source defined in properties
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Import(value = {PersistenceConfig.class, CustomDateTimeProvider.class})
class MessageRepositoryTest extends PostgresBaseTest {
....
Here i am using a pre configured "test slice".
In this case a need to add my JpaAuditingConfig.
But why not just adding the other beans as you did with your SomeSpringConfig.class ?:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { SomeSpringConfig.class, OtherBean.class })
public class SomeSpringConfigTest {
...
Everything listed in test will be injectable directly, all not declared must be added as mocks.
I'm using Spring annotation based configuration in my Play application.
Controllers and DAOs are Spring beans. Controller and DAO layers are defined with different Spring profiles and each layer could be disabled separately.
I'd like to test controller layer in isolation from DAO layer. I've disabled DAO profile and redefined each of DAO beans as a Mockito mock. From functional point of view it works fine, the only thing I don't like is defining mocks manually like this:
#Configuration
#Import(AppContext.class)
public class TestAppContext {
#Bean
public DaoA getDaoA(){
return mock(DaoA.class);
}
//... all dependencies are re-defined manually
}
Is there a way to define package (like with #ComponentScan annotation)
and get all beans in that package as mocks instead of real objects?
UPD:
I'm running tests with FakeApplication (https://www.playframework.com/documentation/2.0/api/java/play/test/FakeApplication.html), so context is started not in the test level, but inside fake application startup.
public class ControllerTest extends WithApplication {
#Before
public void setUp() throws Exception {
start(fakeApplication(new GlobalSettings(){
private ApplicationContext appContext;
public void onStart(Application app) {
appContext = new AnnotationConfigApplicationContext(TestAppContext.class);
}
#Override
public <A> A getControllerInstance(Class<A> clazz) throws Exception {
return appContext.getBean(clazz);
}
}));
}
...
}
I did it like this because I wan't to make the test more reliable and test how controller works in real environment:
#Test
public void testControllerMethod() {
Result result = route(fakeRequest(GET, "/controller/method"));
assertThat(result).is(...);
}
If the number of dependencies you need to mock is huge, you can also use spring-auto-mock.
#ContextConfiguration(classes = { AutoMockRegistryPostProcessor.class, RestOfClasses.class, ... })
#RunWith(SpringJUnit4ClassRunner.class)
public class YourTest {
...
}
As you are creating the ApplicationContext on your own, you can register the postprocessor programmatically:
public void onStart(Application app) {
appContext = new AnnotationConfigApplicationContext(TestAppContext.class);
appContext.getBeanFactory().addBeanPostProcessor(new AutoMockRegistryPostProcessor())
}
Mark your unit-test with #RunWith(SpringJUnit4ClassRunner.class)
Mark your tested class as #InjectMock
Mark you Dao class as #Mock
Make use of Mockito in your project
I have 2 applications, one using Spring inside a web-app, and another local application using Spring Boot. These 2 share a configuration class between them.
I cannot figure out how to configure the local application correctly.
Here is the basic layout of classes I am using:
The main class
#EnableAutoConfiguration
class MainClass{
#Autowired
private static MyComponent component;
public static void main(String args[]){
// code
SpringApplication.run(MyConfiguration.class, args);
component.start();
}
}
The configuration
#Configuration
#EnableConfigurationProperties
#PropertySource("classpath:/path/to/queue.properties")
public class MyConfiguration {
#Autowired
public static Environment env;
// more beans
#Bean
#Qualifier("qualifier1")
public static String getName(){ //made String and simple to match the Component's Autowired
return env.getProperty("property.name");
}
}
The component
#Component
#EnableAutoConfiguration
public class MyComponent extends Thread {
#Autowired
#Qualifier("qualifier1")
private String template; // this isn't actually String, but a springAMQP class. Should have the same effect though.
#Override
public void run(){
//code
template.charAt(0); // just something that fails if it was not autowired
//code
}
}
If .run is given MyConfiguration.class i get a null pointer within the autowired Environment in MyConfiguration. If it is given MainClass.class the autowired MyComponent is still null.
As for some layout restrictions,
the Main Class and MyComponent only exist in the local application. The Configuration is in a shared package between the local application and the web application. This prevents me from simply creating the Bean with MyComponent in the Configuration due to dependencies.
If I remove the MyComponent from the MainClass and add the following configuration within the Local application:
#Configuration
public class MyLocalConfiguration extends MyConfiguration {
private MyComponent listener;
#Bean
public MyComponent getListener(){
if(listener == null){
listener = new MyComponent();
listener.start();
}
return listener;
}
}
I still have the issue of the Environment being null in MyConfiguration, preventing the other beans from being set up.
There are 2 problems with your configuration
#Autowired will only work for Spring Managed beans, your MainClass isn't spring managed so no autowiring is going to happen
#Autowired will not work on static fields
I have a Spring test :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:my-context.xml")
public class MyTest {
#Test
public void testname() throws Exception {
System.out.println(myController.toString());
}
#Autowired
private MyController myController;
}
This works fine when myController is defined in same class as MyTest but if I move MyController to another class it is not autowired, as running below returns null, so myController does not seem to be autowired correctly :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:my-context.xml")
public class MyTest {
#Test
public void testname() throws Exception {
System.out.println(new TestClass().toString());
}
}
#Controller
public class TestClass {
#Autowired
private MyController myController;
public String toString(){
return myController.toString();
}
}
Does autowiring only occur at within the class the test is being run from ? How can I enable autowiring on all classes that are instantiated by the test class ?
Update :
Thanks to the answers from smajlo & Philipp Sander I was able to fix this using this code to access this bean instead of creating the bean explicitly. This is already configured by Spring so I access it from the context :
ApplicationContext ctx = new ClassPathXmlApplicationContext("my-context.xml");
TestClass myBean = (TestClass) ctx.getBean("testClass");
When the bean is created explicitly it is not autowired by Spring.
new TestClass().toString()
If you creating object by manually invoking constructor obejct is not controlled by Spring so field won't be autowired.
EDIT:
maybe you want to create specific test-context and load both on test classes.. because for now i guess your way to do the testing is a little wrong.
Why do you need from test class access to another test class? This is not unit test anymore:)
Your TestClass will be never autowired no matter what annotation you will add, because you creating new instance..
try this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:my-context.xml")
public class MyTest {
#Autowired
private TestClass testClass;
#Test
public void testname() throws Exception {
System.out.println(testClass.toString());
}
}
#Controller
public class TestClass {
#Autowired
private MyController myController;
public String toString(){
return myController.toString();
}
}
As Sotirios Delimanolis already said:
MyTestClass needs to be managed by Spring to get autowired. to do this, simply at #Component to MyTestClass and autowire it