Reputation: 2828
I'm new to java and I'm confused about testing code with JUnit and Mockito.
I have forked a project on github where the task is to write Junit testing for
AccountService
which is an interface. What I don't know is what do I have to test ? Interface or class that implement interface ?
Here is AccountService
:
public interface AccountService {
public Operation deposit(String accountNumber, int amount);
public Operation withdraw(String accountNumber, int amount);
public OperationsDto history(String accountNumber);
}
Here is AccountService
implementation
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountRepository accountRepository;
@Autowired
private OperationRepository operationRepository;
@Autowired
OperationConverter operationConverter;
public Operation deposit(String accountNumber, int amount) {
checkAmount (amount);
AccountEntity account = getAccount (accountNumber);
int balance = account.getBalance();
balance = balance + amount;
OperationEntity operation = new OperationEntity();
operation.setAccount(accountNumber);
operation.setAmount(amount);
operation.setType(OperationType.DEPOSIT.toString());
operation.setDate(DateUtil.getCurrentDate());
operation.setBalance(balance);
operationRepository.save(operation);
//update account balance
account.setBalance(balance);
accountRepository.save(account);
return operationConverter.convert(operation);
}
public Operation withdraw(String accountNumber, int amount) {
checkAmount (amount);
AccountEntity account = getAccount (accountNumber);
int balance = account.getBalance();
balance = balance - amount;
if(balance < 0){
throw new OperationException(Constants.ERROR_INVALID_OPERATION);
}
OperationEntity operation = new OperationEntity();
operation.setBalance(balance);
operation.setAccount(accountNumber);
operation.setAmount(amount);
operation.setType(OperationType.WITHDRAW.toString());
operation.setDate(DateUtil.getCurrentDate());
operationRepository.save(operation);
//update account balance
account.setBalance(balance);
accountRepository.save(account);
return operationConverter.convert(operation);
}
public OperationsDto history(String accountNumber) {
List<OperationEntity> operations =
operationRepository.findByAccount(accountNumber);
return operationConverter.convertList(operations);
}
private AccountEntity getAccount(String accountNumber){
AccountEntity account =
accountRepository.findByNumber(accountNumber);
if(account == null) {
throw new AccountException(Constants.ERROR_INVALID_ACCOUNT);
}
return account;
}
private void checkAmount (int amount) {
if(amount < 0){
throw new OperationException(Constants.ERROR_INVALID_OPERATION);
}
}
}
Upvotes: 1
Views: 2007
Reputation: 3393
You should test the implementation, hence, the class.
The interface should be used only for injections. So, looking to the code you have posted, you should test the AccountServiceImpl
class in a AccountServiceImplTest
class (or something similar).
In this Unit test you should mock AccountRepository
, OperationRepository
and OperationConverter
. In a test unit you should test only the class which is interested in test, not its dependencies. So, You inject the interfaces using @Autowired
. In this way using a mock framework you can mock your class dependencies, isolating the unit you need the test.
Look at the class you posted: you are injecting three beans using an annotation. I'm pretty sure that those types are all interfaces. When you deploy the application, the Spring CDI will take care of injecting the actual implementations right after starting the container (or right running Spring Boot). When you are running this class, you want to isolate it. The test should be as atomic as possible. Using Mockito along with the correct JUnit runner, you will provide a fake implementation for the injected beans. Using Mockito API you can tell how this fake implementation should answer to a certain request. So you take the implementation of AccountServiceImpl
, mock all its dependencies and than you test only the logic contained in that class. You do not test an interface, because an interface represents only the contract for calling a service, not its implementation.
@RunWith(MockitoJUnitRunner.class)
public class AccountServiceTest {
@Mock
private AccountRepository accountRepository;
@Mock
private OperationRepository operationRepository;
@Mock
OperationConverter operationConverter;
@InjectMocks
private AccountServiceImpl accountService;
@Before
public void setUp() {
/* here goes before-class init logic */
}
@Test
public void testMethod1() {
/* here you test a method*/
}
@Test
public void testMethod2() {
/* here you test another method*/
}
}
This is how your test class should appear.
Upvotes: 6