Unit testing using jUnit5 and mockito - spring

Hi Im having troubles with my saveStudent method I can't seem to make the test work. What my saveStudent method do is when I add a student it automatically adds the fixed subjects from the database to the student. Can anyone explain to me what Im doing wrong in the testing and how to test this kind of method. Gladly appreciate it. Thank you for the answer!
Here is my TestService and my ServiceImplimentation
#ExtendWith(MockitoExtension.class)
public class StudentServiceTest {
#Mock
private StudentRepository studentRepository;
#Mock
private SubjectRepository subjectRepository;
#InjectMocks
private StudentServiceImpl studentService;
private Student student;
private List<Subject> subject;
// private List<Subject> subjectList;
private Collection<Remark> remarks = new ArrayList<>();
#BeforeEach
public void setup() {
student = new Student(1L, "Martin", 6, remarks);
subject = Arrays.asList(
new Subject(1L, "Math", remarks),
new Subject(2L, "English", remarks));
}
// JUnit test for saveStudent method
#DisplayName("JUnit test for saveStudent method")
#Test
public void givenStudentObject_whenSaveStudent_thenReturnStudentObject() {
given(subjectRepository.findAll()).willReturn(subject);
System.out.println(subject);
given(studentRepository.save(student)).willReturn(student);
System.out.println(studentRepository);
System.out.println(studentService);
Student savedStudent = studentService.saveStudent(student);
System.out.println(savedStudent);
assertThat(savedStudent).isNotNull();
}
// JUnit test for getStudentList method
#DisplayName("JUnit test for getStudentList method")
#Test
public void givenStudentsList_whenGetAllStudents_thenReturnStudentsList() {
Student student1 = new Student(1L, "Edmark", 4, remarks);
given(studentRepository.findAll()).willReturn(Arrays.asList(student, student1));
List<Student> studentList = studentService.findAll();
assertThat(studentList).isNotNull();
assertThat(studentList.size()).isEqualTo(2);
}
// JUnit test for getStudentById method
#DisplayName("JUnit test for getStudentById method")
#Test
public void givenStudentId_whenGetStudentId_thenReturnStudentObject() {
given(studentRepository.findById(1L)).willReturn(Optional.of(student));
Student savedStudent = studentService.findById(student.getStudentId());
assertThat(savedStudent).isNotNull();
}
// JUnit test for updateStudent method
#DisplayName("JUnit test for updateStudent method")
#Test
public void givenStudentObject_whenUpdateStudent_thenReturnUpdatedStudent() {
given(studentRepository.findById(1L)).willReturn(Optional.of(student));
given(studentRepository.save(student)).willReturn(student);
student.setStudentGrade(8);
student.setStudentName("Mark");
Student updatedStudent = studentService.updateStudent(1L, student);
assertThat(updatedStudent.getStudentName()).isEqualTo("Mark");
assertThat(updatedStudent.getStudentGrade()).isEqualTo(8);
}
#DisplayName("JUnit test for deleteStudent method")
#Test
public void givenStudentId_whenDeleteStudent_thenNothing() {
long studentId = 1L;
given(studentRepository.findById(studentId)).willReturn(Optional.of(student));
BDDMockito.willDoNothing().given(studentRepository).deleteById(studentId);
studentService.deleteStudent(studentId);
verify(studentRepository, times(1)).deleteById(studentId);
}
}
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
private SubjectRepository subjectRepository;
private StudentRepository studentRepository;
public StudentServiceImpl(StudentRepository studentRepository) {
this.studentRepository = studentRepository;
}
#Override
public Student saveStudent(Student student) {
Collection<Remark> remarkList = new ArrayList<>();
List<Subject> subjects = subjectRepository.findAll();
for (Subject subject : subjects) {
Remark remark = new Remark();
remark.setSubject(subject);
remark.setStudent(student);
remark.setGrade(0);
remarkList.add(remark);
}
student.setRemarks(remarkList);
return studentRepository.save(student);
}
#Override
public List<Student> findAll() {
return studentRepository.findAll();
}
#Override
public Student findById(Long studentId) {
Optional<Student> student = studentRepository.findById(studentId);
if (!student.isPresent()) {
throw new ResourceNotFoundException("Student not found with id : " + studentId);
}
return studentRepository.findById(studentId).get();
}
#Override
public Student updateStudent(#PathVariable(value = "id") Long studentId, #RequestBody Student student) {
Optional<Student> tempStudent = studentRepository.findById(studentId);
if (!tempStudent.isPresent()) {
throw new ResourceNotFoundException("Student not found with id : " + studentId);
}
Student studentDetails = tempStudent.get();
studentDetails.setStudentName(student.getStudentName());
studentDetails.setStudentGrade(student.getStudentGrade());
studentDetails.getRemarks().addAll(student.getRemarks());
Student updatedStudent = studentRepository.save(studentDetails);
return updatedStudent;
}
#Override
public void deleteStudent(long studentId) {
Optional<Student> student = studentRepository.findById(studentId);
if (!student.isPresent()) {
throw new ResourceNotFoundException("Student not found with id : " + studentId +" or already deleted");
}
studentRepository.deleteById(studentId);
}
}

