Unit test private inner class methods
Asked Answered
W

4

6

I have a Class A which has an internal Cache represented by a Class B. this inner class is private since the cache need not be visible to external consumers and is only to assist the outer class A. I am using Jmock and Java

public class A {
    ...
    private class B {
    ... 
       public void testMethod() {
          //The method I want to unit test
          ...
       }
    }
}

This is shown above. I am not sure how to unit test the testMethod() which is from private inner class B (as class B is not visible to the outer world).

Please advise!

Thanks!

Wortman answered 28/6, 2015 at 14:56 Comment(5)
possible duplicate of How to cover methods of inner classes?Hunterhunting
Don't unit test private members directly. Unit test the public functionality which invokes those private members. If no public functionality invokes those members, they can be removed entirely.Rick
I am using Jmock and Java unlike Javascript mentioned in the above post. Yes the out public members call the private class methods extensively. is this the preferred way to to do it?Wortman
If these methods from the inner private class are doing non-trivial work, should they be tested using Refection?Wortman
They should not be tested. As the answers already explained - you should test the class contract, not the class implementation.Mckinnon
R
9

since the cache need not be visible to external consumers

A unit test is an external consumer. It's a class which invokes functionality on the object being tested, just like any other class.

Caveat: There is much opinion and debate on this matter. What I'm presenting here isn't "the one true answer" but is based on my own experience in maintaining unit tests in code.

Don't directly unit test private members. Not only does it generally take a little bit of trickery to make that happen, it creates coupling between the classes. (The test class and the class being tested.) Exposing the internals and coupling to them is a violation of object oriented principles.

Rather than thinking of your tests in terms of the methods you call on the class, think of your tests in terms of the functionality you invoke on the unit. Whatever functionality that unit exposes is what should be tested.

This leads to a few conclusions:

  • If there is no public functionality which internally invokes the private members in question, then why are those private members there at all? Just remove them.
  • If the private functionality is very complex and very difficult to invoke/validate using only public functionality then perhaps some refactoring is in order to simplify the class.

Since code which uses the object can only invoke the public functionality, code which tests the object should only validate the public functionality.

Rick answered 28/6, 2015 at 15:9 Comment(2)
Thanks for your opinion. I also can think of 2 other ways, not sure how good/bad they are. 1. Create a Cacheable interface (which gives public access to members of Class B via methods of class A) and have class A implement it & then test that. 2nd option: Use reflection?Wortman
@Rookie: That's part of the "trickery" needed to directly test private members. The first option changes your object model just for the tests, which leaks testing responsibilities into the object model where it doesn't belong. The second option involves peeking into the internals of the object being tested and coupling directly to them. They may "work" in the sense that they allow you to write a unit test for the private functionality, but are they really a good idea for the maintainability of the code?Rick
D
5

If you are strictly following a TDD approach, private methods and private inner classes are only the result of the refactoring step in the red/green/refactor cycle.

So the approach should be:

  1. Write tests that test the public interface of the class including this caching behavior and write code to pass these tests as you go. This will result in long public methods and some fields that are clearly only related to caching.
  2. Then refactor out some cache related private methods from the long public methods.
  3. Next step should be to move the private cache fields and methods to private inner class. After each refactoring, the tests should pass without modification.

You'll finish with privates methods that are fully tested still, but only via the public interface.

Divorce answered 28/6, 2015 at 15:28 Comment(7)
From the above comments, it seems that testing the inner class via the publicly available methods of the outer class is the recommended approach. What i am thinking is that i am not sure if by testing this way would give me full test coverage for the inner private class's methods. In your above reply, are you suggesting that the cache related fields in the outer class should be made accessible to outer world (when they need not be according to the design..since the cache is internal) for increasing coverage by manipulating these fields externally?Wortman
No, I am not suggesting making anything public for the sake of testing.Divorce
Simply suggesting that you do not write a line of code unless it is to pass a failing test. Then you can refactor some of that code into private methods or classes.Divorce
Is the introduction of this cache for performance reasons? If so I might develop the cache as an independent package-private class with own tests, then plug it in as a refactor step. If it's a cache that is there to affect the behavior of the class A then I'd do as I suggested in the answer.Divorce
yes the cache is there for performance reasons. thanks for your advice, really valuable! in case the cache is tested in a separate class as a package private entity, and then later plugged into the class A, not sure if the final maven tests have to include the testing for the separate class, or does it have to be skipped? sounds like a fragile workflowWortman
No I wouldn't skip the tests for the separate class. With the tests placed in the same package as B they can test it even though it's package-private, but B won't clutter up your publicly visible classes.Divorce
Remember "B will only be used by A" is not a good enough reason to include it as an inner class.Divorce
O
3

Unit testing private methods/classes is not recommended.It is suffice to test the parent method which calls the private methods.Having said that,you can assert / validate the effects of the private class.

for example,

if your inner class is changing some value in database,you can call the parent method and can assert against the db values.This make sure that your private methods/private inner classes get tested.

refer : unit testing private methods

Ot answered 28/6, 2015 at 15:11 Comment(0)
A
1

As said above, you should rethink why you are testing a private method, and I will not go into that any more, as others have given some good information.

However, if there is a need to still test a private method, use reflection to "unlock" the private method, and use Apache Commons to avoid having to write common code (DRY - Don't Repeat Yourself) https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/reflect/package-summary.html

If you need to mock a private method, for example a DB call that is done in a private method of a class. Again the best way is to redesign it so the DB component is abstracted out, but if you HAVE to do it, consider PowerMock

https://code.google.com/p/powermock/wiki/MockPrivate

Anopheles answered 28/6, 2015 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.