2

The retry method precisely looks into a folder for a particular file and returns the file if present .It has a Max retry count for 3 and sleeps for 1 min between 2 retries .If after Max retries file is not present it throws some exception

method is soemthing like this

File getFile(int retryCount){
    File file;
    file =getFilefromLocation();
    if(file!=null) return file
    if(file==null and retryCount>0)
    Thread.sleep(1)
     --retryCount;
    File filePicked =getFile( retryCount)
   }
else return null;
}
user8618585
  • 81
  • 1
  • 10
  • Possibilities may vary based on your actual code. Can you post the code that you're testing? – ernest_k Jul 28 '18 at 06:43
  • You very much start by making the delay time configurable. So you can sleep for 1 second instead 1 minute in your test. – GhostCat Jul 28 '18 at 06:59

3 Answers3

2

One possible way is to factor out the part that checks for the presence of the file into its own object and to mock that object.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
Henry
  • 42,982
  • 7
  • 68
  • 84
  • I will only mock the file and make the file available say after 1 min or 2 mins and assert it's presence or assert no availability but I do not have control over how many times the actual method has retried .What I want to know is how to know the method has retried what number of times. – user8618585 Jul 28 '18 at 06:41
  • You would see the number of retries made in the mocked object. – Henry Jul 28 '18 at 06:45
1

I would make the retry time configurable and make it 1 second for testing. You can have tests where the file is already there, never there and one where it is added as a background thread. Should take about 5 seconds.

I would also consider shorter retry times more often eg 120 attempts every 1 second instead of 3 times every minute.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

I will try to give some pointers.

1) Here I would not mock the whole logic to check the presence of the file as it makes part of the behavior of the method under test.
What you want to mock is the part where you actually try to retrieve the file, that is as you invoke getFilefromLocation().

2) Using a short delay between retries is a idea but is it enough to make your test robust?
Not really because you also have to assert that Thread.sleep() was invoked and with the expected value. Not checking that in the unit test means that anybody can remove Thread.sleep() in the getFile() method and the test will still pass. You really don't want that.
Note that you cannot easily mock it : it is static. You should move this statement into a DelayService that you could mock.

3) Actually you parameterize retryCount. As you write unit tests, you want to validate the behavior of the components according to your requirements. So you should also ensure that the class that depends on getFile(int retryCount) passes effectively the expected parameter : 3.
Besides if you don't want to allow more than a certain retry number, you also have to add this check in the method and assert this rule in the unit test.

According to the two first points, your actual code could be refactored as :

private FileLocator fileLocator; // added dependency
private DelayService delayService; // added dependency

//  constructor to set these dependencies
public MyService(FileLocator fileLocator, DelayService delayService){
    ...
}

File getFile(int retryCount){
    File file;
    file = fileLocator.getFilefromLocation(); // -> change here
    if(file!=null) return file
    if(file==null and retryCount>0){
       delayService.waitFor(); // -> other change here
       --retryCount;
       File filePicked = getFile(retryCount)
    }
    else return null;
}

About the unit test part, you could use Mockito for mocking and mix "simple" unit tests with parameterized tests as some scenarios have a similar behavior with as variance the number of actual retries.
For example retrying 0, 1, 2 and 3 times and finding the file are 4 cases that you can parameterize.
And inversely failing to find the file doesn't need to be parameterized as all retries will should be done to validate the behavior.

Here is a code example relying on JUnit5 and Mockito (not tested) that you could adapt according to your actual code. I just illustrated with the parameterized test. The other test cases should not be more complex to implement.

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.api.Assertions;
import org.mockito.Mockito;
import org.mockito.Mock;

private static final long EXPECTED_DELAY_MN = 1; 
private static final long RETRY_COUNT = 3; 

@Mock
FileLocator fileLocatorMock;

@Mock
private DelayService delayServiceMock;


MyServiceTest myServiceTest;

public MyServiceTest(){
   myServiceTest = new MyServiceTest(fileLocatorMock, delayServiceMock);
}

@ParameterizedTest
@ValueSource(ints = { 0, 1, 2, 3 })
public void getFileThatFailsMultipleTimeAndSuccess(int nbRetryRequired){    

    // failing find
    for (int i=0; i < nbRetryRequired; i++) {
        Mockito.when(fileLocatorMock.getFilefromLocation(...)).thenReturn(null);
    }

    // successful find
    File fileByMock = new File(...); //fake file
    Mockito.when(fileLocatorMock.getFilefromLocation(...)).thenReturn(fileByock);
    File actualFile = myServiceTest.getFile(RETRY_COUNT);

    // assertions
    Mockito.verify(delayServiceMock, Mockito.times(nbRetryRequired)) 
           .waitFor();
    Assert.assertSame(fileByMock, actualFile);

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