I have a ConfigurationProperties class and want to test it using junit. But the object is always null. What might be missing in the following code?
#EnableAutoConfiguration
#ComponentScan
#EnableConfigurationProperties(MyProperties.class)
public class AppConfig {
}
#Service
public class MyService {
#Autowired
private MyProperties props;
public void run() {
props.getName();
}
}
#Component
#ConfigurationProperties(prefix = "my")
public class MyProperties {
private String name;
//getter,setter
}
application.properties:
my.name=test
test:
#Configuration
#ComponentScan(basePackageClasses = {MyService.class, MyProperties.class},
includeFilters = #ComponentScan.Filter(value = {MyService.class, MyProperties.class},
type = FilterType.ASSIGNABLE_TYPE),
lazyInit = true
)
#PropertySources(
#PropertySource("application.properties")
)
class AppTest {
#Bean
public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
return new PropertySourcesPlaceholderConfigurer();
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationConfigTest.class)
public class MyTest extends AbstractJUnit4SpringContextTests {
#Autowired
private MyService service;
#Test
public void testService() {
service.run();
}
}
The following will load it for you:
#ContextConfiguration(classes = Application.class, initializers = ConfigFileApplicationContextInitializer.class)
Related
I have a SpringBoot app. with this test, but it does not inject and mock the classes
#RunWith(SpringRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:servlet.xml"
})
public class TerritoriClandestiControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Mock
TerritoriClandestiRepository territoriClandestiRepository = mock(TerritoriClandestiRepository.class);
#InjectMocks
private TerritoriClandestiService territoriClandestiService;
List<Object[]> list;
Resource listResource = new ClassPathResource("list.txt");
#Before
public void setup() throws IOException {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.build();
list = DataLoader.readLines(listgetInputStream());
}
#Test
public void getAll() throws Exception {
when(territoriClandestiRepository.findAllBaseData(anyLong())).thenReturn(list);
mockMvc.perform(get("/terrcland")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andDo(print())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(status().isOk())
.andExpect(jsonPath("$.*", hasSize(1)));
}
}
You can use #MockBean instead of #Mock, it will export the field as a bean in the spring context.
public class TerritoriClandestiControllerTest {
#MockBean
private TerritoriClandestiRepository territoriClandestiRepository;
}
Alternatively you can also do something like this
#RunWith(SpringRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {
"classpath:testDatabaseContext.xml",
"classpath:testServicesContext.xml",
"classpath:servlet.xml"
}, classes = {TerritoriClandestiControllerTest.Config.class})
public class TerritoriClandestiControllerTest {
#TestConfiguration
static class Config {
#Bean
TerritoriClandestiRepository territoriClandestiRepository() {
return Mockito.mock(TerritoriClandestiRepository.class);
}
}
#Autowired
private TerritoriClandestiRepository territoriClandestiRepository;
}
I have a SpringBoot app with this config file:
package com.bonanza.web.config;
#Configuration
#EnableJpaRepositories(basePackages = "com.bonanza.backend.repository")
#EntityScan(basePackages = "com.bonanza.backend")
#EnableTransactionManagement
#EnableCaching
#PropertySource("file:///${user.home}/.bonanza/application-common.properties")
public class BonanzaApplicationConfig {
}
and this service:
package com.bonanza.backend.service;
#Service
#Transactional(
readOnly = true
)
public class UserService {
private final RoleRepository roleRepository;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final PasswordResetTokenRepository passwordResetTokenRepository;
public UserService(RoleRepository roleRepository, UserRepository userRepository, PasswordEncoder passwordEncoder, PasswordResetTokenRepository passwordResetTokenRepository) {
this.roleRepository = roleRepository;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.passwordResetTokenRepository = passwordResetTokenRepository;
}
..
}
and the main class:
package com.bonanza.web
#SpringBootApplication
public class BonanzaWebApplication {
public static void main(String[] args) {
SpringApplication.run(BonanzaWebApplication.class, args);
}
}
and this controller
package com.bonanza.web.controller;
#Controller
public class AppErrorController implements ErrorController {
protected final UserService userService;
..
public AppErrorController(UserService userService, ErrorAttributes errorAttributes, EmailService emailService) {
super(userService);
this.errorAttributes = errorAttributes;
this.emailService = emailService;
}
...
}
but when I start the app. I have this error:
Description:
Parameter 0 of constructor in com.bonanza.web.controller.AppErrorController required a bean of type 'com.bonanza.backend.service.UserService' that could not be found.
Action:
Consider defining a bean of type 'com.bonanza.backend.service.UserService' in your configuration.
#SpringBootApplication, Will only search the current package and all its sub packages for components/beans. Your UserService package
com.bonanza.backend.service
is not a subpackage of BonanzaWebApplication
com.bonanza.web
So you can use with all the packages that need to be Component Scan
#ComponentScan({"com.bonanza.web","com.bonanza.backend.service"})
#SpringBootApplication
public class BonanzaWebApplication {
public static void main(String[] args) {
SpringApplication.run(BonanzaWebApplication.class, args);
}
}
You can also specify component scanning in #SpringBootApplication annotation it self
#SpringBootApplication(scanBasePackages = {"com.bonanza.web","com.bonanza.backend.service"})
I have a #Service bean that I need static access to:
#Service
public class MyBean implements InitializingBean {
private static MyBean instance;
#Override
public void afterPropertiesSet() throws Exception {
instance = this;
}
public static MyBean get() {
return instance;
}
public String someMethod(String param) {
return "some";
}
}
Usage:
#Service
public class OtherService {
public static void makeUse() {
MyBean myBean = MyBean.get();
}
}
Problem: when I write an integration junit test for OtherService that makes use of the stat MyBean access, the instance variable is always null.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ITest {
#Autowired
private OtherService service;
#MockBean
private MyBean myBean;
#Before
public void mock() {
Mockito.when(myBean.someMethod(any()).thenReturn("testvalue");
}
#Test
public void test() {
service.makeUse(); //NullPointerException, as instance is null in MyBean
}
}
Question: how can I write an integration test when using such type of static access to a spring-managed bean?
If you want to influence the #Bean-creation, then use #Configuration
#Configuration
public class MyConfig {
#Bean
public MyBean myBean() {
return new MyBean;
}
#Bean
public OtherService otherService () {
return new OtherService(myBean());
}
}
Then mocking becomes really easy:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ITest {
#MockBean
private MyBean myBean;
#Autowired
private OtherService service;
#Test
public void test() {
// service is initialised with myBean
...
}
}
When more control is needed, then you can choose the following approach. It provides sometimes more control than just a #MockBean. In your test you can easily mock a method just before calling it. This is my preferred way.
#Configuration
public class MyConfig {
...
#Bean
public MyBean getMyBean() {
return mock( MyBean.class);
}
#Bean
public OtherService otherService () {
return new OtherService( getMyBean());
}
}
In the application you can use #Autorwired to access it AND implement method overrule/mocking easily.
#RunWith(SpringRunner.class)
#SpringBootTest
public class AnotherTest {
#Autowired
private MyBean myBean;
#Autowired
private OtherService service;
#Test
public void test() {
when( myBean.method( a, b)).thenReturn( something);
service.doSomething( ....); // with myBean.method() overruled
}
}
I have a declarative spring config
#Configuration
public class SpringConfig {
#Bean
public someBean() {
return new Bean1();
}
}
and a #Component annotated Bean
#Component
public class Bean2 {
}
Now I would like to use both of then in my UnitTest
#RunWith(SpringJUnit4ClassRunner.class)
public void UnitTest {
#Autowired Bean1 bean1;
#Autowired Bean2 bean2;
}
but I have no idea how to do it.
You can do this:
#ContextConfiguration(classes = {SpringConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public void UnitTest {
#Autowired Bean1 bean1;
#Autowired Bean2 bean2;
}
For class Bean2, you can add the #ComponentScan annotation:
#Configuration
#ComponentScan("com....package.of.bean2")
public class SpringConfig {
#Bean
public someBean() {
return new Bean1();
}
}
If you don't want to add the ComponentScan to your SpringConfig class, you can add an additional test config class with the ComponentScan annotation and add it to the ContextConfiguration annotation:
#ContextConfiguration(classes = {SpringConfig.class, SpringTestConfig.class})
What am I doing wrong? The test does not work.
This is my Interface class:
#Validated
public interface ICustomerService
{
public List<Customer> findbyStr(
#NotEmpty(message = "{column.notvalid}")
#NotNull(message = "{column.notvalid}")
String column,
#NotEmpty(message = "{column.notvalid}")
#NotNull(message = "{value.notvalid}")
String value);
}
This is my Implementation class:
#Service("customerService")
#Scope(value = "singleton", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class CustomerService implements ICustomerService {
#Autowired
private IStorageDao<Customer> dao;
#Override
public List<Customer> findbyStr(String column, String value) {
return dao.findByString(Customer.class, column, value);
}
}
This is my unit-Test class:
JUNIT Test does not work.
#RunWith(SpringJUnit4ClassRunner.class)
public class CustomerTest extends BaseIntegrationTest {
#Autowired
private ICustomerService service;
#Autowired
public static Validator validator;
#Test
public void test_A6_CustomerFindByStrNull() {
List<Customer> result = service.findbyStr(null, null);
Set<ConstraintViolation<ICustomerService>> constraintViolations = validator
.validate(service);
assertEquals(0, constraintViolations.size());
assertEquals("Die angegebene E-Mail-Adresse ist fehlerhaft.",
constraintViolations.iterator().next().getMessage());
assertNotNull(result);
assertNotNull(result.get(1));
}
}
I'm pretty sure you cannot test ConstraintViolations when the annotations are on a method of an object since it should throw a MethodConstraintViolationException. You should try something like this :
#RunWith(SpringJUnit4ClassRunner.class)
public class CustomerTest extends BaseIntegrationTest {
#Autowired
private ICustomerService service;
#Test
public void test_A6_CustomerFindByStrNull() {
try {
List<Customer> result = service.findbyStr(null, null);
} catch (MethodConstraintViolationException ex) {
assertEquals("Die angegebene E-Mail-Adresse ist fehlerhaft.", ex.getConstraintViolations().iterator().next().getMessage());
}
fail("Exception expected");
}
}
You need to have the following Bean in your application-context.xml file :
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>