JUnit How to Test methods that create New Objects - spring-boot

I want to test the consumeEvents in the following class using Junit5 in Spring Boot
public class Consumer {
private final ConsumerInterface interface;
public void consumeEvents(Event event) {
EventA eventA = new EventA();
eventA.setPayload(event.getPayload());
interface.handleEvent(eventA.extractModel());
}
}
}
the extractModel() returns an EventPayload class
#ExtendWith(MockitoExtension.class)
class ConsumerTest {
#Mock
ConsumerInterface consumerInterface;
private Consumer consumer;
#BeforeEach
public void setup(){
consumer = new Consumer(consumerInterface);
}
#Test
public void consumeEvents() {
var event = Mockito.mock(Event.class);
var eventPayload = Mockito.mock(EventPayload.class);
/* --------- not working as a solution ------ */
EventA eventA = Mockito.spy(new EventA());
when(eventA.extractModel()).thenReturn(eventPayload);
/* ------------------------------------------ */
doNothing().when(paymentRequester).handleReceiptPaymentReceivedEvent(any());
consumer.consumeEvents(event);
verify(consumerInterface, times(1)).handleEvent(eventPayload);
}
}
The test fails,
java.lang.NullPointerException at EventA.extractModel
How to mock the new created instance inside the method (EventA) ?

Related

Entity listener can inject other Spring dependencies but not repository

I have this entity listener class:
#Component
public class AssignmentListener {
private KafkaService kafkaService;
private String topic;
private AssignmentMapper assignmentMapper;
private AttachmentRepository attachmentRepository;
#Autowired
public final void setKafkaService(KafkaService kafkaService) {
this.kafkaService = kafkaService;
}
#Autowired
public final void setTopic(
#Value("${topic}") String topic
) {
this.topic = topic;
}
#Autowired
public final void setAssignmentMapper(AssignmentMapper assignmentMapper) {
this.assignmentMapper = assignmentMapper;
}
#Autowired
public final void setAttachmentRepository(AttachmentRepository attachmentRepository) {
this.attachmentRepository = attachmentRepository;
}
#PostPersist
#PostUpdate
#Transactional("transactionManager")
#TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void postUpdate(Assignment assignment) {
var attachments = attachmentRepository.findAllByAssignmentId(assignment.getId());
var dto = assignmentMapper.mapToKafkaMessage(assignment);
dto.setAttachments(
attachments.stream()
.map(Attachment::getPath)
.collect(Collectors.toSet())
);
kafkaService.sendMessage(
topic,
dto
);
}
}
and it worked normally until adding this last field which is repository. All other dependencies were injected however no matter what I do this won't get injected. Just to mention this is happening in tests. Do you have any suggestion?

How to handle objects created within the method under test

I have the following model classes:
#Data
public class Address {
private String street;
private int number;
}
#Data
public class Person {
private String name;
private Address address;
}
and the following services:
#Service
public class MyService {
private final OtherService otherService;
public MyService(OtherService otherService) {
this.otherService = otherService;
}
public void create() {
Person myPerson = new Person();
myPerson.setName("John");
otherService.synchronize(myPerson);
myPerson.getAddress().setNumber(12);
}
}
#Service
public class OtherService {
public void synchronize(Person person) {
Address address = new Address();
address.setStreet("sample street");
address.setNumber(123);
person.setAddress(address);
}
}
I want to write a unit test for MyService. This is the not working version of the test:
#ExtendWith(SpringExtension.class)
class MyServiceTest {
#Mock OtherService otherService;
#InjectMocks MyService myService;
#Test
void test_create() {
// GIVEN
doNothing().when(otherService).synchronize(any(Person.class));
// WHEN
myService.create();
// THEN
verify(otherService).synchronize(any());
}
}
This fails because the myPerson object is created within the method being tested and therefore I get a NullPointerException when running the test. How could I deal with this issue? should I capture the value passed to the otherService?
There's a little complexity but it's not bad. Replace your doNothing call with something like this:
Mockito.doAnswer(
new Answer<Void>() {
public Void answer(InvocationOnMock invocation) throws Exception {
Person arg = invocation.getArgument(0);
arg.setAddress(new Address());
return;
}
}).when(otherService).synchronize(any(Person.class));

Error testing with Spring Cloud Stream Test

