0

I am trying to find out how can I check what system printed out at some point of iteration during the while loop statement.

I have this method:

/**
 * This method counts how much money user inserted
 * If it is not enough to buy chosen cup size it asks user to insert more
 * Otherwise it checks if user does not need the change
 * and collects money to machine based on cup size price
 * This method gets the message from properties file by key identifies
 * add.more.money - if not enough money is in machine
 * user.paid.change - if user put too much to machine
 * user.paid - if user paid the exact amount of money
 * @param constantPrice  - cup size price
 * @return countMoney - how much user put to machine
 */
@Override
public String countMoneyForCupSize(double constantPrice) {
    // constant - price depending on cup size
    String countMoney = " ";
    double sessionMoney = 0;
    boolean flag = true;

    while(flag) {
        if(constantPrice > sessionMoney) {
            System.out.println(MessageFormat.format(prop.getProperty("add.more.money"), (constantPrice-sessionMoney)));
            double insertedCash;
            try {
                insertedCash = insertCash();
                sessionMoney = sessionMoney + insertedCash;
                if(insertedCash == 0) {
                    System.out.println(MessageFormat.format(prop.getProperty("end.procedure"), sessionMoney));
                    flag = false;
                }
            } catch (InvalidCoinException e) {
                System.out.println(e.getMessage());
            }       
        }

        else {

            double change = sessionMoney - constantPrice;
            String makeCoffeeText = makeCoffee();   
            if(change > 0) {
                countMoney = MessageFormat.format(prop.getProperty("user.paid.change"), makeCoffeeText, sessionMoney, change);
            }
            else {
                countMoney = MessageFormat.format(prop.getProperty("user.paid"), makeCoffeeText, sessionMoney);
            }

            collectMoney(constantPrice);
            flag = false;
        }
    }
    return countMoney;
}   

JUnit:

@org.junit.Rule
public final ProvideSystemProperty property1 = new ProvideSystemProperty("MoreEuro", "You need more: 0.5 euro");

@org.junit.Test
public void testCountMoneyForCupSize() {
    System.out.println("---------------- Count Money For Cup Size -----------------");
    double largePrice = 1.5;
    double mediumPrice = 1;
    double smallPrice = 0.75;

    try {
        when(machineSpy.insertCash()).thenReturn(1.0);
        assertEquals(machineSpy.countMoneyForCupSize(largePrice), System.getProperty("MoreEuro"));

    } catch (InvalidCoinException e) {
        System.out.println(e.getMessage());
    }
} 

So I want to be clear that If user inserted 1 euro he still needs to insert half a euro more.

default locale
  • 13,035
  • 13
  • 56
  • 62
user2204367
  • 145
  • 3
  • 3
  • 16

2 Answers2

3

1) when(machineSpy.insertCash()).thenReturn(1.0);

Mocking private method invocation called by the tested public method is not necessarily a good thing as you should mock dependency and not the behavior of the object under test. If insertCash() has some specificities that have to be mocked, you should probably move out in another class.

2) About :

how to break loop at some point of iteration and check what system printed out

Why not just executing a break instruction ?

3) machineSpy.countMoneyForCupSize(largePrice);

So I want to be clear that If user inserted 1 euro. He still need to insert half euro more

Rather than trying to test the output written in the console that is not really testing the method behavior, I think that you should change the contract of countMoneyForCupSize(double).

In your actual version the method returns a formatted String. It should not.
Logic and rendering tasks are two distinct things.
Mixing both responsibilities violates the Single Responsibility principle (more than one reason to change a class). Besides, it may prevent from testing the core logic of the method as in your case

Here :

if(change > 0) {
    countMoney = MessageFormat.format(prop.getProperty("user.paid.change"), makeCoffeeText, sessionMoney, change);
}
else {
    countMoney = MessageFormat.format(prop.getProperty("user.paid"), makeCoffeeText, sessionMoney);
}

You should rather create an instance of a custom class that contains all these data and return it rather than a String.

For example :

....
CountMoney countMoney = null;
 ...
if(change > 0) {
    countMoney = new CountMoney(makeCoffeeText, sessionMoney, change);
}
else {
    countMoney = new CountMoney(makeCoffeeText, sessionMoney);
}

...
return countMoney;

In your applicative code that invokes CountMoney countMoneyForCupSize() you could then call a rendered method (located in a rendering class) that performs the rendering, for example : String renderingCountMoney(CountMoney countMoney).

In this way your design would be better (each class and each method has well defined responsibilities) and you can unit test countMoneyForCupSize() without considerations about user text rendering.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
2

You can check directly that insertCash was called multiple times. One way to do this is to to mock multiple calls to insertCash with thenReturn:

when(machineSpy.insertCash()).thenReturn(1.0, 0.5);

After that you can verify that insertCash was called exactly two times:

verify(machineSpy, times(2)).insertCash();
verifyNoMoreInteractions(machineSpy);

Check out this question for other options:

If you still want to capture and test the output to System.out then you need to redirect standard output. This was already covered in earlier questions:

default locale
  • 13,035
  • 13
  • 56
  • 62
  • But what about breaking a loop when(machineSpy.insertCash()).thenReturn(1.0); If I only inserted 1 is it possible to somehow break that loop cause now the loop will insert 1.0 two times – user2204367 Feb 27 '17 at 10:03
  • @user2204367 well, that's an expected behavior, isn't it? As it's currently implemented, your code will call `insertCash` two times, receive 2.0 and proceed to return change. Is this correct? If you need to insert another value, then pass it to `thenReturn` – default locale Feb 27 '17 at 10:17
  • 1
    Well u see the thing is that I want to test also if numbers are counted correctly. If I pass argument 1 I get 0.5 to insert more. Maybe i need then extract separate method like countSessionMoney and return double but with each iteration of while loop statement I hope to get the exact number of money to insert more – user2204367 Feb 27 '17 at 11:51