What is the best way to define access to the methods of a class I want to use for unit testing?

4

I have a Runnable class, which does a lot of things on my system. When I created her, I did not want to instantiate her methods out of her scope. So I wrote the methods as protected .

Now, I'm trying to write some unit tests for this class, I thought of 2 solutions:

  

1 - Leave the method public.

     

2 - Make the test class extends the class in question to use the protected functionality.

What would be the best solution?

I would also like to mention that I am storing my tests in a package other than where the class I want to test is.

public class QualityTest extends DailyJob {

    @Test
    public void testGetBaseById() {
        /*
            testa se o metodo getBasePeloId está retornando um objeto do tipo base, como deveria.
         */
        Base realBase = getBaseById(1);
        assertTrue(realBase instanceof Base);
    }
}

The DailyJob is in package BackgroundProcesses .

The QualityTest is in package UnitTests .

    
asked by anonymous 12.07.2018 / 15:25

2 answers

7

First I'll tell you a bit about inheritance. Inheritance is not for writing tests. Do not use inheritance for this. Incidentally, there are people who argue, including myself, that heritage should not be used for anything, and should be replaced whenever possible by composition or something else in order to achieve a weaker coupling and a higher cohesion, which are objectives opposed to those promoted by the use of inheritance. I speak a little of this in the middle of this other answer . I speak something like this also in that other answer , where I also enter the protected .

Within your unit test, you instantiate the class you want to test or get an instance of it through a factory, singleton or something and use it. What matters is knowing this rule number 1:

  

(1) the test uses the object to be tested.

However inheritance establishes this relationship:

  

(2) An instance of the subclass is a superclass type object.

If you use inheritance in your test, you are establishing that the test is an object to be tested instead of the test test object. That way, if you use your second alternative, you're violating rule 1 above. So do not use inheritance.

Your test would look something like this:

public class QualityTest {

    @Test
    public void testGetBaseById() {
        /*
            testa se o método getBasePeloId está retornando
            um objeto do tipo base, como deveria.
         */
        DailyJob d = new DailyJob();
        Base realBase = d.getBaseById(1);
        assertTrue(realBase instanceof Base);
    }
}
    
12.07.2018 / 16:44
2

You do not have to leave your public method to be able to test it. Ideally, only public methods should be tested. If your method is private, it means that some public method will call it. You can perform the tests through the public method.

For example, imagine that this is your Base class:

public class Base {

    public int getNumero() {
        return 2;
    }
}

And this is your class DailyJob :

public class DailyJob {

    private Base getBaseId(int i) {
        return new Base();
    }

    public int calculo(int i) {
        Base base = getBaseId(i);
        return  base.getNumero() + 1;
    }
}

You can perfectly perform your test using the calculo method. Your test would be:

@Test
public void testCalculo() {
    DailyJob d = new DailyJob();
    int resultado = d.calculo(1);
    int valorEsperado = 3;
    assertEquals(resultado, valorEsperado);
}

If the method is complex and you really do need to create a test just for it, one strategy is to leave accessibility package . However, the test class must be in the same package as the class to be tested. It is worth noting that regardless of this, it is interesting that test classes always fall into the same package as the class being tested.

If you are using some build manager like Maven or Gradle, they do a separation of folders for the codes and resources that should go into production and the codes and resources that are just for testing.

Both maven and gradle use src/main/java as a directory for production and src/test/java as a directory for testing. You can replicate the packages in the two folders, but both will not be considered as packages, just what is below them.

See image below:

Note that DailyJob is in src/main/java and DailyJobTest is in src/test/java . Despite this, both are within the same stackoverflow.dailyJobPackage package. This way you will be able to access the methods quietly and they will remain invisible to the classes of the other packages. When the project is compiled the build manager will only use what is in src/main/java to build.

Without using a build manager it is possible to achieve the same result, but you will have to do the configuration.

    
13.07.2018 / 06:30