Issue
I am creating test cases for my controller
package com.bank.controller;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.http.MediaType;
import com.bank.model.Account;
import com.bank.model.Customer;
import com.bank.service.AccountService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@RunWith(SpringRunner.class)
@WebMvcTest(value=AccountController.class)
class AccountControllerTest{
@Autowired
private MockMvc mockMvc;
@MockBean
private AccountService accountService;
@Test
public void testcreateAccount() throws Exception{
Customer customer=new Customer();
customer.setCustomerId(1);
customer.setCustomerName("Manasa");
Account mockAccount=new Account();
mockAccount.setAccountNumber(456);
mockAccount.setBalance(2000.0);
mockAccount.setAccountType("savings");
mockAccount.setCustomer(customer);
String inputInJson=this.mapToJson(mockAccount);
String URI="/account";
Mockito.when(accountService.accountCreate(Mockito.any(Account.class))).thenReturn(mockAccount);
RequestBuilder requestBuilder=MockMvcRequestBuilders
.post(URI)
.accept(MediaType.APPLICATION_JSON).content(inputInJson)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response=result.getResponse();
String outputInJson=response.getContentAsString();
assertThat(outputInJson).isEqualTo(outputInJson);
assertEquals(HttpStatus.OK.value(),response.getStatus());
}
@Test
public void testaccountList() throws Exception{
Customer customer1=new Customer();
customer1.setCustomerId(1);
customer1.setCustomerName("Manasa");
Account mockAccount1=new Account();
mockAccount1.setAccountNumber(456);
mockAccount1.setAccountType("savings");
mockAccount1.setBalance(2000.0);
mockAccount1.setCustomer(customer1);
Customer customer2=new Customer();
customer2.setCustomerId(2);
customer2.setCustomerName("Madhu");
Account mockAccount2=new Account();
mockAccount2.setAccountNumber(789);
mockAccount2.setAccountType("savings");
mockAccount2.setBalance(6000.0);
mockAccount2.setCustomer(customer2);
Customer customer3=new Customer();
customer3.setCustomerId(3);
customer3.setCustomerName("Lalasa");
Account mockAccount3=new Account();
mockAccount3.setAccountNumber(123);
mockAccount3.setAccountType("savings");
mockAccount3.setBalance(50000.0);
mockAccount3.setCustomer((Customer)Arrays.asList(3,"Lalasa"));
List<Account> accountList=new ArrayList<>();
accountList.add(mockAccount1);
accountList.add(mockAccount2);
accountList.add(mockAccount3);
Mockito.when(accountService.allAccounts()).thenReturn(accountList);
String URI="/account/aclist";
RequestBuilder requestBuilder=MockMvcRequestBuilders.get(
URI).accept(MediaType.APPLICATION_JSON);
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
String expectedJson=this.mapToJson(accountList);
String outputInJson=result.getResponse().getContentAsString();
assertThat(outputInJson).isEqualTo(expectedJson);
}
@Test
public void testbyAccountType() throws Exception{
Customer customer=new Customer();
customer.setCustomerId(1);
customer.setCustomerName("Manasa");
Account mockAccount=new Account();
mockAccount.setAccountNumber(456);
mockAccount.setAccountType("savings");
mockAccount.setBalance(2000.0);
mockAccount.setCustomer(customer);
String expectedJson=this.mapToJson(mockAccount);
Mockito.when(accountService.accountByType(Mockito.anyString())).thenReturn((List<Account>) mockAccount);
String URI="/account//byType/savings";
RequestBuilder requestBuilder=MockMvcRequestBuilders.get(
URI).accept(MediaType.APPLICATION_JSON);
MvcResult result=mockMvc.perform(requestBuilder).andReturn();
String outputInJson=result.getResponse().getContentAsString();
assertThat(outputInJson).isEqualTo(expectedJson);
}
private String mapToJson(Object object) throws JsonProcessingException{
ObjectMapper objectMapper=new ObjectMapper();
return objectMapper.writeValueAsString(object);
}
}
My Controller class is
package com.bank.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bank.model.Account;
import com.bank.service.AccountService;
import com.bank.Exception.ResourceNotFoundException;
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
AccountService accountService;
@PostMapping
public ResponseEntity<?> createAccount(@RequestBody Account account) throws ResourceNotFoundException{
Account acc=accountService.accountCreate(account);
if(acc!=null) {
return new ResponseEntity<>(acc,HttpStatus.CREATED);
}else {
throw new ResourceNotFoundException("Account is not Created!!");
}
}
@GetMapping("/aclist")
public ResponseEntity<?> accountList() throws ResourceNotFoundException{
List<Account> accountList=accountService.allAccounts();
if(!(accountList.isEmpty())) {
return new ResponseEntity<>(accountList,HttpStatus.OK);
}else {
throw new ResourceNotFoundException("accounts not found");
}
}
@GetMapping("/byType/{accountType}")
public ResponseEntity<?> byAccountType(@PathVariable String accountType) throws ResourceNotFoundException{
List<Account> account =accountService.accountByType(accountType);
if(account.isEmpty()) {
throw new ResourceNotFoundException(accountType+""+"Type of account does not exist!!");
}else {
return new ResponseEntity<>(account,HttpStatus.OK);
}
}
@GetMapping("/{accountNumber}")
public ResponseEntity<?> getAccountById(@PathVariable("accountNumber") int accountNumber) throws ResourceNotFoundException{
Account acc=accountService.findAccountById(accountNumber);
if(acc!=null) {
return new ResponseEntity<>(acc,HttpStatus.OK);
}else {
throw new ResourceNotFoundException("account [accountNumber="+accountNumber+"] can't be found");
}
}
@PutMapping("/{from}/{to}/{amount}")
public ResponseEntity<?> transferFunds(@PathVariable("from") int from,@PathVariable("to") int to,@PathVariable("amount") double amount) throws ResourceNotFoundException{
return accountService.transferFunds(from, to, amount);
}
@DeleteMapping("/{accountNumber}")
public ResponseEntity<?> deleteAccById(@PathVariable ("accountNumber") int accountNumber) throws ResourceNotFoundException{
String x=accountService.deleteById(accountNumber);
if(x.equalsIgnoreCase("deleted")){
return new ResponseEntity<>("deleted successfully",HttpStatus.OK);
}else {
throw new ResourceNotFoundException("Account [accountNumber="+accountNumber+"] can't be found");
}
}
@GetMapping("/balance/{accountNumber}")
public ResponseEntity<?> getBalanceById(@PathVariable ("accountNumber") int accountNumber) throws ResourceNotFoundException{
String balance=accountService.getBalanceById(accountNumber);
if(balance.equalsIgnoreCase("Invalid accountNumber")) {
throw new ResourceNotFoundException("Account [accountNumber="+accountNumber+"] can't be found");
}else {
return new ResponseEntity<>(balance,HttpStatus.OK);
}
}
@DeleteMapping("/deleteAll")
public ResponseEntity<?> deleteAllAccounts(){
return new ResponseEntity<>(accountService.deleteAllAccounts(),HttpStatus.OK);
}
@PutMapping("/update/{accountNumber}")
public ResponseEntity<?> UpdateAccount(@PathVariable ("accountNumber") int accountNumber,@RequestBody Account account) throws ResourceNotFoundException{
Account acc=accountService.updateAccount(accountNumber, account);
if(acc!=null) {
return new ResponseEntity<>(acc,HttpStatus.OK);
}else {
throw new ResourceNotFoundException("invalid accountNumber and account");
}
}
@PutMapping("/deposite/{amount}/{accountNumber}")
public ResponseEntity<?> deposite(@PathVariable("amount") double amount,@PathVariable("accountNumber") int accountNumber) throws ResourceNotFoundException{
return accountService.deposite(amount,accountNumber);
}
@PutMapping("/withdraw/{amount}/{accountNumber}")
public ResponseEntity<?> withDraw(@PathVariable("amount") double amount,@PathVariable("accountNumber") int accountNumber) throws ResourceNotFoundException{
return accountService.withdraw(amount,accountNumber);
}
}
My model class is (has many to one relations with customer class)
package com.bank.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Proxy;
@Entity
@Table(name="Account")
@Proxy(lazy = false)
public class Account {
@Id
int accountNumber;
@Column(name="balance")
Double balance;
@Column(name="accountType")
String accountType;
@ManyToOne( targetEntity=Customer.class,cascade = CascadeType.ALL)
@JoinColumn(name="ca_fk",referencedColumnName = "customerId")
Customer customer;
public Account() {
super();
}
public Account(int accountNumber, Double balance, String accountType, Customer customer) {
super();
this.accountNumber = accountNumber;
this.balance = balance;
this.accountType = accountType;
this.customer = customer;
}
@Override
public String toString() {
return "Account [accountNumber=" + accountNumber + ", balance=" + balance + ", accountType=" + accountType
+ ", customer=" + customer + "]";
}
public Integer getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(int accountNumber) {
this.accountNumber = accountNumber;
}
public Double getBalance() {
return balance;
}
public void setBalance(Double balance) {
this.balance = balance;
}
public String getAccountType() {
return accountType;
}
public void setAccountType(String accountType) {
this.accountType = accountType;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
customer class
package com.bank.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.Proxy;
@Entity
@Table(name="Customer")
@Proxy(lazy = false)
public class Customer {
@Id
Integer customerId;
@Column(name="customerName")
String customerName;
public Customer() {
super();
}
public Customer(int customerId, String customerName) {
super();
this.customerId = customerId;
this.customerName = customerName;
}
@Override
public String toString() {
return "Customer [customerId=" + customerId + ", customerName=" + customerName + "]";
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
}
I got the below error
MockHttpServletRequest:
HTTP Method = POST
Request URI = /account
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"114"]
Body = {"accountNumber":456,"balance":2000.0,"accountType":"savings","customer":{"customerId":1,"customerName":"Manasa"}}
Session Attrs = {}
Handler:
Type = com.bank.controller.AccountController
Method = com.bank.controller.AccountController#createAccount(Account)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"accountNumber":456,"balance":2000.0,"accountType":"savings","customer":{"customerId":1,"customerName":"Manasa"}}
Forwarded URL = null
Redirected URL = null
Cookies = []
2022-03-20 02:11:12.422 WARN 12996 --- [ main] o.s.test.context.TestContextManager : Caught exception while invoking 'afterTestMethod' callback on TestExecutionListener [org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@485e36bc] for test method [public void com.bank.controller.AccountControllerTest.testbyAccountType() throws java.lang.Exception] and test instance [com.bank.controller.AccountControllerTest@153cfd86]
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.bank.controller.AccountControllerTest.testbyAccountType(AccountControllerTest.java:139)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3. you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.resetMocks(ResetMocksTestExecutionListener.java:83) ~[spring-boot-test-2.6.4.jar:2.6.4]
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.resetMocks(ResetMocksTestExecutionListener.java:70) ~[spring-boot-test-2.6.4.jar:2.6.4]
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.afterTestMethod(ResetMocksTestExecutionListener.java:64) ~[spring-boot-test-2.6.4.jar:2.6.4]
at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:445) ~[spring-test-5.3.16.jar:5.3.16]
at org.springframework.test.context.junit.jupiter.SpringExtension.afterEach(SpringExtension.java:206) ~[spring-test-5.3.16.jar:5.3.16]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAfterEachCallbacks$12(TestMethodTestDescriptor.java:257) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAllAfterMethodsOrCallbacks$13(TestMethodTestDescriptor.java:273) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.8.2.jar:1.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAllAfterMethodsOrCallbacks$14(TestMethodTestDescriptor.java:273) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ~[na:na]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeAllAfterMethodsOrCallbacks(TestMethodTestDescriptor.java:272) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeAfterEachCallbacks(TestMethodTestDescriptor.java:256) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:141) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66) ~[junit-jupiter-engine-5.8.2.jar:5.8.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) ~[junit-platform-engine-1.8.2.jar:1.8.2]
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.8.2.jar:1.8.2]
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.8.2.jar:1.8.2]
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.8.2.jar:1.8.2]
My pom.xml is
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bank</groupId>
<artifactId>capstoneBankProject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>capstoneBankProject</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<junit.version>4.13.1</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
<!-- exclude junit 4 -->
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
my project structure
Solution
You are casting mockAccount to a list without it actually being a list, that's probably why the mock fails.
Answered By - Johan Nordlinder
Answer Checked By - Timothy Miller (JavaFixing Admin)