I am using Spring autoscan components in my testing framework.
Autowired works fine in all classes except in class which extends Junit Testwatcher.
The class where I extend Junit testwatcher is:
#Component
public class PrintTrace extends TestWatcher{//this class overrides pass and fail
//methods in Testwatcher
#Autowired
private HTMLLogger htmlLogger //this is null all the time. it works in
//other classes
}
My Base class looks like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:beans.xml"})
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
public class YahooTestSetUp {
public static WebDriver driver;
#Rule
public PrintTrace printTrace = new PrintTrace();
#Autowired
private HTMLLogger htmlLogger;//here its working fine
}
And my xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="com.pilz"/>
</beans>
Please advice this is very important for me. Otherwise I should go for
singleton objects,which is not a good solution for me.
Adding more info:
Lets say i have 2 test suites which access 2 common classes HTMLLogger.class and PrintTraece.class.
Each testsuite is independent of other suite. These suites have #BeforeClass and #AfterClass
see below:
#RunWith(Suite.class)
#Suite.SuiteClasses({ AutowireClass.class})
public class YahooTestSuite extends YahooTestSetUp{
private static HTMLLogger htmlLogger;
#BeforeClass
public static void allTests() throws Exception {**//Can I use #Autowired in
static methods(no)**
htmlLogger = new HTMlLogger()
htmlLogger.createHTMLLogFile();
}
#AfterClass
public static void afterAll() throws Exception{
htmlLogger.htmlLogEnd();
}
}
and another suite is doing same for other module.
As i said before my PrintTrace.class extends Testwatcher.class(Junit rule) its look like
This class is invoked with the the rule created in baseclass.
#Component
public class PrintTrace extends TestWatcher{
private HTMLLogger htmlLogger = null;
#Override
public void starting(final Description description) {
}
#Override
public void failed(final Throwable e, final Description description) {
htmlLogger.writeFailLog();**//This is common for all suites**
//This should be same object used in suite
}
#Override
public void succeeded(final Description description) {
htmlLogger.writePassLog();//This is common for all suites
//This should be same object used in suite
}
#Override
public void finished(Description description) {
}
thanks
Why do you want to use Spring DI for JUnit rules? Your test code is not your application.
In some of the java classes, like the test cases or web-listeners, #Autowired doesn't work. You have to define the applicationContext like an object and receive beans manually.
ApplicationContext context = AnnotationConfigApplicationContext("app_context_file_path")
HTMLLogger htmlLogger = context.getBean("loggerBeanName");
Also, have a look over here - http://static.springsource.org/spring/docs/3.0.x/reference/testing.html
And google about the #Resource spring annotation.
To get the exact object you need, you should have the following configuration:
If you create a bean in the application context file, just use the id of it in the getBean(name) method. If you have it as a separate class, provide it (class) with the #Component(value) annotation and scan the package in the application context. After that use the value name of that bean(component) to get the exact object you've configured.
Related
I want to test a class that simple shutdown the application:
#Component
public class ShutdownManager {
#Autowired
private ApplicationContext applicationcontext;
public int shutdown(int returnCode) {
return SpringApplication.exit(applicationcontext, () -> returnCode);
}
}
The test case I created is this:
public class ShutdownManagerTest {
#Test
public void should_shutdown_gracefully() {
SpringApplication app = new SpringApplication(TestClass.class);
ConfigurableApplicationContext context = app.run();
ShutdownManager shutdownManager = (ShutdownManager)context.getBean("shutdownManager");
int result = shutdownManager.shutdown(10);
assertThat(result).isEqualTo(10);
}
#SpringBootApplication
#ImportResource("classpath:/core/shutdownmanagertest.xml")
private static class TestClass {
public TestClass() {
}
public static void main(String[] args) {
}
}
}
My shutdownmanagertest.xml contains only one bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="shutdownManager" class="com.mycodebase.ShutdownManager" />
</beans>
However, when I run this, it complains that:
Field myOtherService in com.mycodebase.MyTask required a bean of type 'com.mycodebase.MyOtherService ' that could not be found.
The class MyTask is in the same package of the ShutdownManager which contains a field myOtherService which has an #Autowire annocation. But it is not defined in my test xml which contains just one bean.
Can someone help me understand why it tries to resolve a bean that is not in the context?
It's doing that because that's how #SpringBootApplication works.
#SpringBootApplication:
This is a convenience annotation that is equivalent to declaring #Configuration, #EnableAutoConfiguration and #ComponentScan.
#ComponentScan
If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
So everything in the same or a subpackage of ShutdownManagerTest is being picked up.
I'm implemented a little service based on Spring. I would like to autowire the class Demo within the DemoController. Therefore I defined the values for it within the beans.xml file. It seems like spring finds the bean because everything is compiling. But the return value of the service looks like this:
{"valueUno":0,"valueDue":null}
DemoApplication:
#SpringBootApplication
#ComponentScan({"com"})
#EnableAutoConfiguration
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Demo:
#Component
public class Demo {
private int valueUno;
private String valueDue;
//getter, setter....
}
DemoController:
#RestController
public class DemoController {
#Autowired
private Demo demo;
#RequestMapping(
value = "/welcome",
method = RequestMethod.GET
)
public HttpEntity<Demo> getMessage() {
return new HttpEntity<Demo>(this.demo);
}
}
beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="demo" class="com.example.demo.Demo">
<property name="valueUno" value="50"></property>
<property name="valueDue" value="Hello"></property>
</bean>
</beans>
Everything is inside one package. I dont get it...
You have annotated Demo as a #Component. Therefore, Spring instantiates a bean of this class using its default constructor during the component scan (you have enabled it with #ComponentScan({"com"})), and then injects (autowires) it into DemoController. So, the autowired bean not the one defined in beans.xml.
There are 2 ways to solve the issue:
1) If you want to go with XML configuration, remove #Component from Demo class, and add #ImportResource("classpath:beans.xml") annotation to your Controller class.
2) If you want to go with JavaConfig (annotations), you would need to add separate class like this:
#Configuration
public class MyConfiguration {
#Bean
public Demo demo() {
return new Demo(50, "Hello"); // or initialize using setters
}
}
...and add #Import(MyConfiguration.class) annotation to your Controller class, and remove #Component from Demo class.
As a solution to what #PresentProgrammer said, you can move your XML configuration to a Java configuration as following:
#Bean
public Demo demo() {
Demo demo = new Demo();
demo.setValueUno(50);
demo.setValueDue("Hello");
return demo;
}
You can add this configuration directly to the DemoApplication class or to a configuration class:
#Configuration
public class BeanConfiguration {
//Bean Configurations
}
For further details, you can read Java-based container configuration Spring documentation.
So, with Spring, I prefer xml over annotation. It's just a personal preference, I like having the xml docs unifying my spring config data.
Anyway, I'm working on a JUnit test case for database access. My first time using the spring-test library. I'm trying to use dependency injection to inject the StudentDao bean into this class below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration ({"classpath:/test-context.xml"})
public class StudentDaoTest extends TestCase {
private StudentDao studentDao;
public StudentDao getStudentDao() {
return studentDao;
}
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
#Test
#Transactional
public void test(){
//This ends up printing null, identifying the problem
if(studentDao == null){
System.out.println("Null");
}
Student student = new Student();
student.setFirstName("First");
studentDao.insertStudent(student);
}
}
The thing is, as you can guess from the comment, is this isn't working. The test-context.xml file starts up, and the other context file it imports also starts up, as I can see from the log, so it's not that the program can't find the file. Somehow the xml doc that I have is just not configuring the bean properly.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="data-context.xml"/>
<bean id="studentDaoTest" class="io.craigmiller160.schedule.persist.StudentDaoTest">
<property name="studentDao" ref="studentDao"/>
</bean>
I discovered that if I used the #Autowired annotation on studentDao it does work. The thing is, I don't use annotations anywhere else in the program, and I want to maintain consistency. Honestly, I would prefer to avoid using #ContextConfiguration too, but I don't think I'll be able to do that.
So, I'm looking for help making this injection work with just xml. I know, I'm being picky, but I like my consistency, as I said.
Thanks in advance.
PS. the full filepath of the files is:
StudentDaoTest: src/test/java/io/myname/schedule/persist/StudentDaoTest
test-context.xml: src/test/resources/test-context.xml
If you don't want to use Spring annotations why use them ?
public class MyTest {
private ConfigurableApplicationContext context;
#Before
public void initApplicationContext() {
context = new ClassPathXmlApplicationContext("...");
}
#After
public void closeApplicationContext() {
if (context != null) {
context.close();
context = null;
}
}
#Test
public void test() {
context.getBean(Object.class);
// ...
}
}
Inject application context and work with it as you like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration ({"classpath:/test-context.xml"})
public class StudentDaoTest extends TestCase {
#Autowired
private ApplicationContext ctx;
Actually the definition of the studentDaoTest bean has no effect on the test just because there is no such functional in the testing framework.
I have a service which I am trying to inject across various classes in my tests but I am getting its instance as null.
My config interface:
MyService.java
public interface MyService {
public String getHostUri();
}
My implementation class of this interface: MyServiceImpl.java
public class MyServiceImpl implements MyService {
private static final String BASE_HOST_URI_CONFIG = "localhost:4444";
#Override
public String getHostUri() {
return BASE_HOST_URI_CONFIG;
}
My Spring configuration class with the bean:
#Configuration
public class AutomationSpringConfig {
#Bean
public MyService getMyService(){
return new MyServiceImpl();
}
}
My testNG class:
#ContextConfiguration(classes=AutomationSpringConfig.class ,loader =AnnotationConfigContextLoader.class)
public class BasicAutomatedTest extends AbstractTestNGSpringContextTests {
private static final Logger LOGGER = Logger.getLogger(BasicAutomatedTest.class);
#Inject
private MyService myService;
#Test
public void basicTest {
Setup setup = new Setup();
LOGGER.info(myService.getHostUri());
LOGGER.info(setup.myService.getHostUri());
}
}
My helper class in which I am not able to get the injection:
public class Setup {
#Inject
public MyService myService;
}
So when I try to get the hostUri via the setup object in the BasicAutomatedTest's basicTest method I get a NullPointerException.
So I am not able to inject the MyService bean in the Setup class.
In order to use annotations you need to specify that behaviour in your beans XML configuration file. Something like this:
<context:component-scan base-package="your.base.package"/>
<context:annotation-config/>
Hope it helps!
I want configure a component test using spring-test configuration inner class (#Configuration). Tested components has some services which I'd like to mock for the test. These services are classes (no interface used) and have spring annotations (#Autowired) in them. Mockito can easily mock them, however, I found no way of disabling spring autowiring.
Example how I can easily reproduce:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Test
public void test() {
}
#Configuration
#ImportResource("/spring/component-config.xml")
static class Beans {
#Bean
ThirdPartyService createThirdPartyService() {
return mock(ThirdPartyService.class);
}
}
}
public class ThirdPartyService {
#Autowired
Foo bar;
}
public class TestedBean {
#Autowired
private ThirdPartyService service;
}
In this example "TestBean" represents the service to be mocked. I would NOT like "bar" to be injected by spring! #Bean(autowire = NO) does not help (in fact, that's the default value).
(Please save me from "use interfaces!" comments - the mocked service can be 3rd party which I can't do anything with.)
UPDATE
Springockito partially solves the problem, as long as you don't have to have anything else to configure (so you can't use configuration class with Springockito - it does not support it), but use mocks only.
Still looking for pure spring solution, if there's any...
Here is my solution to your problem:
import static org.mockito.Mockito.mockingDetails;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MockitoSkipAutowireConfiguration {
#Bean MockBeanFactory mockBeanFactory() {
return new MockBeanFactory();
}
private static class MockBeanFactory extends InstantiationAwareBeanPostProcessorAdapter {
#Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return !mockingDetails(bean).isMock();
}
}
}
and then just
#Import(MockitoSkipAutowireConfiguration.class)
in your test #Configuration and you are all set
I solved it by creating FactoryBean for my bean instead of just mocking bean. At this way Spring don't try to autowire fields.
Factory bean helping class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private final Class<T> clazz;
public MockitoFactoryBean(Class<T> clazz) {
this.clazz = clazz;
}
#Override public T getObject() throws Exception {
return mock(clazz);
}
#Override public Class<T> getObjectType() {
return clazz;
}
#Override public boolean isSingleton() {
return true;
}
}
Actual test context part:
#Configuration
public class TestContext {
#Bean
public FactoryBean<MockingService> mockingService() {
return new MockitoFactoryBean<>(MockingService.class);
}
}
Check Spring profiles. You don't need to disable auto wiring, you need to inject different beans for different configuration.
You could add the mocked service manually to the spring application context via org.springframework.beans.factory.config.SingletonBeanRegistry#registerSingleton. This way the mock is not post-processed by spring and spring does not attempt to autowire the mock. The mock itself will be injected into your tested bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
public class SomeTest {
// configured in component-config.xml, using ThirdPartyService
#Autowired
private TestedBean entryPoint;
#Autowired
private ThirdPartyService thirdPartyServiceMock;
#Test
public void test() {
}
#Configuration
static class Beans {
#Autowired
private GenericApplicationContext ctx;
#Bean
TestedBean testedBean() {
ctx.getBeanFactory().registerSingleton("thirdPartyService", mock(ThirdPartyService.class));
return new TestedBean();
}
}
public static class ThirdPartyService {
#Autowired
Object bar;
}
public static class TestedBean {
#Autowired
private ThirdPartyService service;
}
}
I am in quite the same situation.
What I found that if you do not set the context loader by #ContextConfiguration annotation on your test class, the default context loader will be used, which derived from AbstractGenericContextLoader. I had a look at its source and turned out it registers all the bean post processors which are responsible for reading annotations such #Autowired. In other words, annotation config is enabled by default.
So the main problem is that there are two configurations which are in conflict: in the java config we said that autowiring is not needed, while the autowired annotation tells the opposite. The real question is how to disable the annotation processing in order to eliminate the undesired configuration.
As far as I know there is no such spring implementation of ContextLoader which would not be derived from AbstractGenericContextLoader so I guess the only we can do is to write our own. It would be something like this:
public static class SimpleContextLoader implements ContextLoader {
#Override
public String[] processLocations(Class<?> type, String... locations) {
return strings;
}
#Override
public ApplicationContext loadContext(String... locations) throws Exception {
// in case of xml configuration
return new ClassPathXmlApplicationContext(strings);
// in case of java configuration (but its name is quite misleading)
// return new AnnotationConfigApplicationContext(TestConfig.class);
}
}
Of course it would be worth to spend more time to find out how to implement ContextLoader properly.
Cheers,
Robert
There are so many ways of doing this, I'm pretty sure that this answer will be incomplete, but here are a few options...
As currently seems to be recommended practice, use constructor injection for your services rather than autowiring the fields directly. This makes testing like this so much easier.
public class SomeTest {
#Mock
private ThirdPartyService mockedBean;
#Before
public void init() {
initMocks(this);
}
#Test
public void test() {
BeanUnderTest bean = new BeanUnderTest(mockedBean);
// ...
}
}
public class BeanUnderTest{
private ThirdPartyService service;
#Autowired
public BeanUnderTest(ThirdPartyService ThirdPartyService) {
this.thirdPartyService = thirdPartyService;
}
}
By doing that, you can also mix up autowired and mocked services by autowiring into the test itself and then constructing the beans under test with the most useful mix of autowired and mocked beans.
A reasonable alternative is to use Spring profiles to define stub services. This is particularly useful when wish to use the same stubbed features in multiple tests:
#Service
#Primary
#Profile("test")
public class MyServiceStub implements MyService {
// ...
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SomeTest.Beans.class)
#ActiveProfiles({"test"})
public class SomeTest {
// ...
}
By using the #Primary annotation, it ensures that this stub bean will be used instead of any other bean implementing the MyService interface. I tend to use this approach for things like email services, where by changing profile, I'm able to switch between a real mail server and Wiser.