We are using spring-cloud-stream to manage messages between our applications.
We have custom bindings:
public interface InboundChannels {
String TASKS = "domainTasksInboundChannel";
String EVENTS = "eventsInboundChannel";
#Input(TASKS)
SubscribableChannel tasks();
#Input(EVENTS)
SubscribableChannel events();
}
public interface OutboundChannels {
String TASKS = "domainTasksOutboundChannel";
String EVENTS = "eventsOutboundChannel";
#Output(TASKS)
MessageChannel tasks();
#Output(EVENTS)
MessageChannel events();
}
There are processors that consumes tasks and generate events:
#EnableBinding({InboundChannels.class, OutboundChannels.class})
public class TasksProcessor {
public TasksProcessor(
UserService userService,
#Qualifier(OutboundChannels.EVENTS) MessageChannel eventsChannel
) {
this.userService = userService;
this.eventsChannel = eventsChannel;
}
#StreamListener(value = TASKS, condition = "headers['" + TYPE + "']=='" + CREATE_USER + "'")
public void createUser(Message<User> message) {
final User user = message.getPayload();
userService.save(user)
.subscribe(created -> {
Message<User> successMessage = fromMessage(message, Events.USER_CREATED, created).build();
eventsChannel.send(successMessage);
});
}
}
Now we wanted to test it using spring-cloud-stream-test-support and its amazing features:
#DirtiesContext
#SpringBootTest
#RunWith(SpringRunner.class)
public class TasksProcessorTest {
private User user;
#Autowired
private InboundChannels inboundChannels;
#Autowired
private OutboundChannels outboundChannels;
#Autowired
private MessageCollector collector;
#Before
public void setup() {
user = new User(BigInteger.ONE, "test#teste.com");
}
#Test
public void createUserTest() {
final Message<User> msg = create(CREATE_USER, user).build();
outboundChannels.tasks().send(msg);
final Message<?> incomingEvent = collector.forChannel(inboundChannels.events()).poll();
final String type = (String) incomingEvent.getHeaders().get(TYPE);
assertThat(type).isEqualToIgnoringCase(USER_CREATED);
}
}
application.properties
##
# Spring AMQP configuration
##
spring.rabbitmq.host=rabbitmq
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
# Events channels
spring.cloud.stream.bindings.eventsOutboundChannel.destination=events
spring.cloud.stream.bindings.eventsInboundChannel.destination=events
spring.cloud.stream.bindings.domainTasksOutboundChannel.destination=domainTasks
spring.cloud.stream.bindings.domainTasksInboundChannel.destination=domainTasks
spring.cloud.stream.bindings.userTasksInboundChannel.group=domainServiceInstances
spring.cloud.stream.bindings.eventsInboundChannel.group=domainServiceInstances
But then we get this error:
java.lang.IllegalArgumentException: Channel [eventsInboundChannel] was not bound by class org.springframework.cloud.stream.test.binder.TestSupportBinder
What are we doing wrong?
In the .subscribe() you do eventsChannel.send(successMessage);, where that eventsChannel is from the OutboundChannels.EVENTS, but what you try to do in the test-case is like inboundChannels.events(). And it doesn't look like you really bind this channel anywhere.
I'm sure if you would use outboundChannels.events() instead, that would work for you.

Spring + Hibernate + TestNG + Mocking nothing persist, nothing is readed in test

