Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it a good practice to use package private methods in order to facilitate unit tests?

Some times I found myself in situations in which unit tests would be easier if I change the visibility of some methods from private to package private in order to either facilitate unit test mocking, assertions...

One example would be this

Say I have an object A that contains 4 attributes X, Y, Z and R where X, Y and Z are sets and R are relations among different elements of each set, for example a relation will be composed of an element of X , an element of Y and an element of Z. The object A does not allow direct access to X, Y, Z or R, instead it provides a rich API which allows you to create new elements on X, Y and Z, also allows you to mix those elements into new R elements. For a unit testing it would be highly convenient to have a public getX(), public getY(), public getZ() and public getR() methods, so I can make exact assertions about the internals of the object each time I call the object API. However, exposing X, Y and Z is something I want to prevent, which is why from the very beggining the object make those elements private and only provided indirect access to them by using its API. Would it however make sense to provide package private methods getX(), getY(), getZ() and getR() so that at least form the unit test I can easily check if the internal state of the object is the expected one?

The drawback is of course that the visibility of the method is increased and given that such method was private for a good reason, It feels a little bit weird.

Of course I could use reflection to achieve the same, but that feels even dirtier.

So the question is, is this a good or a bad practice? is it a code smell? does it happen to someone else? is there a better technique for this?

like image 952
Juan Antonio Gomez Moriano Avatar asked Mar 05 '15 00:03

Juan Antonio Gomez Moriano


People also ask

Should unit test methods be private?

As a rule, the unit tests we write should only check our public methods contracts. Private methods are implementation details that the callers of our public methods aren't aware of. Furthermore, changing our implementation details shouldn't lead us to change our tests.

Should unit tests be in the same package?

The need to test protected interfaces means that unit tests should belong to the same package as the production classes they test, so they must have the same directory path. This can be done by creating separate but parallel hierarchies for the production and test code.

Is it a good practice to make all methods public?

Yes it is very bad practice - you're letting your tools make design decisions for you. I think the main problem here is that you're trying to treat each individual method as a unit. This is generally the cause of all unit test woes.


2 Answers

Generally the good practice is to not expose the internal logics. Instead you have to make your classes configurable. For example if your class needs other components like let's say HttpComponent or etc. try to use different dependency injection techniques to provide those dependencies. Then in tests, you can mock those dependencies and verify those mocks.

In your case, it depends on the context. Most of the time, you test the private functions as part of the testing the public functions. So you test the public behaviour for different cases and if all pass it means the private function which has been invoked via that public function works too.

like image 169
Nami Avatar answered Nov 03 '22 00:11

Nami


If you want to test a private method you have a few options:

  1. Dependency Injection

If you really need to test a private method it's very possible that the method should actually be a public one of another class (code smell). This means that if you have the private method 'm' in the class 'A', you might consider moving 'm' as a public method in a class 'B' and then inject 'B' into 'A'.

This technique is quite common and most of the times it does the trick. Nevertheless, it's only meaningful when you move a significant part of your code from the original class ('A') to the new class ('B').

This solution is Mockito friendly.

  1. Test Double Inheritance

Change the visibility of the private method 'm' in the class 'A' to protected. Then create a class 'B' in the test folder that extends 'A'. Finally, create a public method 'pm' in 'B' with the same signature as 'm' that just calls 'm'. This way you can "make" the non-visible 'm' method in 'A' accessible though 'pm' in 'B'.

This solution is also Mockito friendly.

  1. Groovy

Use a JVM dynamic language such as Groovy that allows you to call private methods.

  1. Reflection

Get the private method using reflection and then change its visiblity. See this.

  1. Package Visibility

Declare the method as package (no visibility constraint) and declare the test class in the same package. I personally don't like using this one because I don't find elegant Java's package visibility.

like image 43
Pau Carre Avatar answered Nov 03 '22 01:11

Pau Carre