kk1957
kk1957

Reputation: 8824

Mockito mock a method with infinite loop

I have a method as follows

public class ClientClass {

    public void clientMethod() {
        while(true){
           doSomethings.....
       }
    }
}

I am trying to test using mockito. I am able to make the call to clientMethod, but since there is a while(true) inside clientMethod, the call never returns and I never reach to my assert statements which (of course) occur after clientMethod() invocation. Is there a way to stop the loop after one loop iteration from my test case?

Upvotes: 5

Views: 7943

Answers (3)

tensor
tensor

Reputation: 783

I got stuck in this because I was calling same method from inside the method by mistake.

public OrderEntity createNewOrder(NewDepositRequest request, String userId) {
    return createNewOrder(request, userId);
}

Upvotes: 0

mike rodent
mike rodent

Reputation: 15623

This was a source of a little frustration to me... because I like to start off the most sophisticated of GUI apps with a console handler.

The language I'm using here is Groovy, which is a sort of marvellous extension of Java, and which can be sort of mixed in with plain old Java.

class ConsoleHandler {

    def loopCount = 0
    def maxLoopCount = 100000

    void loop() {
        while( ! endConditionMet() ){
            // ... do something
        }
    }

    boolean endConditionMet() {
        loopCount++
        loopCount > maxLoopCount // NB no "return" needed!
    }

    static void main( args ) {
        new ConsoleHandler().loop()
    }
}

... in a testing class (also in Groovy) you can then go

import org.junit.contrib.java.lang.system.SystemOutRule
import org.junit.contrib.java.lang.system.
    TextFromStandardInputStream.emptyStandardInputStream
import static org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import static org.mockito.Mockito.*

class XXTests {
    @Rule
    public SystemOutRule systemOutRule = new SystemOutRule().enableLog()
    @Rule
    public TextFromStandardInputStream systemInMock = emptyStandardInputStream()
    ConsoleHandler spyConsoleHandler = spy(new ConsoleHandler())

    @Test
    void readInShouldFollowedByAnother()  {
        spyConsoleHandler.setMaxLoopCount 10
        systemInMock.provideLines( 'blah', 'boggle')
        spyConsoleHandler.loop()
        assertThat( systemOutRule.getLog() ).containsIgnoringCase( 'blah' )
        assertThat( systemOutRule.getLog() ).containsIgnoringCase( 'boggle' )

The beautiful thing that's happening here is that simply by declaring maxLoopCount the language automatically creates two methods: getMaxLoopCount and setMaxLoopCount (and you don't even have to bother with brackets).

Of course the next test would be "loop must exit if a user enters Q" or whatever... but the point about TDD is that you want this to FAIL initially!

The above can be replicated using plain old Java, if you must: you have to create your own setXXX method of course.

Upvotes: 0

Jk1
Jk1

Reputation: 11443

Technicaly you can't break the infinite loop in test without throwing an exception from inside it. If there is something inside the loop you can mock, then it may produce an exception for you.

When you're finding yourself in situation like this, when awkward workarounds are necessary for testing, then it's time to stop and think about the design. Non-testable code is generaly ill-maintainable and not very self-explanatory. So my advice would be to get rid of infinite loop and introduce an appropriate loop condition. After all, no application will live forever.

If you're still convinced that endless loop is the best way to go here, then you can perform a slight decomposition to make things more testable:

public class ClientClass {

  // call me in production code
  public void clientMethod() {
    while(true){
        doSomethings();
    }
  }

  // call me in tests
  void doSomethings(){
    // loop logic
  }
}

Upvotes: 3

Related Questions