I frequently see Spring test like this:
#WebMvcTest(controllers = MyController.class)
#Import({DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, JpaConfig.class, MyService.class})
#ContextConfiguration(classes = AppConfig.class)
#MockBeans({#MockBean(MyLocationProvider.class), #MockBean(MyOtherService.class)})
public class MyServiceControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
#Autowired
private MyJobRepository myJobRepository;
#MockBean
private MyComplicatedService myComplicatedService;
....
}
I can see there are #Import, #ContextConfiguration, #MockBeans(Before the class), #MockBeans(As fields), #Autowired.
I can understand the different between #MockBeans(Before the class), #MockBeans(As fields), #Autowired. But I am not sure what is the difference between #Import and #ContextConfiguration.
Can someone help?
Related
In the code below, surprisingly, when I use #AutoWired, the fields are set but this class has not been registered as a bean, and the program works correctly, but when I took the MockingTest bean from the context, it said that there is no such bean. Is it possible to use AutoWired without registering a class as a bean?
#SpringBootTest
public class MockingTest {
#Autowired
private ApplicationContext context;
#Autowired
private CollegeStudent collegeStudent;
#Autowired
private StudentGrades studentGrades;
#Mock
private ApplicationDao applicationDao;
#InjectMocks
private ApplicationService applicationService;
#BeforeEach
void setUp() {
collegeStudent.setFirstname("Al");
collegeStudent.setLastname("Zam");
collegeStudent.setEmailAddress("555#gmail.com");
collegeStudent.setStudentGrades(studentGrades);
}
#Test
#DisplayName("mockito testing")
public void testMocking() {
when(applicationDao.addGradeResultsForSingleClass(studentGrades.getMathGradeResults()))
.thenReturn(100.0);
assertEquals(100.0, applicationService.addGradeResultsForSingleClass(studentGrades.getMathGradeResults()));
verify(applicationDao).addGradeResultsForSingleClass(studentGrades.getMathGradeResults());
verify(applicationDao,times(1)).addGradeResultsForSingleClass(studentGrades.getMathGradeResults());
}
}
#SpringBootTest sets up multiple hooks into the test runner. One of them is to inject beans into #Autowired fields, without the test class itself being a bean.
#SpringBootTest actually does a lot of "magic" behind the scenes, just so that things just work like we might expect, without us thinking about them too much.
I am writing some unit tests and I am confused due to the different annotations in unit tests:
One of them is like:
#ExtendWith(SpringExtension.class)
class EmployeeServiceImplTest {
#MockBean
private EmployeeRepository employeeRepository;
#Autowired
private EmployeeServiceImpl employeeService;
#Test
void testFindAll() {
//...
}
//...
}
And another is using the following annotations:
#RunWith(MockitoJUnitRunner.class)
public class EmployeeServiceImplTest {
#Mock
private EmployeeRepository employeeRepository;
#InjectMocks
private EmployeeServiceImpl employeeService;
#Test
void testFindAll() {
//...
}
//...
}
So, I use Java and Spring Boot for language and framework. So, which annotations should I use?
#ExtendWith(SpringExtension.class) or #RunWith(MockitoJUnitRunner.class),
#MockBean or #Mock,
#Autowired or #InjectMocks
Any idea regarding to these annotations?
I added spring security to my application and it's working great, but now existing tests are broken, when I run them I get:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field userDetailsService in xxx.xxx.someapp.spring.security.WebSecurityConfig required a bean of type 'xxx.xxx.sardinatank.someapp.security.services.UserDetailsServiceImpl' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
This is how I'm wiring the UserDetailsServiceImpl in my WebSecurityConfig
#Autowired
UserDetailsServiceImpl userDetailsService;
This is an example controller
#RestController
public class HelloWorldController {
#GetMapping
public ModelAndView helloWorld() {
final ModelAndView bootstrapFront = new ModelAndView("index.html");
return bootstrapFront;
}
}
And this is a test that fails
#WebMvcTest(controllers = HelloWorldController.class)
#ContextConfiguration
#WebAppConfiguration
class HelloWorldControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void getBootstrappedDoc() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andReturn();
assertEquals("index.html", mvcResult.getModelAndView().getViewName());
}
}
I guess you might have declared it as a #Component or something that is not part of this list:
#Controller, #ControllerAdvice, #JsonComponent, Converter, Filter, WebMvcConfigurer
which means UserdetailServiceImpl will not be part of the #SpringWebMVC sliced test context, hence spring doesn't bootstrap it.
You have 2 options:
Either you decorate your UserServiceImpl with one of the aforementioned annotations: e.g
#JsonComponent
class UserDetailsServiceImpl implements userDetailsService { ... };
Or you inject a mock of UserServiceImpl in your test with #MockBean:
#WebMvcTest(controllers = HelloWorldController.class)
#ContextConfiguration
#WebAppConfiguration
class HelloWorldControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
UserDetailsServiceImpl userDetailsService;
private MockMvc mockMvc;
//...
}
I've been working on a personal project very recently and looking at my test file I realized I have some regarding spring annotations:
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class BookingServicesTests {
private MockMvc mvc;
#Mock
private BookingRepository bookingRepository;
#InjectMocks
private BookingResource bookingController;
#Before
public void setup() {
JacksonTester.initFields(this, new ObjectMapper());
mvc = MockMvcBuilders
.standaloneSetup(bookingController)
.setControllerAdvice(new ConflictExceptionController())
.build();
}
...
}
So the thing is that #SpringBootTest is made to test your application using real HTTP methods. But in my setup method I included a MockMvcBuilders statement, which is a standalone test (no server and no application context).
My question is:
Are those elements incompatible?
One element obfuscate the other? This is: by using MockMvcBuilder can I get rid of #SpringBootTest?
Thanks
Use one or the other, not both. You are only allowed one JUnit's #runwith() and the value you pass in, whether it be SpringRunner.class or MockitoJUnitRunner.class, has very different behaviors.
So the code you posted is incorrect as #SpringBootTest will try to load the application context when your test class is "running with MockitoJUnitRunner". Therefore #SpringBootTest should be used along with #runWith(SpringRunner.class), as such
#RunWith(SpringRunner.class)
#WebMvcTest(BookingResource.class) // multiple controller class could go here
#AutoConfigureMockMvc
public class BookingServicesTests {
#Autowired
private MockMvc mvc;
#MockBean
private BookingRepository bookingRepository;
...
}
Notice how I replace #SpringBootTest() with #WebMvcTest(). This is because #WebMvcTest() only scans components that are #Controller and loads configuration for the web layer, whereas #SpringBootTest() does so for the entire application.
Or what you did with Mockito without Spring:
#RunWith(MockitoJUnitRunner.class)
public class BookingServicesTests {
private MockMvc mvc;
#Mock
private BookingRepository bookingRepository;
#InjectMocks
private BookingResource bookingController;
...
}
Here is my class under test:
KafkaProcessorApplication
#EnableBinding(Processor.class)
#EnableConfigurationProperties(KafkaProperties.class)
public class KafkaProcessorApplication {
#Autowired
private Processor processor;
#Autowired
private KafkaProperties kafkaProperties;
private KafkaTemplate<String, String> kafkaTemplate;
#Autowired
KafkaProcessorApplication(SenderConfig senderConfig) {
this.kafkaTemplate = senderConfig.kafkaTemplate();
}
Here, SenderConfig is a just a simple config class with the method kafkaTemplate() creating a new instance of KafkaTemplate.
SenderConfig
#Configuration
public class SenderConfig {
#Autowired
KafkaProperties kafkaProperties;
public ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(new HashMap());
}
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(ProducerFactory()));
}
}
Here is the test class:
KafkaTestClass
#SpringBootTest
#ActiveProfiles("test")
#ContextConfiguration(classes = {SenderConfig.class, KafkaProcessorApplication.class})
#TestPropertySource(locations = "classpath:test-resources.properties")
#RunWith(SpringRunner.class)
public class KafkaProcessorApplicationTest {
#Autowired
private Processor processor;
#Mock
private SenderConfig senderConfig;
#Mock
private KafkaProperties kafkaProperties = new KafkaProperties();
#Mock private KafkaTemplate mockKafka;
#Autowired
#InjectMocks
private KafkaProcessorApplication app;
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn("ServerConfig").when(kafkaProperties).getServersConfig();
when(senderConfig.kafkaTemplate()).thenReturn(kafkaTemplate);
}
I want to mock kafkaTemplate. But, its instantiation is in constructor which is being executed even before the #Before is executed, where the logic of mocking the method is written.
Just curious why is the constructor being executed first, and also, how can I mock the method if this is the case?
What could be the approaches of mocking the kafkaTemplate, without using Powermock and without modifying the class under test as I can not change it?
When you use #SpringBootTest, the Spring dependency tree is resolved before the #Before method has a chance to execute. This includes constructing the KafkaProcessorApplication bean and its dependencies. This is why the constructor runs before #Before.
What you want is Spring's #MockBean to create and inject a mock bean in the application context.
This question has a great write-up of how you can use this: Difference between #Mock, #MockBean and Mockito.mock()
update
Now I see. The problem is that the KafkaProcessorApplication accesses the mock in its constructor before you can configure it.
This can be solved with a separate test Spring configuration that will return a configured SenderConfig mock bean as described here: Testing spring bean with post construct