I have two classes which are annotated as #Component
#Component
public class ClientMapper {
public Client convert(ClientEntity clientEntity) {
Client client = new Client();
BeanUtils.copyProperties(clientEntity, client);
return client;
}
public ClientEntity convert(Client client) {
ClientEntity clientEntity = new ClientEntity();
BeanUtils.copyProperties(client, clientEntity);
return clientEntity;
}
}
#Component
public class OrderMapper {
public Order convert(OrderEntity orderEntity) {
Order order = new Order();
BeanUtils.copyProperties(orderEntity, order);
return order;
}
public OrderEntity convert(Order order) {
OrderEntity orderEntity = new OrderEntity();
BeanUtils.copyProperties(order, orderEntity);
return orderEntity;
}
}
I injected them into different services
#Service
#AllArgsConstructor
public class ClientServiceImpl implements ClientService {
private final ClientMapper clientMapper;
private final ClientRepository clientRepository;
#Service
#AllArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderMapper orderMapper;
private final OrderRepository orderRepository;
private final OrderNumberRepository orderNumberRepository;
But all time my mappers is null. I don't create new Object of them using new command. Also with my repository interfaces everything is fine, so my way to inject my comments(#AllArgsContrustor) works correct.
Little note, I have tests classes where I used #InjectMocks on my services classes. Can it be that my error occupied because of this annotation?
#ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
#Mock
private OrderRepository orderRepository;
#InjectMocks
private OrderServiceImpl orderService;
You are using MockitoExtension, spring won't create component of OrderMapper.
If you need actual implementation. Use #Spy annotation of MockitoExtension.
#ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
#Mock
private OrderRepository orderRepository;
#Spy
private OrderMapper orderMapper = new OrderMapper(); // Note: new OrderMapper() is optional as you have No Argument Constructor
#InjectMocks
private OrderServiceImpl orderService;
Or as a practice #Mock is always best way to go instead of #Spy incase of Unit test.
Related
I'm writing test cases in which I would like to use the same BeanFactory.getBean() instance with only small changes - so I don't want to write e.g.: Class class = factory.getBean("someName", Class.class); every single time, but I'd rather just put it in a private field and use the class variable in the various test cases.
The test class looks like this:
#ExtendWith(SpringExtension.class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles(value = "test")
#Import(TestConfiguration.class)
public class FeedbackServiceTest {
#Autowired
private BeanFactory beanFactory;
//I'd like to use this in the various methods:
private AppUser testSender = beanFactory.getBean("sender", AppUser.class);
//other fields to be cleared up with BeanFactory:
private final FeedbackRepository feedbackRepository = Mockito.mock(FeedbackRepository.class);
private final AppUserRepository appUserRepository = Mockito.mock(AppUserRepository.class);
private final ValueRepository valueRepository = Mockito.mock(ValueRepository.class);
private final FeedbackService feedbackService = new FeedbackServiceImpl(feedbackRepository, appUserRepository,
valueRepository);
private final AppUser testTarget = new AppUser("someUserDatas"));
private final Value testValue = new Value("someValueDatas");
private final Feedback testFeedback = new Feedback(testSender, testValue, testTarget, "otherDatas");
#Test
public void saveNewFeedback_WithValidParams_ShouldSaveFeedback() {
Mockito.when(appUserRepository.findById(testSender.getId()))
.thenReturn(Optional.of(testSender));
Mockito.when(appUserRepository.findById(testTarget.getId()))
.thenReturn(Optional.of(testTarget));
Mockito.when(valueRepository.findById(testValue.getId()))
.thenReturn(Optional.of(testValue));
feedbackService.saveNewFeedback(testFeedback);
Mockito.verify(feedbackRepository, times(1)).save(Mockito.any(Feedback.class));
}
And the TestConfiguration class is this:
#Configuration
public class TestConfiguration {
#Bean(name = "sender")
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public AppUser getAppUser() {
return new AppUser("Pista", "pista#pista.com", Gender.MALE, "p123_ABC", "USER",
LocalDate.parse("1967-10-12"));
}
}
If I put the private AppUser testSender = beanFactory.getBean("sender", AppUser.class); line in the method scope, it runs fine, otherwise it throws the glorious NullPointerException
If I understand you correctly, then your requirement is to use reference of testSender in each of method. For this I can suggest you to use #BeforeEach in FeedbackServiceTest
:
public class FeedbackServiceTest {
#Autowired
private BeanFactory beanFactory;
//I'd like to use this in the various methods:
private AppUser testSender;
#BeforeEach
public void setBean(){
testSender= beanFactory.getBean("sender", AppUser.class);
}
...//rest other class
& then use testSender in each of your test method.
Edit:
Alternatively, you can use Qualifier bean if your TestConfiguration is correctly creating bean (Here no need to use #BeforeEach in this case):
public class FeedbackServiceTest {
#Autowired
#Qualifier(value="sender")
private AppUser testSender;
...
#RequiredArgsConstructor
public class GService {
#Autowired
private GRepository repository;
private final List<GRule> rules;
And my test class is like this;
#ExtendWith(MockitoExtension.class)
class GServiceTest {
#InjectMocks
private GService service;
#Mock
private GRepository repository;
#Mock
private List<GameRule> rules;
Rules is okay but repository is not initialized and it is null. How can I initialize the repository?
You can set by using ReflectionTestUtils.setField(...) in Before Method;
ReflectionTestUtils.setField(service, "repository", Mockito.mock(GRepository.class));
Is it possible to use dependency injection with unit tests using Spring Boot? For integration testing #SpringBootTest start the whole application context and container services. But is it possible to enable dependency injection functionality at unit test granularity?
Here's the example code
#ExtendWith(SpringExtension.class)
public class MyServiceTest {
#MockBean
private MyRepository repo;
#Autowired
private MyService service; // <-- this is null
#Test
void getData() {
MyEntity e1 = new MyEntity("hello");
MyEntity e2 = new MyEntity("world");
Mockito.when(repo.findAll()).thenReturn(Arrays.asList(e1, e2));
List<String> data = service.getData();
assertEquals(2, data.size());
}
}
#Service
public class MyService {
private final MyRepository repo; // <-- this is null
public MyService(MyRepository repo) {
this.repo = repo;
}
public List<String> getData() {
return repo.findAll().stream()
.map(MyEntity::getData)
.collect(Collectors.toList());
}
}
Or should I just manage the SUT (service class) as POJO and manually inject the mocked dependencies? I want to keep tests fast but minimize boilerplate code.
As #M.Deinum mentioned in the comments, unit tests shouldn't use dependency injection. Mock MyRepository and inject MyService using Mockito (and Junit5):
#ExtendWith(MockitoExtension.class)
public class MyServiceTest {
#InjectMocks
private MyService service;
#Mock
private MyRepository repo;
#Test
void getData() {
MyEntity e1 = new MyEntity("hello");
MyEntity e2 = new MyEntity("world");
Mockito.when(repo.findAll()).thenReturn(Arrays.asList(e1, e2));
List<String> data = service.getData();
assertEquals(2, data.size());
}
}
If you want to test the repository, use #DataJpaTest. From the docs:
Using this annotation will disable full auto-configuration and instead
apply only configuration relevant to JPA tests.
#DataJpaTest
public class MyRepositorTest {
#Autowired
// This is injected by #DataJpaTest as in-memory database
private MyRepository repo;
#Test
void testCount() {
repo.save(new MyEntity("hello"));
repo.save(new MyEntity("world"));
assertEquals(2, repo.count());
}
}
In conclusion, the suggested approach is to test the service layer mocking the repository layer with Mockito (or similar library) and to test the repository layer with #DataJpaTest.
You have not added the #Autowired in service for MyRepository
Service Class
#Service
public class MyService {
private final MyRepository repo; // <-- this is null
#Autowired
public MyService(MyRepository repo) {
this.repo = repo;
}
public List<String> getData() {
return repo.findAll().stream()
.map(MyEntity::getData)
.collect(Collectors.toList());
}
}
Service Test Class
#ExtendWith(MockitoExtension.class)
public class MyServiceTest {
#Mock
private MyRepository repo;
#InjectMocks
private MyService service;
#Test
void getData() {
MyEntity e1 = new MyEntity("hello");
MyEntity e2 = new MyEntity("world");
Mockito.when(repo.findAll()).thenReturn(Arrays.asList(e1, e2));
List<String> data = service.getData();
assertEquals(2, data.size());
}
}
#Mapper(uses = SomeMapper.class,imports = Date.class)
public interface DomainModelMapper {
Model domainToModel(Domain domain);
#Mapping(target="dateUpdated", source="dateUpdated" ,defaultExpression = "java(Date.from(java.time.OffsetDateTime.now().toInstant()))")
#Mapping(target="id.key",source="id.key",defaultExpression = "java(com.datastax.driver.core.utils.UUIDs.timeBased())")
Domain modelToDomain(Model model);
}
I have a mapper class to do some Date conversions
public class SomeMapper {
public Date OffsetDateTimeToDate(OffsetDateTime offsetDateTime) {
return offsetDateTime != null ? Date.from(offsetDateTime.toInstant()):null;
}
public OffsetDateTime DateToOffsetDateTime(Date date) {
return date != null ? date.toInstant().atOffset(ZoneOffset.UTC) : null;
}
}
This is my service class where I use DomainModelMapper
#Service
public class SomeServiceImpl implements SomeService {
#Autowired
someRepository someRepository;
private final DomainModelMapper domainToModelMapper =
Mappers.getMapper(DomainModelMapper.class);
#Override
public Model saveSomething(Model model) {
return DomainModelMapper.domainToModel(someRepository
.save(DomainModelMapper.modelToDomain(model)));
}
How can I unit test saveSomething(Model model) method? How I can inject Mapstruct classes or mock them?
If you make the #Mapper interface as a Spring-based component model, then it can be autowired through #Autowired annotation. Read more at 4.2. Using dependency injection
#Mapper(uses = SomeMapper.class,imports = Date.class, componentModel = "spring")
public interface DomainModelMapper {
// IMPLEMENTATION
}
#Service
public class SomeServiceImpl implements SomeService {
#Autowired
SomeRepository someRepository;
#Autowired
DomainModelMapper domainModelMapper;
// THE REST OF THE IMPLEMENTATION
}
The testing becomes fairly easy since all the beans can be also injected in the #SpringBootTest with the #Autowired annotation.
The DomainModelMapper can be autowired and used in the unit test as is and rely on its implementation
The SomeRepository shall be either mocked using #MockBean which overwrites an existing bean or creates a new one if none of that type exists... or autowired as in the implementation if you use an in-memory database for the testing phase, such as H2.
In any case, the test class will be ready for testing.
#SpringBootTest
public class SomeServiceTest {
#Autowired // or #MockBean
SomeRepository someRepository;
#Autowired // no need to mock it
DomainModelMapper domainModelMapper;
#Test
public void test() {
// TEST
}
}
I have been doing Junit tests the past few weeks so my experience, being a junior programmer is fairly limited. After testing the easier service classes in the project, now I am stuck.
The problem is that I can't inject some private final someRepository into the constructor of the service class that I am testing, namely:
#RunWith(SpringRunner.class)
public class SomeServiceTest {
#Mock
private SomeRepository someRepository;
#InjectMocks
private SomeService someService;
#Test
public void testMyFunc() {
SomeOtherDto param = new SomeOtherDto();
param.setVar1(...);
param.setVar2(...);
Mockito.when(someRepository.getIt()).thenReturn(-1L);
Mockito.when(someService.myPrivBoolBuilder(param,-1L))
.thenReturn(new BooleanBuilder());
Pageable pageable = null;
Page<SomeDto> result = someService.myFunc(param, pageable);
assertEquals(expResult,
}
/* ... */
}
and the service I am testing:
#Service
#Transactional
public class SomeService implements someAbstractService {
private final CustomMapper customMapper
private final SomeRepository someRepository;
private final SomeOtherRepository someOtherRepository;
#Autowired
public SomeService(final CustomMapper customMapper, final SomeRepository someRepository,
final SomeOtherRepository someOtherRepository, etc)
{ /* ... */ }
public Page<SomeDto> myFunc(final SomeOtherDto param, final Pageable pageable) {
final BooleanBuilder predicate = myPrivBoolBuilder(param,
someOtherRepository.getId());
return someRepository.findAll(predicare, pageable).map(obj -> {
return customMapper.map(obj) });
}
public BooleanBuilder myPrivBoolBuilder(final SomeOtherDto param, final Long id) {
BooleanBuilder predicate = new BooleanBuilder();
final QSomeRepository qSomeRepository = QSomeRepository.someRepository;
final QSomeOtherRepository qSomeOtherRepository = QSomeOtherRepository.someOtherRepository();
predicate.and(qSomeRepository.someField.someOtherField.goe(param.getX()));
predicate.and(qSomeRepository.someField2.someOtherField2.isNotNull()));
predicate.and(qSomeOtherRepository.someField.someOtherField.id.eq(id()
.or(qSomeOtherRepository.someField.someOtherField.id.in(...))));
return predicate;
}
/* ... */
}
My problem is that when I run the test someOtherRepository.getId() return null with SpringRunner.class. When I run with MockitoJUnitRunner.class the someService constructor throws a constructor error: someRepository is NULL
I have tried multiple ways (tried #Spy, #MockBean, Mockito().doReturn... syntax, etc), but these are the two errors I get. I'm pretty sure it's a matter of using the Mocking framework correctly.
If you need other snippets or details, I will kindly offer them.
The reason is that Mockito tries to construct the object because of the #InjectMocks annotation.
SpringRunner is not necessary as this is not a spring test. If you really want a special runner, you can go for MockitoJUnitRunner.
You can simply initialize the Mockito annotations in your Before method and then create your service instance with the constructor providing the mocked dependencies.
public class SomeServiceTest {
#Mock
private SomeRepository someRepository;
private SomeService someService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
someService = new SomeService(someRepository);
}
/* ... */
}
You could use ReflectionTestUtil provided by Spring to inject a mock of SomeRepository from the test class.
eg;
ReflectionTestUtils.setField(someService, "someRepository", mock(SomeRepository.class));