Mock Service class that has Autowired dependencies - java

I have a Service class & a config class as below:
public class MyService{
#Autowired
MyConfig myconfig;
#Autowired
private WebClient webClient;
private String result;
public String fetchResult(){
return webClient.get().uri(myConfig.getUrl()).retrieve().bodyToMono(String.class).block();
}
}
#ConfigurationProperties("prefix="somefield")
#Component
class MyConfig{
private String url;
//getter & setter
}
}
Below is the Junit:
#Runwith(MockitoJUnitRunner.class)
public class TestMe{
#InjectMocks
MyService myService;
#Test
public void myTest(){
when(myService.fetchResult().then return("dummy");
}
}
I am getting null pointer error when I run this class at webClient in Service class.
What could be the issue. I am new to JUnits.
How do I write a proper JUnit for this.

The easiest way to make the class testable is to use constructor injection
public class MyService{
private final MyConfig myconfig;
private final WebClient webClient;
private String result;
#AutoWired
MyService(
MyConfig myconfig,
WebClient webClient
) {
this.myconfig = myconfig;
this.webClient = webClient;
}
...
}

Related

How to reuse #MockBean definitions in Spring?

Is it possible to externalize #MockBean definitions, and combine them with composition instead of inheritance?
Because I find myself often repeating mock definitions, and I would rather be able to load/inject them and define them outside of the test class.
Example:
#SpringBootTest
public class Service1Test {
#MockBean
private Service1 service1;
#BeforeEach
public void mock() {
when(service1.call()).thenReturn(result1);
}
}
#SpringBootTest
public class Service2Test {
#MockBean
private Service2 service2;
#BeforeEach
public void mock() {
when(service2.call()).thenReturn(result2);
}
}
#SpringBootTest
public class Service3Test {
#MockBean
private Service1 service1;
#MockBean
private Service2 service2;
#BeforeEach
public void mock() {
when(service1.call()).thenReturn(result1);
when(service2.call()).thenReturn(result2);
}
}
I would like to externalize the mocks somehow, so I could just load them.
Pseudocode as follows:
public class MockService1 {
#MockBean
private Service1 service1;
#BeforeEach
public void mock() {
when(service1.call()).thenReturn(result1);
}
}
#SpringBootTest
#Import({MockService1.class, MockService2.class})
public class Service3Test {
}
Yes, you can achieve it by using an external configuration class. For example:
#TestConfiguration
public class TestConfig {
#Bean
#Primary
public Foo foo() {
return mock(Foo.class); //you can use Mockito or a different approach
here
}
#Bean
#Primary
public Bar bar() {
return mock(Foo.class);
}
}
#Import(TestConfig.class)
public class MyTestClass {
#Autowire
private Foo foo;
#Autowire
private Bar bar;
}

Unable to inject mock in Spring java bean?

I have a spring boot test as below
#SpringBootTest(class=AppConfig.class)
Public class AppTest{
#Autowired
private Product product
#Test
Public void test(){
.....
.....
}
}
My AppConfig.class is as below
Public clas AppConfig{
#Mock
EMailService emailService;
public AppConfig(){
MockitoAnnotations.initMocks(this)
}
#Bean
Public Product getProduct(){
return new Product();
}
}
Class Product{
#Autowired
private EMailService emailService
.....
......
}
Even after i defined #Mock EMailService emailService, whem i run the test, I get error EMailService bean not defined.
In your AppTest class
#SpringBootTest(class=AppConfig.class)
public class AppTest{
#Mock
private EMailService emailService;
#InjectMocks
private Product product;
#Test
public void test(){
.....
.....
}
}
Also, I think you do not need the definitions in the AppConfig class anymore

How to mock any services inside a #MockedBean?

Is it possible to just ignore/mock any injected dependencies inside a MockedBean?
Example:
#Service
public class MyService {
#Autowired
private MailerService mailer;
public void test1() {
//does not use mailer
}
public void test2() {
//...
mailer.send();
}
}
#Service
public class MailerService {
//I want these to be automatically mocked without explicit declaration
#Autowired
private JavaMailSender sender;
#Autowired
private SomeMoreService more;
//also these should be mocked without having to provide properties
#Value("${host}") private String host;
#Value("${user}") private String user;
#Value("${pass}") private String pass;
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class MyServiceTest {
#Autowird
private MyService myservice;
#MockBean
private MailserService mailer;
#Test
public void test1() {
myservice.test1();
}
}
I could use #MockBean to sort out mailer injection dependency. But any service inside the mocked bean would also have to be explicitly mocked.
Question: is it possible to mock a service "away". Means, just mock the bean and don't care what's inside the #MockedBean (or automatically also mock anything inside #MockedBean)?
As for me the best way to inject mocks is to use MockitoJUnitRunner
#RunWith(MockitoJUnitRunner.class)
public class MocksTests {
#InjectMocks
private ParentService parent;
#Mock
private InnerService inner; // this will be injected into parent
//your tests
}

How can Mockito mocks be wired when there is no constructor?

I was going through this tutorial which sets up the SUT using a constructor but my question is what if there is no constructor e.g. if we have:
#Autowire private PetRepository petRepository;
#Autowire private VetRepository vetRepository;
#Autowire private OwnerRepository ownerRepository;
#Autowire private VisitRepository visitRepository;
in the service/controller. How we can set this up?
I prefer to annotate the setters instead of the attributes on the classes. For example:
public class SomeClass implements SomeInterface {
private PetRepository petRepository;
private VetRepository vetRepository;
private OwnerRepository ownerRepository;
private VisitRepository visitRepository;
... some methods ...
#Resource
public void setPetRepository(PetRepository petRepository) {
this.petRepository= petRepository;
}
#Resource
public void setVetRepository(VetRepository vetRepository) {
this.petRepository= vetRepository;
}
#Resource
public void setOwnerRepository(OwnerRepository ownerRepository) {
this.ownerRepository = ownerRepository;
}
#Resource
public void setVisitRepository(VisitRepository visitRepository) {
this.visitRepository= visitRepository;
}
}
Then you can create a test case like this with Mockito and Junit:
public class SomeClassTestCase {
#Mock
private PetRepository petRepository;
#Mock
private VetRepository vetRepository;
#Mock
private OwnerRepository ownerRepository;
#Mock
private VisitRepository visitRepository;
private SomeClass someClass;
#Before
public void before(){
MockitoAnnotations.initMocks(this);
someClass = new SomeClass();
someClass.setPetRepository(petRepository);
someClass.setVetRepository(vetRepository);
someClass.setOwnerRepository(ownerRepository);
someClass.setVisitRepository(visitRepository);
}
#Test
public void someTest() {
...
}
}
Hope it helps.
You can use Mockito's #InjectMocks annotation. This will instantiate and then inject the Mock dependencies, for example:
#RunWith(MockitoJUnitRunner.class)
public ServiceTest {
#Mock
PetRepository petRepository
// ...omitted mocks ...
#InjectMocks
ClinicServiceImpl service;
}
See the documentation for further usage examples and caveats.

#InjectMocks and non-mocked objects (Spring Data Repositories)

Is there any way to inject non-mocked objects with #InjectMocks?
My Setup has a UserSignupService with dependencies to a MailService and a UserRepository (a Spring Data Repository). I've a unit test creating a spy of the MailService and I annotated the UserSignupService with #InjectMocks. Sadly this won't inject the UserRepository (non-mocked) into the service class.
Combining #InjectMocks with #Autowired won't inject the mocked MailService, but the bean from the application context.
MockitoAnnotations.initMocks() is run in AbstractServiceTest.setUp(). This class also holds the configuration of the the unit test (SpringJunit4TestRunner, etc.)
public class UserSignupServiceTest extends AbstractServiceTest {
#Autowired #Spy
private MailService<UserActivationMail> mailService;
#InjectMocks
private UserSignupServiceImpl service;
#Before
public void setUp() throws Exception {
super.setUp();
}
}
#Service
public class UserSignupServiceImpl implements UserSignupService {
private final UserRepository repository;
private final MailService<UserActivationMail> mailService;
#Autowired
public UserSignupServiceImpl(UserRepository repository,
MailService<UserActivationMail> mailService) {
this.repository = repository;
this.mailService = mailService;
}
//methods...
}
You need to initialize your Mockito MockitoAnnotations.initMocks(this);
Here is sample Spring JUnit test class I have
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:config/spring-context.xml" })
public class SpringKPTest {
#Autowired
SomeClassA SomeClassA;
#Mock
SomeClassB SomeClassB;
#Before
public void init(){
MockitoAnnotations.initMocks(this);
}
}

Categories