Related

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));

Take the sum of the query result as separate data

Etity
#Entity
public class DateFMail {
#Id
private double balance;
public DateFMail() {
}
public DateFMail(double balance) {this.balance = balance;}
public DateFMail(DateFMail dateFMail) {
}
public double getBalance() { return balance;}
#Override
public String toString() {
return "DateFMail{" +
"balance=" + balance +
'}';
}
}
Service
public interface DateFMailService {
List<DateFMail> findAll();
}
Impl
#Service
public class DateFMailServiceImpl implements DateFMailService {
#Autowired
private DateFMailRepository mailRepository;
#Override
public List<DateFMail> findAll() {
return mailRepository.findAll();
}
}
Repository
#Repository
public interface DateFMailRepository extends JpaRepository<DateFMail, Long> {
#Query(value = "SELECT SUM(balance) \n" +
" FROM agents", nativeQuery = true)
List<DateFMail> findAll();
}
Mail Seder
#Service
public class EmailDos {
#Autowired
private JavaMailSender mailSender;
private DateFMailRepository mailRepository;
String fileDate1 = new SimpleDateFormat("dd.MM.yyyy").format(new Date());
LocalDate today = LocalDate.now();
String fileDate = (today.minusDays(1)).format(DateTimeFormatter.ofPattern("dd MMM"));
String fileDate2 = (today.minusMonths(1)).format(DateTimeFormatter.ofPattern("MMM"));
public void sendMailSum(String from, String to, String subject, String body, String fileToAttach) throws SQLException {
List<DateFMail> list = new ArrayList<>(mailRepository.findAll());
List<DateFMail> list1 = list.stream()
.map(DateFMail::new)
.collect(Collectors.toList());
System.out.println("sending email...................");
System.out.println(list1);
MimeMessagePreparator preparator = new MimeMessagePreparator() {
public void prepare(MimeMessage mimeMessage) throws Exception {
mimeMessage.setFrom(new InternetAddress(from));
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
mimeMessage.setSubject(subject);
mimeMessage.setText(body);
FileSystemResource file = new FileSystemResource(new File("C:...xlsx"));
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setFrom("SomeAddress#gmail.com");
helper.setTo(InternetAddress.parse("SomeAddress#gmail.com"));
helper.setText("Good day!\nIn attachment payments for " + fileDate + " с 12.00-00.00" + "\nAmount for " + fileDate1 + list1);
helper.addAttachment("...xlsx", file);
mailSender.send(mimeMessage);
System.out.println("email Fab was successfully sent.....");
}
};
try {
mailSender.send(preparator);
} catch (MailException ex) {
System.err.println(ex.getMessage());
}
}
}
Controller
#Component
public class DateFMailController {
#Autowired
private DateFMailService mailService;
public void saveSum() throws IOException {
saveExcel(mailService.findAll(), "....xlsx");
}
private void saveExcel(List<DateFMail> list, String fileName) throws IOException {
Workbook workbook = new XSSFWorkbook();
CreationHelper createHelper = workbook.getCreationHelper();
Sheet sheet = workbook.createSheet("ECards");
sheet.autoSizeColumn(0);
Row header = sheet.createRow(0);
CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
XSSFFont font = ((XSSFWorkbook) workbook).createFont();
font.setFontName("Arial");
font.setFontHeightInPoints((short) 10);
font.setBold(true);
headerStyle.setFont(font);
Cell headerCell = header.createCell(0);
headerCell.setCellValue("Sum");
headerCell.setCellStyle(headerStyle);
CellStyle style = workbook.createCellStyle();
style.setWrapText(true);
int ix_row=2;
for (DateFMail dateFMail : list) {
Row row = sheet.createRow(ix_row);
Cell cell = row.createCell(0);
cell.setCellValue(dateFMail.getBalance());
cell.setCellStyle(style);
ix_row++;
}
FileOutputStream outputStream = new FileOutputStream(fileName);
workbook.write(outputStream);
workbook.close();
}
}
Save Runer
#Component
public class SaveCardsStartupRunner implements ApplicationRunner {
#Autowired
private ECardController eCardController;
private DateFMailController controller;
// #Autowired
// private EmailDos emailDos;
String fileDate1 = new SimpleDateFormat("dd.MM.yyyy").format(new Date());
LocalDate today = LocalDate.now();
String fileDate = (today.minusDays(1)).format(DateTimeFormatter.ofPattern("dd MMM"));
String fileDate2 = (today.minusMonths(1)).format(DateTimeFormatter.ofPattern("MMM"));
#Override
public void run(ApplicationArguments args) throws Exception {
eCardController.saveCards();
controller.saveSum();
}
}
I have corrected my question. I've pasted all the code here that pertains to my question. For starters, I would like to simply output the Query result of the repository to the console. But in the form that I just posted here, I get a NullPointerException error and says that in a part of the code: controller.saveSum (); - controller = null.
Create a PaymentService class which should contain the method getTotalPayment. Inject this class in EmailSend (tip: please change this class name from EmailSend to EmailSender as class names should be noun) class. And then in PaymentService Class you should interact Data Repository class. Call this getTotalPayment method from the EmailSend class.

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

