I have a service which I want to test:
public class DepartmentServiceImpl implements DepartmentService {
#Autowired
private DepartmentDao departmentDao;
//... other functions
#Override
public Department getDepartment(int depid) {
return departmentDao.getDepartment(depid);
}
}
and here is my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:sdnext-servlet.xml")
public class TestDepartmentDetails {
Department department = new Department();
DepartmentServiceImpl service = new DepartmentServiceImpl();
#Autowired
private DepartmentDao myDao;
#Test
public void testGetDepartment(){
assertEquals("lalalalalalala", service.getDepartment(98).getDepName());
}
Why it gives me a failure and throws a NullPointerException? Is there any other solution?
Note: This is an application which uses Spring and Hibernate.
Try it this way
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:sdnext-servlet.xml")
public class TestDepartmentDetails {
#Autowired
DepartmentService service;
#Test
public void testGetDepartment(){
assertEquals("lalalalalalala", service.getDepartment(98).getDepName());
}
Here you are creating instances manually which is not the right way while using Spring.
Please refer to the Spring docs below:
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/
You have to autowire DepartmentServiceImpl service instead of DepartmentDao in this case.
Related
I use Spring Boot 5 and JUnit in my project. I create a unit test to test the service.
Here is the service that I am testing:
#Service
#RequiredArgsConstructor
#Slf4j
public class BuilderServiceImpl implements BuilderService{
#Autowired
public AutoMapper autoMapper;
private final BuilderRepository builderRepository;
private final AdminUserRepository adminUserRepository;
#Override
public BuilderDto getByEmail(String email){
}
#Override
public List<BuilderMinDto> getAll() {}
#Override
public List<BuilderMinDto> getAll(int page, int size) {}
#Override
public SaveBuilderResponse create(Builder builder){
var str = autoMapper.getDummyText();
Builder savedBuilder = builderRepository.save(builder);
return new SaveBuilderResponse(savedBuilder);
}
}
And here is the test class that tests the service above:
#SpringBootTest
#RequiredArgsConstructor
#Slf4j
class BuilderServiceImplTest {
#Mock
private BuilderRepository builderRepository;
#Mock
private AdminUserRepository adminUserRepository;
private AutoCloseable autoCloseable;
private BuilderService underTest;
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
#AfterEach
void tearDown () throws Exception{
autoCloseable.close();
}
#Test
void getByEmail(){}
#Test
#Disabled
void getAll() { }
#Test
#Disabled
void testGetAll() {}
#Test
void create() {
//given
Builder builder = new Builder();
builder.setName("John Johnson");
builder.setCompanyName("Builders Test");
builder.setEmail("test#builders.com");
//when
underTest.create(builder);
//then
ArgumentCaptor<Builder> builderArgumentCaptor = ArgumentCaptor.forClass(Builder.class);
verify(builderRepository)
.save(builderArgumentCaptor.capture());
Builder captureBuilder = builderArgumentCaptor.getValue();
assertThat(captureBuilder).isEqualTo(builder);
}
}
When I start to run the test class the create method in BuilderServiceImpl fired and on this row:
var str = autoMapper.getDummyText();
I get NullPointerException(autoMapper instance is null).
Here is the definition of AutoMapper class:
#Component
#Slf4j
#RequiredArgsConstructor
public class AutoMapper {
public String getDummyText(){
return "Hello From AutoMapper.";
}
}
As you can see I use #Component annotation to register the AutoMapper class to the IoC container and Autowired annotation to inject it into autoMapper property in BuilderServiceImpl class.
Why autoMapper instance is null? How can I make autoMapper to be initialized?
In order to make #Autowire work you have to use the instance of BuilderServiceImpl (object under test) created by spring itself.
When you create the object like this (by yourself, manually):
#BeforeEach
void setUp(){
....
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
Spring doesn't know anything about this object, hence Autowiring won't work
Another thing that might be useful:
You've used #Mock for BuilderRepository and AdminUserRepository.
This are plain mockito annotation, and if you're using an integration/system test that runs the spring under the hood, probably this is not what you want:
Surely, it will create a mock, but it won't put it onto an application context, and won't substitute the beans of these classes that might have been created by spring.
So if this is what you want to achieve, you should use #MockBean instead.
This annotation belongs to Spring Testing framework rather than a plain mockito annotation.
All-in-all you might end up with something like this:
#SpringBootTest
class MyTest
{
#MockBean
BuilderRepository builderRepo;
#MockBean
AdminUserRepository adminUserRepo;
#Autowired // spring will inject your mock repository implementations
// automatically
BuilderServiceImpl underTest;
#Test
void mytest() {
...
}
}
Add #Autowire annotation Annotations on below fields. Error due your not initialized below object In BuilderServiceImpl
#Autowire
private final BuilderRepository builderRepository;
#Autowire
private final AdminUserRepository adminUserRepository;
Why are you creating BuildService manually? If you do this, set AutoMapper manualy too.
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
underTest.setAutoMapper(new AutoMapper());
}
You are not using di.
I would like to test a Java Web Controller using Spring (Not Spring Boot).
My Controller is
#Controller
#RequestMapping("/orders")
public class OrderHdrController {
#RequestMapping(value = "/getOrderList", method = RequestMethod.POST)
#ResponseBody
public Map<String, Object> getOrderTables(OrderSearchDto orderSearchDto) { ... }
}
And my test class is:
public class FilterActivityTest2 {
#Autowired
private OrderHdrController orderHdrController;
#Test
public void testActivity() {
OrderSearchDto orderSearchDto = new OrderSearchDto();
OrderSearchPanelDto orderSearchPanelDto = new OrderSearchPanelDto();
orderSearchPanelDto.setActivityTypes(Arrays.asList("TAKEOVER","DELIVERY"));
orderSearchDto.setOrderSearchPanelDto(orderSearchPanelDto);
Map<String, Object> result = orderHdrController.getOrderTables(orderSearchDto);
assertNotNull(result);
}
}
I do not want to mock any objects. I just want to run the test on the controller all the way to the db. But when I debug into the test, the orderHdrController is null in testActivity method.
What have I done wrong? Please help or ask me for more information. Thanks.
#RunWith(SpringRunner.class)
#WebMvcTest(OrderHdrController.class)
public class FilterActivityTest2 {
#Autowired
private OrderHdrController orderHdrController;
#Test
public void testActivity() {
OrderSearchDto orderSearchDto = new OrderSearchDto();
OrderSearchPanelDto orderSearchPanelDto = new OrderSearchPanelDto();
orderSearchPanelDto.setActivityTypes(Arrays.asList("TAKEOVER","DELIVERY"));
orderSearchDto.setOrderSearchPanelDto(orderSearchPanelDto);
Map<String, Object> result = orderHdrController.getOrderTables(orderSearchDto);
assertNotNull(result);
}
}
or If dont using any spring or junit then why using #test
simply make a main class
public class FilterActivityTest2{
public static void main(String args[]){
....... put your tast case code here
}
}
Your FilterActivityTest2 needs to be managed by the Spring context, to be able to autowire dependencies.
To do that, either annotate your test class with:
#RunWith(SpringRunner.class)
#SpringBootTest
Or extend the main test class that already has these annotations. If you created your project using spring initializer, you'll find that class in the tests created for you.
public class FilterActivityTest2 extends MyApplicationTests {
EDIT
For Spring, you can use #ContextConfiguration. Here is a good tutorial.
Also see the official documentation here.
If you do not use spring boot then you can create the application context manually
#Before
public void init() {
ApplicationContext context = desired implementation;
controller = context.getBean("bean name");
}
But better do this
#RunWith(MockitoJUnitRunner.class)
public class FilterActivityTest2 {
private OrderHdrController orderHdrController;
#MockBean
private Service service;
#MockBean
private Dao dao;
#Before
public void init() {
orderHdrController = new OrderHdrController(service, dao ....);
}
#Test
....
}
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 4 years ago.
This is my main code:
#Service
public class MainService {
public String mainMethod() {
SomeService someService = new SomeService("required");
// do Sth.
}
}
#Service
#RequiredArgsConstructor
public class SomeService {
#Autowired
private SomeOtherService someOtherService;
#Notnull
private final String requiredField;
// ...
}
#Service
public class SomeOtherService {
// just a bunch of public methods that might as well be static
}
And this is the test setup:
#RunWith(SpringRunner.class)
public class MainServiceTest {
#InjectMocks
private MainService mainService;
#Test
public void givenSth_whenSth_doSth() {
// test mainService.mainMethod();
}
}
Can you please tell me why someOtherService inside SomeService is null?
I figured out when yo use Spring's injection, you should not use manual instantiation (new ...) at the same time; so maybe new SomeService("required") is the problem? But then how do I inject field variables into SomeService if not by constructor call? I don't want to use the Builder because requiredField is supposed to be NotNull.
You can follow Adam Weidner's tutorial using MockBean for Spring Bean and extending AbstractTestNGSpringContextTest for TestNG with Spring
#TestExecutionListeners(MockitoTestExecutionListener.class)
#SpringBootTest
public class WidgetServiceIntegrationTest extends AbstractTestNGSpringContextTest {
#MockBean private SprockService sprockService;
#MockBean private SpringService springService;
#Autowired WidgetService widgetService;
#Test
public void testSomethingOnWidgetService() {
when(sprockService.makeASprock()).thenReturn(...);
For integration testing consider instantiating bean using context configuration like below
#RunWith(SpringRunner.class)
#ContextConfiguration("mockito-config.xml")
For a detailed explanation, please refer
https://blog.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/
For unit testing, Please consider using #Mock annotation provided by Mockito.
In this specific example, We could mock SomeOtherService as shown below
#RunWith(SpringRunner.class)
public class MainServiceTest {
#InjectMocks
private MainService mainService;
#Mock
private SomeOtherService someOtherService
#Test
public void givenSth_whenSth_doSth() {
// test mainService.mainMethod();
}
}
For a detailed explanation please refer the following post
http://www.vogella.com/tutorials/Mockito/article.html
http://static.javadoc.io/org.mockito/mockito-core/2.23.0/org/mockito/MockitoAnnotations.html
I'm trying to unit test a camel route. The route under test extends a custom abstract RouteBuilder (I know about favouring composition over inheritance - this is maintenance code). I've set up my test as #Roman Vottner did over here. Everything works (is initialized) until I hit the first abstract class up the hierarchy. It has an #Autowired class which wasn't initialized (is null) even though it was mocked and #Autowired when the test started. Any ideas on how to solve my injection problem?
#RunWith(CamelSpringRunner.class)
#BootstrapWith(CamelTestContextBootstrapper.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = {FooRouteTest.ContextConfig.class})
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class FooRouteTest {
#Configuration
#PropertySource({"classpath:some.properties", "classpath:environment.properties"})
public static class ContextConfig extends CamelConfiguration {
#Bean
public UserServices userServices() {
return mock(UserServices.class);
} //and many more of the like
}
#Autowired
private UserServices userServices; //and all the others too
#Test
public void testAfoo() throws Exception {
//....
template.setDefaultEndpointUri("direct://getTheData");
template.sendBody(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode));
//...
}
}
in the abstract super class while debugging:
#Autowired
public ClientServices clientServices;
//...
String clientNumber=clientServices.getLoggedInNumber(); //clientServices is null and not mocked!
//...
Solved this by explicitly declaring FooRoute as a bean:
#Bean
public FooRoute fooRoute(){
return new FooRoute();
}
#Override
public List<RouteBuilder> routes() {
final List<RouteBuilder> routes = new ArrayList<>();
routes.add(fooRoute());
return routes;
}
I want to test a class using Spring + JUnit + Mockito but I don't manage to make it work properly.
Let's say my class references a Service:
#Controller
public class MyController
{
#Autowired
private MyService service;
#PostConstruct
public void init() {
service.whatever();
}
public void doSomething() {
service.create();
}
}
And this Service references a Repository:
#Service
public class MyService {
#Autowired
private MyRepository repository;
public void whatever() {}
public void create() {
repository.save();
}
}
When testing the MyController class, I want the service to be mocked. The problem is: even when the service is mocked, Spring tries to inject the repository in the mock.
Here is what I did. Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyControllerTestConfiguration.class })
public class MyControllerTest {
#Autowired
private MyController myController;
#Test
public void testDoSomething() {
myController.doSomething();
}
}
Configuration class:
#Configuration
public class MyControllerTestConfiguration {
#Bean
public MyController myController() {
return new MyController();
}
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
}
And the error I get: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [test.test.MyRepository] found for dependency
I tried to initialize the mock using Mockito's #InjectMocks annotation but this fails because the #PostConstruct method is called before the mocks injection, generating a NullPointerException.
And I cannot simply mock the repository because in real life that would make me mock A LOT of classes...
Can anyone help me on this?
Use constructor instead of field injection. That makes testing a lot easier.
#Service
public class MyService {
private final MyRepository repository;
#Autowired
public MyService(MyRepository repository) {
this.repository = repository;
}
public void whatever() {}
public void create() {
repository.save();
}
}
-
#Controller
public class MyController {
private final MyService service;
#Autowired
public MyController(MyService service) {
this.service = service;
}
#PostConstruct
public void init() {
service.whatever();
}
public void doSomething() {
service.create();
}
}
This has several advantages:
You don't need Spring in your tests. This allows you to do proper unit tests. It also makes the test incredibly fast (from seconds to milliseconds).
You cannot accidentally create an instance of a class without its dependencies which would result in a NullPointerException.
As #NamshubWriter pointed out:
[The instance fields for the dependencies] can be final, so 1) they cannot be accidentally modified, and 2) any thread reading the field will read the same value.
Discard the #Configuration class and write a test like this:
#RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
#Mock
private MyRepository repository;
#InjectMocks
private MyService service;
#Test
public void testDoSomething() {
MyController myController = new MyController(service);
myController.doSomething();
}
}
Use interfaces, especially if you use some kind of AOP (transactions, security, etc), i.e. you'll have interface MyService and class MyServiceImpl.
In configuration you'll have:
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
you should put the #InjectMocks annotation in your controller and #Mock in your service, look:
#Autowired
#InjectMocks
private MyController myController;
#Autowired
#Mock
private MyService myService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testDoSomething() {
myController.doSomething();
}