Gitesh
Gitesh

Reputation: 396

Junit Test Case for private method

I have a below class. I am new in writing junit tests. I need to write the test case for this. How can I write test method for startSchemaMaintenance method in the test class as it is calling a private method with no args?

public class SchemaMaintenance {

    //Loading statuses overview
    // NOT_STARTED = 0
    // START_LOADING = 1
    // IN_PROGRESS = 2
    // COMPLETED = 3
    // LOADING_ERROR = 4
    private static volatile Integer loading_status = 0;

    public void startSchemaMaintenance() throws Exception {
        if (checkLoadingStatus() == 1) {
            doSchemaMaintenance();

            loading_status = 3;
        }
    }

    private void doSchemaMaintenance(){
        //Do something....
    }

    private int checkLoadingStatus() throws Exception {
        if (loading_status==0 ||loading_status == 2) {
            synchronized (loading_status) {
                if (loading_status==0) {
                    loading_status = 2;
                    return 1;
                }else if(loading_status == 2) {
                    while(loading_status == 2);

                    if((loading_status == 4)){

                        throw new Exception("status = " + 4);
                    }
                }else if(loading_status == 4) {

                    //log.error(generateErrorMessage());
                    throw new Exception("status = " + 4);
                }
            }
        }else if((loading_status == 4)){
            //log.error(generateErrorMessage());
            throw new Exception("status = " + 4 );
        }

        return loading_status;

    }
}

Upvotes: 1

Views: 28414

Answers (4)

will
will

Reputation: 5081

Here's an approach that worked for me.

  1. Make a public "tester" inner class in your class.
    • If you are testing an inner class the "tester" still needs to be on the outer class.
  2. The tester is a delegate for the private methods you'd like to test.
  3. Add a method to your class to get and instance of the tester
  4. In your test method get an instance of the tester and
  5. Invoke your private method via the appropriate tester instance method.

There will be different variations depending on the access to methods and inner classes, etc required.

This example can't have a static "tester" because we needed to test methods in a non-static inner class.

//  Outer owner class
//
public class OwnerClass Class {
    
    private class InnerClass {
            : 
            
        //  method I'm testing
        //
        private void doSomething)( String name ){
            ...
        }
    }
    
        :
    
    //  Tester class
    //
    public class InnerClassTester {
        
        private final InnerClass        innerClass    = new InnerClass();
            :
            
        //  test invocatiion
        //
        private void doSomething)( String name ){
            
            innerClass.doSomething)( name )
        }
    }

    //  Get an instance of the tester
    //    
    public InnerClassTester getInnerClassTester() {
        return new InnerClassTester();
    }

Inside your the Unit Test ...

@Test
public void doSomethingTest() throws Exception {
    
    OwnerClass ownerClass = new OwnerClass();
    innerClassTester      = ownerClass.InnerClassTester();
            :
            
    //  Test the private method
    //
    innerClassTester.doSomething( "123456" );
}

Works with debugger. You need to be flexible defining the tester and the tester-getter depending on your access. The tester can probably be protected too, I didn't try that. All the best.

Upvotes: -1

Jan Held
Jan Held

Reputation: 654

Normally, when you are not able to test your code, you should think of rewriting it, so that it is testable. Private methods cannot be called in a regular way, outside of your class, not even within a JUnit test.

There are a few things you could do to make it testable:

  1. Rewrite your code to split the method into smaller ones. Should always be done, where appropriate.

  2. You could make the method package private (remove the private modifier), so your test class, which has to be in the same package, can access it.

  3. You could make the method protected, so you could inherit your class in your test and indirectly call that method over your inherited class.

  4. You could call a method, that calls the method, you want to test and make your assertions accordingly.

If you tend to change the method's visibility to package private or protected, you could annotate your method as @VisibleForTesting, so tools like Sonar and other team members know, why it is not private.

Hope it helps.

Upvotes: 1

xleon90
xleon90

Reputation: 1336

Your private method works according to the internal private variable loading_status value so on your tests you should just be able to change that variable. To do this you can do as follow:

package com.test

import com.test.SchemaMaintenance;
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;

public class SchemaMaintenanceTest {

    @Test
    public void TestSchema() throws Exception {
        SchemaMaintenance schema = new SchemaMaintenance();
        Whitebox.setInternalState(schema,"loading_status",2);
        schema.startSchemaMaintenance();
    }

}

Supposing You should include the mockito dependency on your project:

e.g. Maven

<dependency>
  <groupId>org.powermock</groupId>
  <artifactId>powermock-api-mockito</artifactId>
  <version>1.6.4</version>
</dependency>

Upvotes: 0

dfritsi
dfritsi

Reputation: 1232

First of all a couple of things:

  1. Why is loading_status static? Basically 2 instances of this class would use the same loading_status variable. loading_status should either not be static or checkLoadingStatus should be static as well.
  2. loading_status is terribly named. If you want to keep it static, you should name it LOADING_STATUS. If it won't be static, let's name it loadingStatus. That's the Java convention.
  3. You should create a proper enum type for loading_status instead of it being an integer.
  4. while (loading_status == IN_PROGRESS); is just simply bad practice. At least what you can do is: while(loading_status == IN_PROGRESS) { Thread.sleep(100); }; However a timeout is a better practice. Sometehing like:
long timeoutMillis = 5000L; // Should come from a configuration or something
long endTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (loadingStatus == IN_PROGRESS) {
    long remainingMillis = end - System.currentTimeMillis();
    if (remaining <= 0) {
        // Timeout while loading
        loadingStatus = LOADING_ERROR;
        break;
    }
    Thread.sleep(50);
}
// ...

And finally I guess something will update loadingStatus to completed or something. I guess you are using synchronized there as well. If the loading happends on 2 different threads then you will end-up in a dead-lock. If checkLoadingStatus enter the synchronized block first, so when the loading is completed the loading thread will never be able to enter the synchronized block, because the checkLoadingStatus is holidng the lock.

Last but not least to answer you question, if you want to keep your method private, you can invoke it through reflection, but that's again bad practice. Generally you should avoid unit testing private methods and unit test those methods which are invoking it. If however you definitely need to unit test a particular method, make it package-private instead of private, and then you can create a unit test in the same package where the class is which contains your method. And you can add a comment to the method saying it's only visible for unit testing. Example of your code structure in this case:

src
+-- main
+-- +-- com/app/MyClass.java
+-- test
    +-- com/app/MyClassTest.java // this test class will able to access package-private methods in MyClass

Upvotes: 2

Related Questions