Mockito mocking get methods from the database services

I am trying to mock a getBy() method after adding an element by a mocked service add.
This is what I have:
FeedItem feedItem = feedServiceTested.createFeedItem("Text Test", "Category Test", "Author Test");
Mockito.verify(feedRepository).add(feedItem);
Mockito.verify(feedRepository).findAllByCategory("Category Test");
However I get the following error:
Wanted but not invoked:
feedRepository.findAllByCategory(
"Category Test"
);
-> at ie.cit.adf.services.FeedServiceImplTest.testSearchFeedItemsByCategory(FeedServiceImplTest.java:55)
However, there were other interactions with this mock:
-> at ie.cit.adf.services.FeedServiceImpl.createFeedItem(FeedServiceImpl.java:44)
at ie.cit.adf.services.FeedServiceImplTest.testSearchFeedItemsByCategory(FeedServiceImplTest.java:55)
Any idea how to mock this findAllByCategory()?
Here are the 2 classes:
Repository:
#Secured("ROLE_USER")
public class JdbcFeedRepository implements FeedRepository {
private JdbcTemplate jdbcTemplate;
private FeedItemsMapper feedItemsMapper = new FeedItemsMapper();
public JdbcFeedRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
public FeedItem findById(String feedItemId) {
return jdbcTemplate.queryForObject(
"SELECT ID, TEXT, CATEGORY, AUTHOR FROM FEEDITEMS WHERE ID=?",
feedItemsMapper,
feedItemId
);
}
#Override
public List<FeedItem> findAll() {
return jdbcTemplate.query(
"SELECT ID, TEXT, CATEGORY, AUTHOR FROM FEEDITEMS",
feedItemsMapper
);
}
#Override
public List<FeedItem> findAllByCategory(String category) {
return jdbcTemplate.query(
"SELECT ID, TEXT, CATEGORY, AUTHOR FROM FEEDITEMS WHERE CATEGORY=?",
feedItemsMapper,
category
);
}
#Override
public List<FeedItem> findAllByAuthor(String author) {
return jdbcTemplate.query(
"SELECT ID, TEXT, CATEGORY, AUTHOR FROM FEEDITEMS WHERE AUTHOR=?",
feedItemsMapper,
author
);
}
#Override
public void add(FeedItem feedItem) {
jdbcTemplate.update(
"INSERT INTO FEEDITEMS VALUES(?,?,?,?)",
feedItem.getId(),
feedItem.getText(),
feedItem.getCategory(),
feedItem.getAuthor()
);
}
#Override
public void delete(String feedItemId) {
jdbcTemplate.update("DELETE FROM FEEDITEMS WHERE ID=?", feedItemId);
}
/**
* Returns the name of the currently logged in Author.
*
* #return String
*/
private String getCurrentUser() {
return SecurityContextHolder.getContext().getAuthentication().getName();
}
}
class FeedItemsMapper implements RowMapper<FeedItem> {
#Override
public FeedItem mapRow(ResultSet rs, int rowNum) throws SQLException {
FeedItem feedItem = new FeedItem();
feedItem.setId(rs.getString("ID"));
feedItem.setText(rs.getString("TEXT"));
feedItem.setCategory(rs.getString("CATEGORY"));
feedItem.setAuthor(rs.getString("AUTHOR"));
return feedItem;
}
}
Service:
#Transactional
public class FeedServiceImpl implements FeedService {
private FeedRepository repo;
public FeedServiceImpl(FeedRepository repo) {
this.repo = repo;
}
#Override
public FeedItem get(String feedItemId) {
return repo.findById(feedItemId);
}
#Override
public List<FeedItem> getAllFeedItems() {
return repo.findAll();
}
#Override
public List<FeedItem> getAllFeedItemsByCategory(String category) {
return repo.findAllByCategory(category);
}
#Override
public List<FeedItem> getAuthorFeedItems(String author) {
return repo.findAllByAuthor(author);
}
#Override
public FeedItem createFeedItem(String text, String category, String author) {
FeedItem feedItem = new FeedItem();
feedItem.setText(text);
feedItem.setCategory(category);
feedItem.setAuthor(author);
repo.add(feedItem);
return feedItem;
}
#Override
public void delete(String feedItemId) {
repo.delete(feedItemId);
}
}
It seems your code never calls:
feedRepository.findAllByCategory("Category Test");
But you added a verifier for it. Mockito verify ensures the method is called one time in your test. When this did not happen its complains with an exception.
Your test calls:
feedServiceTested.createFeedItem(...)
Which only calls the following methods on repo:
add(feedItem)
Which is your first verify. So at the moment it seems your code did not use findAllByCategory and so does the verify throws this exception.
Or is there a call in FeedItem to the repo? Then please provide the code for this class too.

