3

I need to run a code for given number of times. I'm wondering how to unit test this loop

public void executeCode(int iterations){

  for (int i = 0; i < iterations; i++)
      try {
           callToCode(parms...);
      } catch(Exception e) {
           // handle exception 
           i--;
      }
}

callToCode this method has 100% line coverage but one of the requirement it that this method get executed for given number of iterations.

I can have instance variable to track execution but what is the best way to unit test such loop?

I can only use Junit and not other library

tkruse
  • 10,222
  • 7
  • 53
  • 80
Rahul B
  • 132
  • 1
  • 1
  • 11
  • Use Mockito and check if the method is being called. Mockito allows you to test behavior, JUnit lets you test state. – BeardedDev Jun 29 '18 at 13:37
  • here i have a restriction on using Mockito or any other framework. I should mention that in question – Rahul B Jun 29 '18 at 13:41
  • Well obviously you method is always getting executed successfully the given number of iterations as you decrease `i` on a exception. It might take an infinitely amount of time though. – Herr Derb Jun 29 '18 at 13:46
  • Does "handle exception" have code that needs JUnit coverage? – Mark Jeronimus Jun 29 '18 at 14:09
  • @ Mark - No that part is also delegated to separate method which is covered. – Rahul B Jun 29 '18 at 14:25
  • 1
    If you can use JUnit 5, parametrized tests do just that: https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests – AlexC Jun 29 '18 at 14:54

2 Answers2

0

If you can, refactor the code to model the dependency call as a call to an injected member.

public void executeCode(int iterations, Callable callable) {
  for (int i = 0; i < iterations; i++)
      try {
         callable.call();
      } catch(Exception e) {
         // handle exception 
         i--;
      }

}

Then in JUnit you can pass a counting callable of your choice. The code above is just one of many examples, you could also inject a dependency in the constructor of the tested class, like

public class MyClass {
   private Dependency dep;
   public MyClass(Dependency dep) {
      this.dep = dep;
   }

   public void executeCode(int iterations, Callable callable) {
       //...
       dep.callToCode(parms...);
   }
}

Then again in JUnit you can inject a different Class that counts invocations.

Finally if callToCode(parms...); is another method in your class, you can also override it to count, like this:

public class MyClassTest {
    @Test
    public void testCountLoop() {
        MyClass class = new MyClass() {
            int calls;
            @Override
            public void callToCode(parms...) {
                calls++;
            }
        }
        // ... run and assert calls
    }
}
tkruse
  • 10,222
  • 7
  • 53
  • 80
0

I would extract callToCode to another class like

public class Caller{
    public void callToCode(String someparam){//Some logic}
} 

Then your loop would be in another class, with a dependency in the constructor:

 public class OuterClass {
    Caller caller;
    public OuterClass(Caller caller){
        this.caller = caller;
    }
    public void executeCode(int iterations){

      for (int i = 0; i < iterations; i++)
         try {
              caller.callToCode(parms...);
         } catch(Exception e) {
              // handle exception 
              i--;
         }
      }
}

Then in your test, you can use Mockito.verify (called 2 times with any parameter):

public class Test{
    OuterClass target = ...

    Caller mockeCaller = Mockito.mock(Caller.class);

    @Test
    public void test(){
       Mockito.verify(mockCaller, Mockito.times(2)).callToCode(Mockito.any());
    } 
}

With ArgumentCaptor you can validate the parameters of the method call also. Mostly if you cannot unit test your code properly, it is a smell of design issue. By separating the different abstraction levels of your code you can test each level separately and you will get a much cleaner, readable solution.

balint.steinbach
  • 278
  • 3
  • 16