Issue
I have a interface CalculatorService
whose implementation contains business logic.
Interface:
public interface CalculatorService {
LoanWeb calculateSimpleLoan(LoanWeb loanWeb) throws Exception;
}
Implementation:
@Service
@AllArgsConstructor
public class CalculatorServiceImpl implements CalculatorService {
private final LoanRepository loanRepository; //this extends JpaRepository
private final PropertiesConfig propertiesConfig;
private final CalculatorUtility calculatorUtility;
private final LoanInfoRepository loanInfoRepository;
@Override
public LoanWeb calculateSimpleLoan(LoanWeb loanWeb) throws Exception {
//validation
if (loanWeb.getLoanTerm() == null || loanWeb.getLoanTerm() <= 0) throw new LeanPayException(ErrorCode.INVALID_INTEGER_ERROR.code, List.of("loanTerm"));
var payments = someCalculation();
Loan loan = loanRepository.findFirstByAmountAndRateAndPaymentsAndFrequency
(loanWeb.getLoanAmount(), loanWeb.getInterestRate(), payments, Frequency.MONTHLY); //loan is null???!!!
if (loan != null) return LoanWeb.builder() //this is false, I need it to be true!
.monthlyPayment(loan.getPmt())
.totalInterestPaid(loan.getLoanInfos().stream().mapToDouble(LoanInfo::getInterest).sum()).build();
}
}
JpaRepository:
@Repository
public interface LoanRepository extends JpaRepository<Loan, Long> {
Loan findFirstByAmountAndRateAndPaymentsAndFrequency(Double amount, Double rate, Integer payments, Frequency frequency);
}
Test class:
It looks like this:
@SpringBootTest(properties = {"spring.cloud.config.enabled: false", "logging.level.com.package.calculator: OFF"},
classes= {ObjectMapper.class, CalculatorServiceImpl.class, LoanRepository.class})
public class CalculatorServiceTest {
@Autowired private ObjectMapper objectMapper;
@Autowired
private CalculatorService calculatorService;
@MockBean(classes = LoanRepository.class) private LoanRepository loanRepository;
@MockBean private PropertiesConfig propertiesConfig;
@MockBean private CalculatorUtility calculatorUtility;
@MockBean private LoanInfoRepository loanInfoRepository;
@MockBean private TestService testService;
private Loan loan1; //this is not null, it is correctly loaded from a file.
@PostConstruct
public void init() throws IOException{
String jsonString =
IOUtils.toString(
Objects.requireNonNull(this.getClass().getResourceAsStream("/json/PreExistingLoan.json")), StandardCharsets.UTF_8);
this.loan1 = objectMapper.readValue(jsonString, Loan.class);
}
@Test
void calculateSimpleLoanWithPreExistingLoan() throws LeanPayException {
Mockito.doReturn(this.loan1).when(loanRepository).findFirstByAmountAndRateAndPaymentsAndFrequency( ArgumentMatchers.anyDouble(), ArgumentMatchers.anyDouble(),
ArgumentMatchers.anyInt(), ArgumentMatchers.any(Frequency.class));
LoanWeb result = this.calculatorService.calculateSimpleLoan(LoanWeb.builder().loanTerm(10).simpleLoanTerm(SimpleLoanTerm.MONTH).build());
Assertions.assertEquals(result.getTotalInterestPaid(), 10);
Assertions.assertEquals(result.getMonthlyPayment(), 107.0);
}
}
Application context is brought up, test runs, but mocked repo method call returns null in service. Where am I wrong? I tried million things, this is code from beginning, don't have any ideas.
Solution
ArgumentMatchers.anyDouble() matches any double (primitive) or non-null Double (boxed) value.
If you happen to pass a null value, it won't be matched.
You have a couple of options:
- use
ArgumentMatchers.any()
which matches anything, including nulls - use
ArgumentMatchers.eq(null)
on the argument where the null is passed - modify your test to pass a non-null value
Answered By - Lesiak
Answer Checked By - Mildred Charles (JavaFixing Admin)