I am trying to Unit test a simple Spring-MVC-Controller but even the simplest Unit Test fails because a #ModelAttribute throws a NullpointerException. My Question is: How to mock/set the ModelAttribute?
I've tried mocking the findAll() method from the repository but it fails.
Below are my used classes:
TestContext:
#Configuration
public class TestContext {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("i18n/messages");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
#Bean
public BenutzerRepository benutzerRepository() {
return Mockito.mock(BenutzerRepository.class);
}
}
StandaloneBenutzerController:
#RunWith(MockitoJUnitRunner.class)
#SpringApplicationConfiguration(TestContext.class)
public class StandaloneBenutzerControllerTest {
public MockMvc mockMvc;
#Mock
private BenutzerRepository benutzerRepositoryMock;
#Mock
private Benutzer benutzer;
#Before
public void setUp() {
this.benutzerRepositoryMock = Mockito.mock(BenutzerRepository.class);
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
Benutzer hauke = new Benutzer("test","tester", "test#tester.de");
List<Benutzer> mockList = new ArrayList<Benutzer>();
mockList.add(hauke);
mockMvc = MockMvcBuilders.standaloneSetup(new BenutzerController()).setViewResolvers(viewResolver)
.build();
}
#Test
public void testSimpleStatus() throws Exception {
Mockito.when(benutzer.getEmail()).thenReturn("test#tester.de");
//Mockito.when(benutzerRepositoryMock.findAll()).thenCallRealMethod();
//Mockito.when(benutzerRepositoryMock.findAll()).thenReturn(userList);
this.mockMvc.perform(get("/verwaltung"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("/WEB-INF/jsp/verwaltung.jsp"));
}
}
BenutzerController: the Part which throws the Nullpointer
#ModelAttribute("userList")
public List<Benutzer> userList() {
return toList(repository.findAll());
}
The solution is like Si mo hinted to set the mock repository for the tested BenutzerController via the constructor:
mockMvc = MockMvcBuilders.standaloneSetup( new BenutzerController(benutzerRepositoryMock)).setViewResolvers(viewResolver).build();
Related
I'm using springboot and spring-data-jdbc.
I wrote this repository class
#Repository
#Transactional(rollbackFor = Exception.class)
public class RecordRepository {
public RecordRepository() {}
public void insert(Record record) throws Exception {
JDBCConfig jdbcConfig = new JDBCConfig();
SimpleJdbcInsert messageInsert = new SimpleJdbcInsert(jdbcConfig.postgresDataSource());
messageInsert.withTableName(record.tableName()).execute(record.content());
throw new Exception();
}
}
Then I wrote a client class that invokes the insert method
#EnableJdbcRepositories()
#Configuration
public class RecordClient {
#Autowired
private RecordRepository repository;
public void insert(Record r) throws Exception {
repository.insert(r);
}
}
I would expect that no record are insert to db when RecordClient's insert() method is invoked, because RecordRespository's insert() throws Exception. Instead the record is added however.
What am I missing?
EDIT. This is the class where I configure my Datasource
#Configuration
#EnableTransactionManagement
public class JDBCConfig {
#Bean
public DataSource postgresDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/db");
dataSource.setUsername("postgres");
dataSource.setPassword("root");
return dataSource;
}
}
You have to inject your datasource instead of creating it manually. I guess because #Transactional only works for Spring managed beans. If you create a datasource instance by calling new constructor (like this new JDBCConfig(). postgresDataSource()), you are creating it manually and it's not a Spring managed beans.
#Repository
#Transactional(rollbackFor = Exception.class)
public class RecordRepository {
#Autowired
DataSource dataSource;
public RecordRepository() {}
public void insert(Record record) throws Exception {
SimpleJdbcInsert messageInsert = new SimpleJdbcInsert(dataSource);
messageInsert.withTableName(record.tableName()).execute(record.contents());
throw new Exception();
}
}
This question already has an answer here:
#Autowired objects are null only when testing
(1 answer)
Closed 3 years ago.
I have following implementation which uses RestTemplate to make request to external service:
#Component
public class ExternalProvider {
private ProviderProperties providerProperties;
#Autowired
#Qualifier("exprd")
private RestTemplate restTemplate;
#Bean
#Qualifier("exprd")
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Autowired
public ExternalProvider(ProviderProperties providerProperties) {
this.providerProperties = providerProperties;
}
public String request(String requestParams) {
...
return restTemplate.getForObject(providerProperties.getUrl(), String.class);
}
}
And here is the test:
#ContextConfiguration(classes = {ExternalProviderTest.TestConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class ExternalProviderTest {
private ExternalProvider externalProvider;
private ProviderProperties providerProperties;
#TestConfiguration
static class TestConfig {
#Bean
#Qualifier("exprd")
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
#Before
public void setUp() {
providerProperties = new ProviderProperties();
externalProvider = new ExternalProvider(providerProperties);
}
#Test
public void test_makeRequest() {
assertNotNull(externalProvider.request("params"));
}
}
My test above is not running because of a NullPointerException when restTemplate is null. It seems that the TestConfig I define in my test is ignored. Anyone has an idea what did I configure wrong here? Thank you!
You have to use the Spring injection in your test, don't use new (or don't use Spring at all!)
You also have to choose between constructor injection and autowired fields, avoid mixing it.
In your example I just removed the #before/new, and added #Autowired for Spring injection.
#ContextConfiguration(classes = {ExternalProviderTest.TestConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class ExternalProviderTest {
#Autowired
private ExternalProvider externalProvider;
#Autowired
private ProviderProperties providerProperties;
#TestConfiguration
static class TestConfig {
#Bean
#Qualifier("exprd")
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Bean
public ExternalProvider externalProvider () {
return new ExternalProvider (providerProperties());
}
#Bean
public ProviderProperties providerProperties() {
return new ProviderProperties();
}
}
#Test
public void test_makeRequest() {
assertNotNull(externalProvider.request("params"));
}
}
edit:
also it seems that your Bean weren't configured, Id did it here in the test, make me know if there are other configuration errors.
I have a class called SomeBean and two tests that are configured with Stubs for different scenarios. I am using Spring Boot.
The second test is supposed to pass without Exception because there is no stubbing that I did to throw Exception.
The DirtiesContext is not working as well. If I remove the commented code in Test2.java I get the test to pass. I would like to remove the unnecessary subbing by using something similar to DirtiesContext.
I may be missing something basic. Can someone point to what I am doing incorrect.
#Service
public class SomeBeanProcessor {
#Autowired
private BeanValidator beanValidator;
public ResultBean process(SomeBean sb) throws ValidationException{
beanValidator.validateBean(sb);
//Do some processing and return ResultBean;
}
}
Test1.java
RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApp.class})
#WebAppConfiguration
#ContextConfiguration(classes=Test1.Test1Config.class) {
public class Test1 {
#Configuration
static class Test1Config {
#Bean
public BeanValidator getSomeRequestValidator() {
return new BeanValidator() {
public void validateBean(SomeBean bean) throws ValidationException {
throw new ValidationException("Validation failed");
}
};
}
}
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private SomeBeanProcessor aBeanProcessor;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
#DirtiesContext
public void doTestValidationErrors() throws ValidationException{
SomeBean sb = new SomeBean();
this.aBeanProcessor.process(sb);
Assert.fail("Should throw exception");
}
}
Test2.java
RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApp.class})
#WebAppConfiguration
#ContextConfiguration(classes=Test2.Test2Config.class) {
public class Test2 {
#Configuration
static class Test2Config {
//#Bean
//public BeanValidator getSomeRequestValidator() {
// return new BeanValidator() {
// public void validateBean(SomeBean bean) throws ValidationException { //Do nothing
// }
// };
//}
}
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private SomeBeanProcessor aBeanProcessor;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
#DirtiesContext
public void doTestSuccess() throws ValidationException{
SomeBean sb = new SomeBean();
this.aBeanProcessor.process(sb);
}
}
I am trying to implement a simple test for Java Spring Framework. Below is my code.
My Controller:
#RequestMapping(value = "/someMapping", method = RequestMethod.GET)
public String toTestMethod(Model model)
{
model.addAttribute("test", 1);
return "test";
}
Controller Test:
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup()
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void testMethodTest() throws Exception
{
this.mockMvc.perform(get("/someMapping"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(model().attribute("test", 1));
}
Application context:
#Configuration
#EnableWebMvc
public class WebAppContext extends WebMvcConfigurerAdapter
{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
{
configurer.enable();
}
#Bean
public ViewResolver viewResolver()
{
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
When I run this test, it fails with the following exception
java.lang.AssertionError: Model attribute 'test' expected:<1> but was:<null>.
I have no idea why this happens, can someone please clarify?
If needed, I will provide any additional information.
So, in my case the problem was in the setup(), now it looks like this:
#Before
public void setup()
{
// this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
MyController ctrl = new MyController();
this.mockMvc = MockMvcBuilders.standaloneSetup(ctrl).build();
}
It started working as expected after this change.
I'm using Spring Batch version 2.2.4.RELEASE
I tried to write a simple example with stateful ItemReader, ItemProcessor and ItemWriter beans.
public class StatefulItemReader implements ItemReader<String> {
private List<String> list;
#BeforeStep
public void initializeState(StepExecution stepExecution) {
this.list = new ArrayList<>();
}
#AfterStep
public ExitStatus exploitState(StepExecution stepExecution) {
System.out.println("******************************");
System.out.println(" READING RESULTS : " + list.size());
return stepExecution.getExitStatus();
}
#Override
public String read() throws Exception {
this.list.add("some stateful reading information");
if (list.size() < 10) {
return "value " + list.size();
}
return null;
}
}
In my integration test, I'm declaring my beans in an inner static java config class like the one below:
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
public class SingletonScopedTest {
#Configuration
#EnableBatchProcessing
static class TestConfig {
#Autowired
private JobBuilderFactory jobBuilder;
#Autowired
private StepBuilderFactory stepBuilder;
#Bean
JobLauncherTestUtils jobLauncherTestUtils() {
return new JobLauncherTestUtils();
}
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder embeddedDatabaseBuilder = new EmbeddedDatabaseBuilder();
return embeddedDatabaseBuilder.addScript("classpath:org/springframework/batch/core/schema-drop-hsqldb.sql")
.addScript("classpath:org/springframework/batch/core/schema-hsqldb.sql")
.setType(EmbeddedDatabaseType.HSQL)
.build();
}
#Bean
public Job jobUnderTest() {
return jobBuilder.get("job-under-test")
.start(stepUnderTest())
.build();
}
#Bean
public Step stepUnderTest() {
return stepBuilder.get("step-under-test")
.<String, String>chunk(1)
.reader(reader())
.processor(processor())
.writer(writer())
.build();
}
#Bean
public ItemReader<String> reader() {
return new StatefulItemReader();
}
#Bean
public ItemProcessor<String, String> processor() {
return new StatefulItemProcessor();
}
#Bean
public ItemWriter<String> writer() {
return new StatefulItemWriter();
}
}
#Autowired
JobLauncherTestUtils jobLauncherTestUtils;
#Test
public void testStepExecution() {
JobExecution jobExecution = jobLauncherTestUtils.launchStep("step-under-test");
assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
}
}
This test passes.
But as soon as I define my StatefulItemReader as a step scoped bean (which is better for a stateful reader), the "before step" code is no longer executed.
...
#Bean
#StepScope
public ItemReader<String> reader() {
return new StatefulItemReader();
}
...
And I notice the same issue with processor and my writer beans.
What's wrong with my code? Is it related to this resolved issue: https://jira.springsource.org/browse/BATCH-1230
My whole Maven project with several JUnit tests can be found on GitHub: https://github.com/galak75/spring-batch-step-scope
Thank you in advance for your answers.
When you configure a bean as follows:
#Bean
#StepScope
public MyInterface myBean() {
return new MyInterfaceImpl();
}
You are telling Spring to use the proxy mode ScopedProxyMode.TARGET_CLASS. However, by returning the MyInterface, instead of the MyInterfaceImpl, the proxy only has visibility into the methods on the MyInterface. This prevents Spring Batch from being able to find the methods on MyInterfaceImpl that have been annotated with the listener annotations like #BeforeStep. The correct way to configure this is to return MyInterfaceImpl on your configuration method like below:
#Bean
#StepScope
public MyInterfaceImpl myBean() {
return new MyInterfaceImpl();
}
We have added a warning log message on startup that points out, as we look for the annotated listener methods, if the object is proxied and the target is an interface, we won't be able to find methods on the implementing class with annotations on them.
as suggested by pojo-guy
Solution is to implement StepExecutionListener and Override beforeStep method to set stepExecution
#Override
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}