Fighting with TestNG, Spring an Hibernate. I'm writing test for Service class, and it's always failure. But without test class works fine. So App is working, but tests don't want to.
Here is my test class
#Transactional
public class BorrowerServiceTest {
#Mock
BorrowerDAOImpl borrowerDAO;
#InjectMocks
BorrowerService borrowerService;
#BeforeClass
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void persistTest() {
Borrower borrower = new Borrower.BorrowerBuilder().firstName("Lars").lastName("Urlich").adress("LA")
.phoneNumber("900900990").build();
borrowerService.persist(borrower);
List<Borrower> borrowerList = borrowerService.getBorrowerByName("Lars Urlich");
Assert.assertEquals(true, borrower.equals(borrowerList.get(0)));
}
}
My BorrowerService:
#Service("borrowerService")
#Transactional
public class BorrowerService {
#Autowired
private BorrowerDAO borrowerDAO;
public List<Borrower> getBorrowers() {
return borrowerDAO.getBorrowers();
}
public List<Borrower> getBorrowerByName(String name) {
return borrowerDAO.getBorrowerByName(name);
}
public boolean removeBorrower(Borrower borrower) {
return borrowerDAO.removeBorrower(borrower);
}
public boolean persist(Borrower borrower) {
return borrowerDAO.persist(borrower);
}
}
My BorrowerDAOImpl:
#Repository("borrowerDAO")
#Transactional
public class BorrowerDAOImpl extends DAO implements BorrowerDAO {
#Override
public List<Borrower> getBorrowers() {
List<Borrower> borrowerList = null;
Query query = entityManager.createQuery("SELECT B FROM Borrower B");
borrowerList = query.getResultList();
return borrowerList;
}
#Override
public List<Borrower> getBorrowerByName(String name) {
List<Borrower> borrowerList = null;
String[] values = name.split(" ");
Query query = entityManager.createQuery("SELECT B FROM Borrower B WHERE B.firstName LIKE '" + values[0]
+ "' AND B.lastName LIKE '" + values[1] + "'");
borrowerList = query.getResultList();
return borrowerList;
}
#Override
public boolean removeBorrower(Borrower borrower) {
String firstName = borrower.getFirstName();
String lastName = borrower.getLastName();
Query query = entityManager
.createQuery("DELETE Borrower where FIRST_NAME LIKE :FirstName AND LAST_NAME LIKE :LastName");
query.setParameter("FirstName", firstName);
query.setParameter("LastName", lastName);
query.executeUpdate();
return true;
}
#Override
public boolean persist(Borrower borrower) {
entityManager.persist(borrower);
return true;
}
}
and abstract DAO:
#Repository
#Transactional
public abstract class DAO {
#PersistenceContext
protected EntityManager entityManager;
}
Maven returns failure:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.LinkedList.checkElementIndex(LinkedList.java:555)
at java.util.LinkedList.get(LinkedList.java:476)
at com.me.service.test.BorrowerServiceTest.persistTest(BorrowerServiceTest.java:41)
I also had to fight with this. The problem here is that your test runs in it's own transaction, so nothing will be committed during method's execution. Now here is what I did:
public class IntegrationTest extends SomeTestBase
{
#Autowired
private PlatformTransactionManager platformTransactionManager;
private TransactionTemplate transactionTemplate;
#Autowired
private BeanToTest beanToTest;
#Override
#Before
public void setup()
{
super.setup();
this.transactionTemplate = new TransactionTemplate(this.platformTransactionManager);
}
#Test
public void fooTest()
{
// given
// when
boolean result = this.transactionTemplate.execute(new TransactionCallback<Boolean>()
{
#Override
public Boolean doInTransaction(TransactionStatus status)
{
return IntegrationTest.this.beanToTest.foo();
}
});
// then
}
}
This allows you to have methods execute within a separate transaction. Please note that you might declare some variables as final.
Hope that helps.
Check the Spring documentation: it looks your test class should extend AbstractTestNGSpringContextTests.
Use #Commit annotation on the whole test class or even method to persist changes made in the test. For more information https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#commit

Use #Autowired in #Webservice (other solutions found did not work)

We are trying to use autowiring in our webservice, but this doens't seem to work (generates nullPointer). We have been searching for a solution for quite a long time, but did not succeed.
Our webservice:
#WebService(wsdlLocation = "/WEB-INF/wsdl/contract.wsdl", serviceName = "BookingService", targetNamespace = "http://realdolmen.com/", portName = "BookingServicePortType")
public class BookingService extends SpringBeanAutowiringSupport implements BookingServicePortType {
#Autowired
BookingServiceBean bookingServiceBean;
#Autowired
TariffService tariffService;
#Override
public BookingResponse createBooking(#WebParam(name = "bookingInput", targetNamespace = "http://realdolmen.com/", partName = "tariffId") BookingInput input) {
Tariff tariff = tariffService.getTariffById(input.getTariffId());
Booking booking = new Booking.BookingBuilder().withBaggageAllowance(tariff.getFlight().getBaggageAllowance())
.withDayOfDeparture(input.getDayOfDeparture()).withHourOfDeparture(input.getHourOfDeparture()).withTariff(tariff).withDuration(input.getDuration()).createBooking();
bookingServiceBean.createBooking(booking);
BookingResponse bookingResponse = new BookingResponse();
bookingResponse.setBookingId(booking.getId());
bookingResponse.setBaggageAllowance(booking.getBaggageAllowance());
bookingResponse.setDayOfDeparture(createWeirdDateClass(booking.getDayOfDeparture()));
bookingResponse.setDuration(booking.getDuration());
bookingResponse.setHourOfDeparture(booking.getHourOfDeparture());
return bookingResponse;
}
private XMLGregorianCalendar createWeirdDateClass(String lexicalRepresentation) {
try {
return DatatypeFactory.newInstance().newXMLGregorianCalendar(lexicalRepresentation);
} catch (DatatypeConfigurationException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
return null;
}
}
}
our spring service:
#Service
#Transactional
public class BookingServiceBeanImpl implements BookingServiceBean {
#Autowired
BookingDAO bookingDAO;
#Override public void createBooking(Booking booking) {
bookingDAO.createBooking(booking);
}
}
The spring bean can be used in the spring controllers so I don't think there's a problem there..

Resources