<div class="page"> <div class="cover text-center"> <img class="mx-auto" src=/itb/images/logo_mislata.png alt="logo"> # BankAccountServiceTest amb Mockito <div class="text-end fit-content ms-auto my-3 mt-auto pt-3"> <p><strong>Autor:</strong> Joan Puigcerver Ibáñez</p> <p><strong>Correu electrònic:</strong> j.puigcerveribanez@edu.gva.es</p> <p><strong>Curs:</strong> 2024/2025</p> </div> <div> <p class="fw-bold mb-0">Llicència: BY-NC-SA</p> <p class="d-none d-md-block">(Reconeixement - No Comercial - Compartir Igual)</p> <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ca" target="_blank"> <img class="mx-auto" src="/itb/images/license.png" alt="Licence"/> </a> </div><!--license--> </div><!--cover--> </div><!--page--> {:toc} ## Objectius L'objectiu principal d'aquest exercici és posar en pràctica la creació de tests unitaris utilitzant __objectes simulats__ amb la llibreria __Mockito__ en una aplicació amb arquitectura per capes. ## Enunciat Se'ns ha demanat crear una aplicació bancària per gestionar els comptes bancaris dels clients. La nostra tasca consistirà en provar la funcionalitat de la classe `BankAccountServiceImpl` utilitzant tests unitaris i objectes simulats utilitzant __Mockito__. Les regles de negoci són les següents: - Els comptes bancaris han de ser retornat ordenats pel número de compte bancari. - La capa de serveis (`BankAccountService`) és la responsable de llançar les excepcions quan la capa de persistència (`BankAccountRepository`) no pot trobar un compte bancari. - El nom del titular del compte bancari ha de tenir com a mínim 3 caràcters. - Quan s'actualitza el titular del compte bancari, s'ha de notificar al client. ## Codi Font ### Class [`BankAccount`](/DAM-ED/files/ud5/practices/bank/BankAccount.java) ```java package ud5.practices.bank; public class BankAccount { private final String accountNumber; private String accountHolder; private double balance; public BankAccount(String accountNumber, String accountHolder) { this(accountNumber, accountHolder, 0.0); } public BankAccount(String accountNumber, String accountHolder, double initialBalance) { this.accountNumber = accountNumber; this.accountHolder = accountHolder; this.balance = initialBalance; } public String getAccountNumber() { return accountNumber; } public String getAccountHolder() { return accountHolder; } public void setAccountHolder(String accountHolder) { this.accountHolder = accountHolder; } public double getBalance() { return balance; } public boolean deposit(double amount) { if (amount > 0) { balance += amount; return true; } return false; } public boolean withdraw(double amount) { if (amount > 0 && amount <= balance) { balance -= amount; return true; } return false; } public boolean transfer(BankAccount destination, double amount) { boolean success = withdraw(amount); if (success) { destination.deposit(amount); } return success; } } ``` ### Interface [`BankAccountService`](/DAM-ED/files/ud5/practices/bank/service/BankAccountService.java) ```java package ud5.practices.bank.service; import ud5.practices.bank.BankAccount; import ud5.practices.bank.exception.BankAccountNotFoundException; import ud5.practices.bank.exception.InvalidAccountHolderNameException; import java.util.List; public interface BankAccountService { /** * Get all bank accounts * @return a list with all bank accounts */ List<BankAccount> getAll(); /** * Get a bank account by its account number * @param accountNumber the account number * @return the bank account * @throws BankAccountNotFoundException if does not exist a bank account with the given account number */ BankAccount getBankAccountByNumber(String accountNumber) throws BankAccountNotFoundException; /** * Update the account holder name of a bank account * @param bankAccount the bank account * @param accountHolder the new account holder name * @throws InvalidAccountHolderNameException if the account holder name is invalid (less than 3 characters) */ void updateAccountHolder(BankAccount bankAccount, String accountHolder) throws InvalidAccountHolderNameException; } ``` ### Interface [`BankAccountRepository`](/DAM-ED/files/ud5/practices/bank/service/BankAccountRepository.java) ```java package ud5.practices.bank.repository; import ud5.practices.bank.BankAccount; import java.util.List; public interface BankAccountRepository { /** * Get all bank accounts * @return a list with all bank accounts */ List<BankAccount> getAll(); /** * Get a bank account by its account number * @param accontNumber the account number * @return the bank account; null if does not exist a bank account with the given account number */ BankAccount getBankAccountByNumber(String accontNumber); /** * Update the account holder name of a bank account * @param bankAccount the bank account */ void updateAccountHolder(BankAccount bankAccount); } ``` ### Interface [`NotificationService`](/DAM-ED/files/ud5/practices/bank/service/NotificationService.java) ```java package ud5.practices.bank.service; import ud5.practices.bank.BankAccount; public interface NotificationService { /** * Notify the change of the account holder name of a bank account * @param bankAccount the bank account */ void notifyUpdateAccountHolderChange(BankAccount bankAccount); } ``` ### Exception [`BankAccountNotFoundException`](/DAM-ED/files/ud5/practices/bank/exception/BankAccountNotFoundException.java) ```java package ud5.practices.bank.exception; public class BankAccountNotFoundException extends Exception { public BankAccountNotFoundException(String message){ super(message); } } ``` ### Exception [`InvalidAccountHolderNameException`](/DAM-ED/files/ud5/practices/bank/exception/InvalidAccountHolderNameException.java) ```java package ud5.practices.bank.exception; public class InvalidAccountHolderNameException extends Exception { public InvalidAccountHolderNameException(String message){ super(message); } } ``` ### Class [`BankAccountServiceImpl`](/DAM-ED/files/ud5/practices/bank/service/BankAccountServiceImpl.java) ```java package ud5.practices.bank.service.impl; import ud5.practices.bank.BankAccount; import ud5.practices.bank.exception.BankAccountNotFoundException; import ud5.practices.bank.exception.InvalidAccountHolderNameException; import ud5.practices.bank.repository.BankAccountRepository; import ud5.practices.bank.service.BankAccountService; import ud5.practices.bank.service.NotificationService; import java.util.Comparator; import java.util.List; public class BankAccountServiceImpl implements BankAccountService { private final BankAccountRepository bankAccountRepository; private final NotificationService notificationService; public BankAccountServiceImpl(BankAccountRepository bankAccountRepository, NotificationService notificationService) { this.bankAccountRepository = bankAccountRepository; this.notificationService = notificationService; } public List<BankAccount> getAll() { List<BankAccount> bankAccounts = bankAccountRepository.getAll(); bankAccounts.sort(Comparator.comparing(BankAccount::getAccountNumber)); return bankAccounts; } public BankAccount getBankAccountByNumber(String accountNumber) throws BankAccountNotFoundException { BankAccount bankAccount = bankAccountRepository.getBankAccountByNumber(accountNumber); if (bankAccount == null) { throw new BankAccountNotFoundException("Invalid account number: " + accountNumber); } return bankAccount; } public void updateAccountHolder(BankAccount bankAccount, String accountHolder) throws InvalidAccountHolderNameException { if (bankAccount.getAccountHolder().length() < 3) { throw new InvalidAccountHolderNameException("Holder name must be at least 3 characters long"); } bankAccount.setAccountHolder(accountHolder); notificationService.notifyUpdateAccountHolderChange(bankAccount); bankAccountRepository.updateAccountHolder(bankAccount); } } ```