2

I am trying to test my method which uses scanner object and dynamic input from keyboard by user. I could not figure out a way to test it. The thing is that the method which I want to use is also used in another method's loop. I think that Mockito can come in handy in this particular situation, unfortunately, I could not find out the way of how to test it. If that particular method is trying to return wrong value which would throw an exception. Well, code will provide deeper explanation (I hope).

/**
 * This method asks user to insert cash
 * It gets the property text from properties file by key insert.cash
 * Checks if coin is in proper format
 * Checks if coin exists in available coins array
 * Otherwise throws exception
 * @throws InvalidCoinException
 * @return cash - how much money user inserted
 */
@Override
public double insertCash() throws InvalidCoinException {
    double cash = 0;
    double temp; // temporary variable which goes through all if statems if all conditions are satisfied it gets assigned to cash variable
    boolean coinExists = false;
    System.out.println(prop.getProperty("insert.cash"));

    if(!sc.hasNextDouble()) {
        throw new InvalidCoinException(MessageFormat.format(prop.getProperty("invalid.coin"), sc.next()));
    }

    else {

        temp = sc.nextDouble();

        for(int i = 0; i < availableCoins.length; i++) {
            if(temp == availableCoins[i] || temp == 0) {
                coinExists = true;
            }
        }


        if(coinExists == true) {
            cash = temp;
        }

        else {
            throw new InvalidCoinException(MessageFormat.format(prop.getProperty("invalid.coin"), temp));
        }

    }

    return cash;

}

JUNIT:

@org.junit.Before
public void initializeTest() throws IOException {
    machine = new CoffeeMachineImplementation();
    machineSpy = Mockito.spy(CoffeeMachineImplementation.class);
}

@org.junit.Test
public void testInsertCash() throws InvalidCoinException {  
    System.out.println("---------------- Insert cash -----------------");

    Double input2 = 0.06;
    Double input3 = 200.0;
    Double input4 = 0.02;



    try {
        when(machineSpy.insertCash()).thenReturn(input2)
                                     .thenThrow(new InvalidCoinException("..."));
        fail("Should have thrown an exception " + input2);
    }

    catch (InvalidCoinException e) {
        String exception = "Invalid coin: " + input2 + " euro is not existing coin";
        System.out.println(e.getMessage());
        assertEquals(exception, e.getMessage());
    }

My scanner object is declared in constructor (because not only one method uses scanner):

public CoffeeMachineImplementation() throws IOException {
        prop = new Properties();
        input = new FileInputStream("src/localization.properties");
        maWaterCap = 5;
        maCoffeeCap = 3;
        prop.load(input);
        sc = new Scanner(System.in);
    }

1 Answer 1

1

There seems to be no point in that case...

I would change the class like following:

a) To enable scanner mock injection

b) mock the exception message creation to avoid invoking any outside resources

Impl Class

public CoffeeMachineImplementation() throws IOException {
        maWaterCap = 5;
        maCoffeeCap = 3;

        setScannerInstance(new Scanner(System.in));
    }

void setScannerInstance(Scanner s){
   this.sc = sc;
}

String getExceptionMessage(String propKey, Double value){
   return MessageFormat.format(prop.getProperty(propKey), value);
}

Properties getProperties(){
    if(prop == null){
       prop = new Properties();
       input = new FileInputStream("src/localization.properties");
       prop.load(input);
    }

    return prop;
}


public double insertCash() throws InvalidCoinException {
    double cash = 0;
    double temp; // temporary variable which goes through all if statems if all conditions are satisfied it gets assigned to cash variable
    boolean coinExists = false;
    System.out.println(getProperties().getProperty("insert.cash"));

    if(!sc.hasNextDouble()) {
        throw new InvalidCoinException(getExceptionMessage("invalid.coin", sc.next()));
    }

Test Class

As Scanner is a final class.. PowerMockito should be used (example)

@RunWith(PowerMockRunner.class)
@PrepareForTest(Scanner.class)
class CoffeeMachineImplementationTest{

@org.junit.Before
public void initializeTest() throws IOException {
    machine = new CoffeeMachineImplementation();
    machineSpy = Mockito.spy(CoffeeMachineImplementation.class);
    doReturn(new Properties()).when(machineSpy).getProperties();
}

@org.junit.Test
public void shouldThrowException_whenNoNextDouble() throws InvalidCoinException {  

    Double input = 0.06;
    String expectedMessage = "expectedMessage";

    Scanner scMock = PowerMock.createMock(Scanner.class)
    machineSpy.setScannerInstance(scMock);

    when(scMock.hasNextDouble()).thenReturn(false);
    when(scMock.next()).thenReturn(input);

    doReturn(expectedMessage).when(machineSpy)
        .getExceptionMessage("invalid.coin", input);   

    try {
        machineSpy.insertCash();
    }

    catch (InvalidCoinException e) {
        assertEquals(expectedMessage, e.getMessage());
    }

The bottom line is that you should try to mock the scanner class in various test situations and then invoke the insertCash() normally.. and expect certain behavior.

Sign up to request clarification or add additional context in comments.

2 Comments

im suspecting its about the properties loading in the constructor.. check out my update
Well i fixed it. Anyways thanks for help. Mostly it was about that scanner setter injection that method helped a lot :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.