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
}
Related
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;
}
Earlier i had the implemenation like:
public class FeedbackService {
private final FeedbackHelper feedbackHelper;
#Inject
public FeedbackService(FeedbackHelper feedbackHelper) {
this.feedbackHelper = feedbackHelper;
}
//rest of the class
}
Test file
public class FeedbackDataServiceTest {
private FeedbackService feedbackService;
#Mock private FeedbackHelper feedbackHelper;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.feedbackService = new FeedbackService(feedbackHelper);
}
}
It was workign fine. But when I changed to:
public class FeedbackService {
#Inject private FeedbackHelper feedbackHelper;
}
Test file
public class FeedbackDataServiceTest {
private FeedbackService feedbackService;
#Mock private FeedbackHelper feedbackHelper;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
}
The test cases started failing. Is anything else required to be done?
Use #InjectMocks to inject the mocks into the Service class.
public class FeedbackDataServiceTest {
#InjectMocks private FeedbackService feedbackService;
#Mock private FeedbackHelper feedbackHelper;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
}
There are three types of injection. The suggestion is to use constructor injection if the dependency is mandatory and setter injection if it is optional.
You can use #InjectMock on the under test object if you want to mock a field injection. But it is hard to test if you want to test without mocking framework.
#RunWith(MockitoJUnitRunner.class)
public class ApplicationTest
{
#InjectMocks
MainClass sut;
#Mock
DatabaseDAO dependentClassOne;
#Test
public void validateTest()
{
boolean saved = sut.save("abcd");
assertEquals(true, saved);
}
I have a bit of mess with Mockito and fields annotated with #Spy .
I want to test this class:
#Service
public class CategoryServiceImpl implements CategoryService {
#Autowire
private CategoryRepository categoryRepository;
private BoundMapperFacade<CategoryDTO, Category> boundMapper;
#Autowired
public void createMapper(final Mapper mapper) {
boundMapper = mapper.getMapperFactory().getMapperFacade(
CategoryDTO.class, Category.class);
}
// Rest of methods...
}
So, I have created a testing class as shown below:
#RunWith(MockitoJUnitRunner.class)
public class CategoryServiceImplTest {
#Mock
private CategoryRepository categoryRepository;
#Spy
private Mapper mapper;
#Spy
private BoundMapperFacade<CategoryDTO, Category> boundMapper;
#InjectMocks
private CategoryServiceImpl categoryServiceImpl;
// Rest of methods...
}
When Mockito instances categoryServiceImpl injects categoryRepository as a mock class but it does not know how to create boundMapper. This class depends on mapper, so I have rewritten my code:
#RunWith(MockitoJUnitRunner.class)
public class CategoryServiceImplTest {
#Mock
private CategoryRepository categoryRepository;
#Spy
private Mapper mapper;
private BoundMapperFacade<CategoryDTO, Category> boundMapper = spy(mapper.getMapperFactory().getMapperFacade(
CategoryDTO.class, Category.class));
#InjectMocks
private CategoryServiceImpl categoryServiceImpl;
// Rest of methods...
}
But now the problem is the creation of boundMapper is executed before Mockito injects mapper, so I get a NullPointerException.
So, is there any way to create an spy class who depends on another one and finally inject this last one in a field tagged as #InjectMocks?
If you have a spy that depends on a mock or a spy, the only way I've seen to do it is inside the #Before decorated function.
#RunWith(MockitoJUnitRunner.class)
public class CategoryServiceImplTest {
#Mock
private CategoryRepository categoryRepository;
#Spy
private Mapper mapper;
private BoundMapperFacade<CategoryDTO, Category> boundMapper = ;
#InjectMocks
private CategoryServiceImpl categoryServiceImpl;
#Before
public void setup() {
boundMapper = spy(mapper
.getMapperFactory()
.getMapperFacade(CategoryDTO.class, Category.class)
);
}
// Rest of methods...
}
I'm not sure how this works with #InjectMocks, so that might be something to look into. There might be a non-decorator version of it that you can run after boundMapper is initialized if CategoryServiceImpl is dependent on it.
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.
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);
}
}