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

Several times I found myself in situations where unit tests would be easier if I changed the visibility of some methods from a private to a private package to facilitate unit test mocking, assertions ...

One example would be this

Let's say I have an object A that contains 4 attributes X, Y, Z and R, where X, Y and Z are sets and R is the relationship between the different elements of each set, for example, the relationship will consist of the element X, the element Y and element Z. Object A does not allow direct access to X, Y, Z or R, instead it provides a rich API that allows you to create new elements on X, Y and Z, and also allows you to mix these elements with new elements of R. For unit testing it would be very convenient to have public methods getX (), public getY (), public getZ () and p ublic getR (), so I can make precise statements about the internal components of an object every time I call the API of the object. However, exposing X, Y, and Z is what I want to prevent, so from the very beginning the object makes these elements private and provides indirect access to them using its API. However, does it make sense to provide private methods getX (), getY (), getZ () and getR () so that at least to form a unit test, I can easily check if the internal state of the object is expected?

The disadvantage, of course, is that the visibility of the method increases, and given that such a method was closed for a good reason, it feels a little strange.

Of course, I could use reflection to achieve the same, but it feels even messier.

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

+5
source share
4 answers

As a rule, it is not good practice to disclose internal logic. Instead, you should customize your classes. For example, if your class needs other components, for example, let HttpComponent, etc. Trying to use various dependency injection methods to provide these dependencies. Then, in tests, you can mock these dependencies and test these mocks.

In your case, it depends on the context. In most cases, you test private functions as part of testing public functions. This way you check public behavior for different occasions, and if all of this is passed, it means that the private function that was called using this public function also works.

+1
source

If you want to test a private method, you have several options:

  • Dependency Injection

If you really need to test a private method, it is very possible that the method should be actually public from another class (code smell). This means that if you have a private method β€œ m ” in class β€œ A ”, you might think of porting β€œ m ” as a public method in class ' B ', then type 'B' into 'A' .

This technique is quite common, and most of the time it does the trick. However, this only makes sense when you move a significant portion of your code from the source class ( 'A' ) to the new class ( 'B' ).

This is a Mockito solution.

  1. Test Double Inheritance

Change the visibility of the private method 'm' in class "A" for protection. Then create class "B" in the test folder which extends "A" . Finally, create a public 'pm' method in 'B' with the same signature as ' m ' , which simply calls ' m . That way you can β€œmake” the invisible method β€œm” in β€œA” , accessible though β€œpm” in B ' .

This solution is also Mockito .

  1. Groovy

Use a dynamic JVM language like Groovy, which allows you to call private methods.

  1. Reflection

Get a private method using reflection, and then change its visibility. Cm. .

  1. Package Visibility

Declare the method as a package (without restriction of visibility) and declare a test class in the same package. I personally don’t like it because I don’t see the elegant visibility of the Java package.

+1
source

It looks like A does too many things if he controls X, Y, Z, and R.

It might be a good idea to reorganize your code to make R a separate class that accepts X, Y, and Z as input parameters. If you want, the class can be made private. At the same time, you will be able to test R directly by providing different X, Y and Z.

0
source

For unit test, you may have problems with testing and bullying personal, static methods and constructors. Thus, you have the choice to use one of two solutions:

  • Do not use a private method and make them visible. Also, do not use static methods and do them in a singleton class.

  • Use a mock library that runs tests like powermock. It can also mock the constructor and the static method.

Bad or good decision, I think not, it depends on the strategies of the project and on the decision to make them for verification.

-1
source

Source: https://habr.com/ru/post/1214703/


All Articles