Amadou Beye
Amadou Beye

Reputation: 2828

Testing interface or class?

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

Answers (1)

Lorelorelore
Lorelorelore

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

Related Questions