57

I´m running pure JUnit4 java tests over my pure java files on my project but I can't find a way to use @VisibleForTesting clearly without making the thing manually public.

Ex:

@VisibleForTesting
public Address getAddress() {
  return mAddress;
}

The method has to be public to let it be "public" to tests, but in that case the annotation doesn't make sense right? why not just use a comment if the annotation will not do nothing?

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Daniel Gomez Rico
  • 15,026
  • 20
  • 92
  • 162

4 Answers4

66

Make the method package-private and the test will be able to see it, if the test is in the corresponding test package (same package name as the production code).

@VisibleForTesting
Address getAddress() {
  return mAddress;
}

Also consider refactoring your code so you don't need to explicitly test a private method, try testing the behaviour of a public interface. Code that is hard to test can be an indication that improvements can be made to production code.

The point of an annotation is that its convention and could be used in static code analysis, whereas a comment could not.

MikeJ
  • 2,367
  • 3
  • 18
  • 23
  • not sure why this has so many more up votes. it's the same as the accepted answer except it's much less flexible the accepted answer doesn't require you to put your production code into the same package as your unit tests. – mpellegr Oct 04 '18 at 17:02
  • 6
    "Also consider refactoring your code so you don't need to explicitly test a private method, try testing the behavior of a public interface." disagree with this. sometimes you need to do dependency injection by passing a set of arguments into some class A that creates some object fro class B. I had to create a public constructor for class B so class A could use that directly instead of class Bs getInstance() method. The accepted answer worked for me, I couldn't move my units tests into different packages to make this work. This seems like a workaround to me and the accepted answer is better. – mpellegr Oct 04 '18 at 17:05
  • @MikeJ, can you please expand on the comment: "Also consider refactoring your code so you don't need to explicitly test a private method." One method/function should have one responsibility. If it has knowledge of a given class' implementation then it may be private, but it can still be very useful to test it against various inputs. – Andy Marchewka Jan 25 '22 at 15:54
  • If a test is written to test behaviour, and not the implementation it would be possible to refactor a public method by splitting it out some into a private method, and the existing tests would still cover the private method. TDD is good for this, as the test is done first before the implementation. The key is to test the behaviour, covering all the inputs from the publicly accessible API for the Class Under Test. It's much harder to write tests to cover a private method after it's implemented. By testing behaviour, you can refactor the production code without having to change the test code. – MikeJ Feb 04 '22 at 13:48
37

According to the Android docs:

You can optionally specify what the visibility should have been if not for testing; this allows tools to catch unintended access from within production code.

Example:

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public Address getAddress()
Priyanka
  • 3,369
  • 1
  • 10
  • 33
Rodrigo Borba
  • 1,334
  • 1
  • 14
  • 21
32

The Tag itself helps with the linter to identify unwanted access.

To lower the risk of use it directly, add this methods as internal in Kotlin or protected in Java instead of public and with that only the tests or classes that are in the same package will be able to access that method.

Java:

@VisibleForTesting
protected Address address() {
  return mAddress;
}

Kotlin:

@VisibleForTesting
internal fun address(): Address {
  return address;
}
Daniel Gomez Rico
  • 15,026
  • 20
  • 92
  • 162
  • 1
    From the annotation's JavaDoc: "Do not use this interface for public or protected declarations." The other answers are correct, such methods should have package visibility in Java. – winne2 Jan 29 '22 at 07:24
8

@VisibleForTesting annotation is used in package-methods in Guava, and does not part of JUnit API. The annotation is just a tag to indicate the method can be tested. It even doesn't be loaded in JVM.