candied_orange
candied_orange

Reputation: 7336

How evil is it to use reflection to break encapsulation when testing?

I'm testing 3 new classes that make 3 collections of objects that are serialized to a database. 1 of the classes has a hard coded array of strings. The 3 collections end up the same size as the array of strings and each object in the collection gets a name/tag based on string in the array.

One of my tests is an end-to-end test that will try to make all three collections. I would like to have access to the array of strings for testing but it's private. I see three possible ways to deal with this:

Can you think of a fourth way? Is there a good reason to not use reflection for this?

Upvotes: 3

Views: 516

Answers (2)

Oleg Estekhin
Oleg Estekhin

Reputation: 8395

The variable itself should stay private, but you can add a getter with default (package-private) access.

If the test code is located in the same package. this solves the problem right away.

If the test code from other packages needs to access this getter, you will have to write some boilerplate of the following form:

The class under test has private field and a getter with default access:

public class MyClass {

    private Object someData;

    // you should research the usage of these or similar annotations
    // they will help your IDE to catch improper usage of test-only methods
    //@com.google.common.annotations.VisibleForTesting
    //@org.jetbrains.annotations.TestOnly
    Object getSomeData() {
        return someData;
    }

}

The test code contains a helper that exposes getter as public so it can be used from tests in other packages:

public class MyClassTestHelper {

    public static Object getSomeData(MyClass instance) {
        return instance.getSomeData();
    }

}

You should also look at How do I test a class that has private methods, fields or inner classes? (your question can be even considered duplicate of it) and annotation to make a private method public only for test classes .

Upvotes: 1

Michael Aaron Safyan
Michael Aaron Safyan

Reputation: 95489

It really depends on what your test is trying to exercise. In general, if you are testing the internal implementation details of your code, you are probably doing the wrong thing with your tests. After all, your tests should be exercising the observable effects of your code, not the implementation details. Tests that exercise implementation details are brittle (needing to be updated whenever the implementation changes) and usually are just a copy of the implementation. Also, because they mirror the implementation, bugs in the implementation are likely to be reflected in the tests, as well, so such tests are typically of dubious value.

A better approach for such a thing is to use an interface to represent your database object and to use a mocking framework such as mockito to verify that the data you expect is written to and read from the database, without inspecting the innards of how your code is storing the data internally before writing it to the database.

Also, in terms of not writing code differently for tests... in general, it is good practice not to have test-only methods in your production code. However, refactoring code to make it more modular (and therefore more testable), is a generally good coding practice to have.

Upvotes: 2

Related Questions