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

Asked

Viewed 95 times

4

I have a class Runnable, that does a lot of things in my system. When I created it, I didn’t want to instantiate its methods out of her scope. That’s why I wrote the methods like protected.

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

1 - Leave the public method.

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

What would be the best solution?

I would also like to mention that I am saving my tests in a package different from where is the class I want to test.

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 the package BackgroundProcesses.

The QualityTest is in the package UnitTests.

2 answers

7


First I’ll talk a little bit about inheritance. Inheritance is not for writing tests. Do not use inheritance for this. Indeed, there are people who argue, including myself, that inheritance should not be used for anything and should be replaced whenever possible by composition or by something else with the aim of achieving a weaker coupling and a higher cohesion, which are objectives opposed to those promoted by the use of inheritance. I’m talking a little bit about in the middle of that other answer. Say something like that too in that other answer, where I also address 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 that rule number one:

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

But the inheritance establishes that relationship:

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

If you use inheritance in your test, you will be establishing that the test is a object to be tested instead of the test use the object to be tested. Thus, if you use your second alternative, you will be violating rule 1 above. So, do not use inheritance.

Your test would be 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);
    }
}
  • 2

    Cool Victor, with the extra answers you linked there, all my doubts have been resolved. Thank you very much!

2

You don’t need 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 some public method will call you. You can perform the tests through the public method.

For example, imagine that this is your class Base:

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 through the method calculo. 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 it is really necessary to create a test just for it, one strategy is to leave the accessibility package. However, the test class needs to be in the same package as the test class. It is worth noting that regardless of this, it is interesting that the test classes always stay in the same package of the class being tested.

If you’re using some build manager like Maven or Gradle, they sort folders for codes and Resources that should go to production and 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 test directory. You can replicate the packages in both folders, but both will not be considered as packages, only what is below them.

See image below:

inserir a descrição da imagem aqui

Realize that DailyJob is in src/main/java and DailyJobTest is in src/test/java. Despite this, they are both within the same package stackoverflow.dailyJobPackage. Then 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 on src/main/java to build.

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

  • Very good response Fagner! I was in doubt "src/main/java" and "src/test/java" are under the source "src" source? I don’t understand how they’re under the same package

  • 2

    Both are in the package stackoverflow.dailyJobPackage, that’s who counts.

  • @user94991 I updated the answer to explain this question of folders.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.