0

Consider I have a class Tournament with methods register() and isAlreadyRegistered(). Below is the sample code.

public class Tournament {

    private boolean register(String teamName) {

        if(!isAlreadyRegistered(teamName)) {
    
           // register team
    
           return True;
        }
        return False;
    }
    
    private boolean isAlreadyRegistered(String teamName) {
        // Check if team is already registered, involves DB calls
    }

    public static void main(String[] args) throws Exception {
        Tournament tournament = new Tournament();
        tournament.register("e-LEMON-ators");
    }
}

I have a Java test-case which calls main method of class Tournament, which leads to call to register() method and register() method calls isAlreadyRegistered(). Consider below code:

@Test
public void testTournament() {
    try {
        Tournament.main(args);
        } catch (Exception e) {
            fail();
        }
}

I want to mock isAlreadyRegistered(), maybe using Mockito, so it always returns True

Note: The example is only for demonstration purpose and I cannot modify the Tournament class. Modifications can only be made in Test case. Testing register() separately is not an option (call has to be made through main method)

EDIT: I cannot create object for class Tournament i.e. I can interact with the class only through main() method

s.k
  • 193
  • 1
  • 2
  • 15
  • Does this help? https://stackoverflow.com/questions/37095096/how-to-mock-a-call-of-an-inner-method-from-a-junit – JCompetence Dec 08 '20 at 13:14
  • Thanks @SusanMustafa, but I cannot create object for class `Tournament` i.e. I can interact with the class only through `main()` method – s.k Dec 08 '20 at 13:17
  • Then you have basically "super-hard to test code". The real world solution would be to change your production code. But note: you could use PowerMock(ito), that would allow you to "intercept" that call to `new Tournament()` to return some mocked object (and you could still have a mock that calls real methods where needed). But all of that is dirty dark magic. Dont do it unless you really have no other option. – GhostCat Dec 08 '20 at 16:05

1 Answers1

2

Try moving the actual register method call into a different method so that you can pass the instance of tournament to the method. Which means, modify your main method to

public static void main(String[] args) throws Exception {
    Tournament tournament = new Tournament();
    performRegister(tournament);
  }

  public static void performRegister(Tournament tournament) {
    tournament.register("e-LEMON-ators");
  }

Now your test method becomes as below.

@Test
  public void testTournament() {
    try {
      Tournament tournament = Mockito.mock(Tournament.class);
      Mockito.when(tournament.isAlreadyRegistered(anyString())).thenReturn(true);
      Tournament.performRegister(tournament);
    } catch (Exception e) {
      fail();
    }
  }

Edit : Another solution is, If you don't want to modify Tournament class, use PowerMock.

Here is the test class

    import static org.junit.Assert.fail;
    import static org.mockito.Matchers.anyString;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    import org.powermock.modules.junit4.PowerMockRunner;
    
    @RunWith(PowerMockRunner.class)
    @PrepareForTest(Tournament.class)
    public class TournamentTest {
    
      @Test
      public void testTournament() {
        try {
          Tournament mock = PowerMockito.mock(Tournament.class);
          PowerMockito.when(mock.isAlreadyRegistered(anyString())).thenReturn(true);
          PowerMockito.whenNew(Tournament.class).withAnyArguments().thenReturn(mock);
          Tournament.main(null);
        } catch (Exception e) {
          fail();
        }
      }
    
    }

Here are the dependencies

    <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>1.6.4</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>1.6.4</version>
      <scope>test</scope>
    </dependency>

Here are the files for the reference
Tournament.java : https://gist.github.com/sjabiulla/4bdf71f81079e38aef137e64913bf26b TournamentTest.java : https://gist.github.com/sjabiulla/4a557516e834bba6d6047687f7e32deb pom.xml : https://gist.github.com/sjabiulla/10abb153e82e14194fd1ccc2689b192d

i.am.jabi
  • 620
  • 1
  • 5
  • 23
  • Hi @i.am.jabi, like I mentioned in my question, I cannot modify `Tournament` class and your solution would involve adding additional method. – s.k Dec 08 '20 at 14:24
  • @ShubhamKadam check the edited solution now – i.am.jabi Dec 08 '20 at 15:13
  • Hi @i.am.jabi, I tried your code, it didn't work. I tried printing value returned by `isAlreadyRegistered()` and it printed `false` – s.k Dec 08 '20 at 15:48
  • @ShubhamKadam , I have tested this and provided the solution. It works 100% on my side. Can you tell me what dependencies you have.. In my pom.xml, I only have added the above mentioned PowerMock dependencies and literally nothing else. And I am using Java 8 . – i.am.jabi Dec 08 '20 at 15:56
  • Hi @i.am.jabi, I created a new project and used dependencies mentioned by you, then I made sure `isAlreadyRegistered()` throws exception (divide by zero) and it did, which caused testcase to fail. Shouldn't PowerMockito prevent that from happening? i.e return `true` FYI. I am too using java 8 (openJDK) – s.k Dec 08 '20 at 16:02
  • I have updated the solution and added the files that I have used. If you are still facing issues, provide the files that fails for you. As you can see, I am also throwing the Exception on `isAlreadyRegistered()` method and it works fine for me. – i.am.jabi Dec 08 '20 at 16:04
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/225676/discussion-between-shubham-kadam-and-i-am-jabi). – s.k Dec 08 '20 at 16:04