Cannot delete entity (JPA & Spring)

What ever I try, I cannot delete a user entity when I call delete() from my userService class. I get an exception java.lang.IllegalArgumentException: Entity must be managed to call remove: com.blackbox.genesis.entities.User#30168a, try merging the detached and try the remove again. I'm obviously doing something wrong - despite merging, but I can't see what. Everything else works fine - I can create and update user entities without any problem.
Regards
My entity class;
#Entity
#Table(uniqueConstraints = {#UniqueConstraint(columnNames = "EMAIL")})
public class User implements Serializable {
#Id
#Column(name="username", length=50)
private String username;
#OneToOne(cascade = {CascadeType.ALL})
private Password password;
private boolean enabled;
private int serial;
private String email;
#Version
private int version;
#ElementCollection(targetClass=Authority.class)
#CollectionTable(name="USER_AUTHORITY")
private List<Authority> authorities;
#OneToMany(mappedBy="user", fetch=FetchType.LAZY, cascade=CascadeType.ALL, ``orphanRemoval=true)
private Set<License> licenses;
private static final long serialVersionUID = 1L;
public User() {
super();
this.authorities = new ArrayList<Authority>();
}
.... getters/setters.
My DAO class;
#Repository
public class UserJpaController {
#PersistenceContext
EntityManager em;
protected static final Logger logger = Logger.getLogger("com.blackbox.genesisng.entities.UsersJpaController");
public void create(User user) throws PreexistingEntityException, Exception {
if (findUser(user.getUsername()) != null) {
throw new PreexistingEntityException("Users " + user + " already exists.");
}
em.persist(user);
em.flush();
}
public void edit(User user) throws NonexistentEntityException, Exception {
user = em.merge(user);
em.flush();
}
public void destroy(String id) throws NonexistentEntityException {
User user = em.find(User.class, id);
user = em.merge(user);
em.remove(user);
}
public List<User> findUserEntities() {
return findUserEntities(true, -1, -1);
}
public List<User> findUserEntities(int maxResults, int firstResult) {
return findUserEntities(false, maxResults, firstResult);
}
private List<User> findUserEntities(boolean all, int maxResults, int firstResult) {
Query q = em.createQuery("select object(o) from User as o");
if (!all) {
q.setMaxResults(maxResults);
q.setFirstResult(firstResult);
}
return q.getResultList();
}
public User findUser(String id) {
return em.find(User.class, id);
}
public int getUserCount() {
Query q = em.createQuery("select count(o) from User as o");
return ((Long) q.getSingleResult()).intValue();
}
public User findUserByEmail(String email) {
Query q = em.createQuery("select Object(o) from User as o where o.email = :email");
q.setParameter("email", email);
List list = q.getResultList();
if (list.size() == 0) {
return null;
}
return (User) list.get(0);
}
public boolean exists(String id) {
try {
em.getReference(User.class,id);
return true;
}
catch (EntityNotFoundException e) {
return false;
}
}
}
and finally, the relevant portion of my service class
#Service
public class UserService {
#Autowired
UserJpaController dao;
#Autowired
LicenseJpaController licenseDao;
#Transactional
public void delete(UserDTO userDTO) {
if (exists(userDTO.getUserName())){
try {
dao.destroy(userDTO.getUserName());
} catch (NonexistentEntityException e) {
// ignore as the previous test should prevent this.
}
}
}
So sorry, but I'm an idiot! I was not calling the service class that I thought I was. Fixed that and everything works as expected. Once again, sorry folks.
Regards
Remove the
user = em.merge(user);
statement in your DAO destroy method. I am not sure if it causes the probem, but it is not needed because the user is loaded in the statement before.

Resources