1

I have a piece of code that retries when it fails with ExceptionOne, and if it continues to fail, it will throw a ExceptionTwo. I want to test this behavior, but am not sure how to.

public void someMethod(String x) {
    boolean retry;
    int try = 0;
    do {
        retry = false;
        try {
             someFunction(x);
        } catch (ExceptionOne e) {
            if (try < 5) {
                retry = true;
                attempt++;
            } else {
                throw new ExceptionTwo();
            }
        }
    } while (retry);
    
}

private void someFunction(String x) {
    doSomething(); //This can throw ExceptionOne
}

Suppose these functions are inside a SomeClass. I mocked SomeClass and tried something like

doThrow(new ExceptionOne()).doNothing().when(mockObject).someMethod(any())

for consecutive calls, but this isn't quite what I am looking for since I believe this is really testing the someMethod, not someFunction or the retry functionality.

How do you test something like this? That is, how do I verify that the behavior that when someFunction fails with ExceptionOne, it will retry until it succeeds or until it runs out of retries and throws an ExceptionTwo?

askaka12
  • 195
  • 2
  • 2
  • 9
  • What are the condition(s) that `doSomething()` would throw? The test needs to be able to set those conditions, either through an input or perhaps through an injected mocked dependency. Note that the scope of methods/fields should not be increased to support unit tests. If it's hard to test, then the class needs to be refactored (probably using dependency injection). – Andrew S Feb 02 '22 at 22:32
  • `doSomething` writes to a database through a client and there is a rare chance that this operation might go wrong and throw an exception. Is there no way to force `someFunction` to throw an exception the first time and then not throw an exception the second time? That would be the retry succeeding case, and then I suppose I'd also need to test `someFunction` failing all 5 times and resulting in `ExceptionTwo` – askaka12 Feb 02 '22 at 22:55
  • I was hoping that there would be a way to do something similar to the `doThrow` I have above for the `someFunction` method; and so I can call `someMethod` in my test and then do something like `verify(mockObject, times(2)).someFunction()` to check whether `someFunction` ran twice. – askaka12 Feb 02 '22 at 23:03
  • That `client` should be an injected dependency so it can be mocked. And using the other mocktio syntax to chain, the setup would be something like `when(mockClient.write(any())).thenThrow(new ExcpetionOne()).thenReturn(...success...);` – Andrew S Feb 02 '22 at 23:05
  • The database in question in Amazon DynamoDB. The way the current class that has `doSomething` is initialized is through supplying a `DynamoDBMapper`. Supposing the class is called `MyClass`, I suppose you mean I would have to do something like `MyClass mockObject = new MyClass(mockMapper)`? If so, another question I have is, do I mock the mapper or the client? The mapper is created by supplying a client. Do I only mock the client, then create a mapper through that mockClient, and then create MyClass through that mockMapper? – askaka12 Feb 03 '22 at 14:59
  • Mock whatever `doSomething()` invokes. It may be helpful to update the question with the body of `doSomething()`. – Andrew S Feb 03 '22 at 15:05
  • If you want to test retry functionality in a unit-test, then one way of doing it would be to relax visibility of `someFunction()` to package-private and then just mock it in the test as you did for `someMethod()`. In general it's fine to relax a visibility of a method for better testability. Guava even has `@VisibleForTesting` annotation for documenting such cases. This annotation is used to "annotate a program element that exists, or is more widely visible than otherwise necessary, only for use in test code." – ouid Feb 03 '22 at 17:09

1 Answers1

0

I ended up scrapping my current retry logic and using an external library to do the retry instead.

askaka12
  • 195
  • 2
  • 2
  • 9
  • may be you can add a little more detail to the question as to what you did and what error/issue you saw. What's the external library you used? – Champ Feb 04 '22 at 17:23
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Champ Feb 04 '22 at 17:23