332

Moderator Note: There are already 39 answers posted here (some have been deleted). Before you post your answer, consider whether or not you can add something meaningful to the discussion. You're more than likely just repeating what someone else has already said.


I occasionally find myself needing to make a private method in a class public just to write some unit tests for it.

Usually this would be because the method contains logic shared between other methods in the class and it's tidier to test the logic on its own, or another reason could be possible be I want to test logic used in synchronous threads without having to worry about threading problems.

Do other people find themselves doing this, because I don't really like doing it?? I personally think the bonuses outweigh the problems of making a method public which doesn't really provide any service outside of the class...

UPDATE

Thanks for answers everyone, seems to have piqued peoples' interest. I think the general consensus is testing should happen via the public API as this is the only way a class will ever be used, and I do agree with this. The couple of cases I mentioned above where I would do this above were uncommon cases and I thought the benefits of doing it was worth it.

I can however, see everyones point that it should never really happen. And when thinking about it a bit more I think changing your code to accommodate tests is a bad idea - after all I suppose testing is a support tool in a way and changing a system to 'support a support tool' if you will, is blatant bad practice.

displayName
  • 13,888
  • 8
  • 60
  • 75
jcvandan
  • 14,124
  • 18
  • 66
  • 103
  • I say no because what happens when you forget to make it private again? Consumers will use it and when you do make it private in later releases, bang! breaking changes. – Dustin Davis Aug 16 '11 at 20:38
  • 2
    "changing a system to 'support a support tool' if you will, is blatant bad practice". Well, yes, sort of. But OTOH if your system does not work well with established tools, maybe something *is* wrong with the system. – Thilo Aug 16 '11 at 23:55
  • 1
    How isn't this duplicate of http://stackoverflow.com/questions/105007/do-you-test-private-method? – Sanghyun Lee Aug 17 '11 at 04:07
  • separation of concern. Code should just do the work, not to test itself. Put the test in other file. – Rudy Aug 17 '11 at 04:42
  • @Sangdol - have you even read that other question? Completely different question... – jcvandan Sep 08 '11 at 13:15
  • This question appears to be off-topic because it is polling for opinions. – Raedwald Jul 01 '13 at 22:37
  • 1
    Not an answer, but you can always access them by reflection. – Zano Jul 27 '15 at 06:22
  • 35
    No, you shouldn't expose them. Instead, you should test that your class **behaves** as it should do, via its public API. And if exposing internals is really the only option (which I doubt), then you should at least make the accessors package protected, so that only classes in the same package (as your test should be) can access them. – JB Nizet Jul 27 '15 at 06:22
  • I don't think you should be exposing the stacks to the outside world. This is an implementation detail and you might move to a new data structure in the future. As far as unit testing is concerned, you should test all corner cases and as long as you get the desired results from the `first`, `last` etc methods, you should be good. You may be tempted to give them the default access modifier but that does not guarantee that other developers won't abuse this. – Chetan Kinger Jul 27 '15 at 06:23
  • 5
    Yes, it is. It is perfectly acceptable to give a method package-private (default) visibility to make it accessible from unit tests. Libraries like Guava even provide an [`@VisibileForTesting`](https://guava-libraries.googlecode.com/svn/tags/release09/javadoc/com/google/common/annotations/VisibleForTesting.html) annotation to make such methods - I recommend you do this so that the reason for the method not being `private` is properly documented. – Boris the Spider Jul 27 '15 at 06:24
  • Make them package private. – Jonathan Jul 27 '15 at 18:31
  • You should never need to make the fields more than package local. You could add a helper class with package local getters for access if you need to. – Peter Lawrey Jul 28 '15 at 21:38
  • @Boris the Spider Your link is broken. Up to date link is here: [@VisibleForTesting annotation](https://guava.dev/releases/31.1-jre/api/docs/com/google/common/annotations/VisibleForTesting.html). – Mustafa Özçetin Jul 08 '23 at 08:31

35 Answers35

193

Note:
This answer was originally posted for the question Is unit testing alone ever a good reason to expose private instance variables via getters? which was merged into this one, so it may be a tad specific to the usecase presented there.

As a general statement, I'm usually all for refactoring "production" code to make it easier to test. However, I don't think that would be a good call here. A good unit test (usually) shouldn't care about the class' implementation details, only about its visible behavior. Instead of exposing the internal stacks to the test, you could test that the class returns the pages in the order you expect it to after calling first() or last().

For example, consider this pseudo-code:

public class NavigationTest {
    private Navigation nav;

    @Before
    public void setUp() {
        // Set up nav so the order is page1->page2->page3 and
        // we've moved back to page2
        nav = ...;
    }

    @Test
    public void testFirst() {
        nav.first();

        assertEquals("page1", nav.getPage());

        nav.next();
        assertEquals("page2", nav.getPage());

        nav.next();
        assertEquals("page3", nav.getPage());
    }

    @Test
    public void testLast() {
        nav.last();

        assertEquals("page3", nav.getPage());

        nav.previous();
        assertEquals("page2", nav.getPage());

        nav.previous();
        assertEquals("page1", nav.getPage());
    }
}
Community
  • 1
  • 1
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • 9
    I completely agree with your answer. Many developers would be tempted to use the default access modifier and justify that good open source frameworks use this strategy so it must be correct. Just because you can doesn't mean you should. +1 – Chetan Kinger Jul 27 '15 at 06:37
  • 7
    Although others have provided useful insights, I have to say this is the solution that I find most appropriate to this problem. It keeps the tests completely agnostic to how the intended behavior is achieved internally. +10! – Gitahi Ng'ang'a Jul 27 '15 at 07:00
  • By the way, is it okay that in the example provided by @Mureinik here, the test methods end up covering more than the actual method under test? For instance, testFirst() calls next(), which is not really the method under test here, first() is. Would it not be cleaner and clearer to just have 1 test method covering all the 4 navigation methods? – Gitahi Ng'ang'a Jul 28 '15 at 11:48
  • @GitahiNg'ang'a Yes, it is OK. You are testing different behaviors of a unit. Both next() and first() are part of that unit. – Darsstar Jul 28 '15 at 12:28
  • @GitahiNg'ang'a It's okay, but if you feel bad about it, you don't need to. `public void testFirst() { nav.next(); nav.first(); assertEquals("page1", nav.getPage()); }` should be just fine, provided you test `next()`, in another test method. – Bart van Nierop Jul 28 '15 at 12:49
  • 1
    While this is the ideal, there are times you need to check a component did something the right way not just that it was successful. This can be harder to black box test. Sometime you have to white box test a component. – Peter Lawrey Jul 28 '15 at 21:41
  • This doesn't really address the question asked. – blank Jan 05 '16 at 08:10
  • 1
    Creating getters just for the sake of unit-testing? This definitely goes against the grain of **OOP/Object Thinking** in that the interface should expose behaviour, not data. – IntelliData Jan 26 '17 at 14:38
155

Personally, I'd rather unit test using the public API and I'd certainly never make the private method public just to make it easy to test.

If you really want to test the private method in isolation, in Java you could use Easymock / Powermock to allow you to do this.

You have to be pragmatic about it and you should also be aware of the reasons why things are difficult to test.

'Listen to the tests' - if it's difficult to test, is that telling you something about your design? Could you refactor to where a test for this method would be trivial and easily covered by testing through the public api?

Here's what Michael Feathers has to say in 'Working Effectively With Legacy Code"

"Many people spend a lot of time trying ot figure out how to get around this problem ... the real answer is that if you have the urge to test a private method, the method shouldn't be private; if making the method public bothers you, chances are, it is because it is part of a separate reponsibility; it should be on another class." [Working Effectively With Legacy Code (2005) by M. Feathers]

blank
  • 17,852
  • 20
  • 105
  • 159
  • 54
    +1 For "Listen to the tests". If you feel a need to test a private method, chances are good that the logic in that method is so complex that it should be in a separate helper class. _Then_ you can test the helper class. – Phil Aug 16 '11 at 13:43
  • 2
    'Listen to the tests' appears to be down. Here is a cached link: http://webcache.googleusercontent.com/search?q=cache:6s3cEcf3oAIJ:static.mockobjects.com/labels/listening%2520to%2520the%2520tests.html+http://static.mockobjects.com/labels/listening%2520to%2520the%2520tests.html&hl=en&gl=au&strip=1 – Jess Telford Aug 29 '11 at 10:01
  • 1
    I agree with @Phil (especially the book reference), but I often find myself in a chicken/egg situation with legacy code. I know this method belongs in another class, however I'm not 100% comfortable refactoring without tests in the first place. – Kevin Aug 15 '17 at 17:52
  • I concur doctor. If you need to test a private method it should probably be in another class. You may note that the other class / helper class that private method is likely to become public / protected. – rupweb Nov 30 '17 at 14:33
  • But extracting the method to another class, is same effect as making it public. Thats why I came here to see. So then you test that new class via public api but existing class api can fail even if new class is correct, just because wrong paramters are passed or result of the new class is used in the wrong way. So I am not sure is that really good. I used to do that - extracting to new class. – Darius.V Feb 09 '23 at 17:07
  • I am thinking - theoretically the private method can be even incorrect as long as the end result of class calling it is correct. On the other hand - then code looking at that incorrect method will not understand - is this a bug or what. – Darius.V Feb 09 '23 at 17:12
  • @Darius.V "just because wrong parameters are passed or result of the new class is used in the wrong way" - then that is the fault of the calling code, not the code under test. A class is not responsible for the behaviour of it's clients. – blank Feb 16 '23 at 16:30
78

As others have said, it is somewhat suspect to be unit testing private methods at all; unit test the public interface, not the private implementation details.

That said, the technique I use when I want to unit test something that is private in C# is to downgrade the accessibility protection from private to internal, and then mark the unit testing assembly as a friend assembly using InternalsVisibleTo. The unit testing assembly will then be allowed to treat the internals as public, but you don't have to worry about accidentally adding to your public surface area.

Mal Ross
  • 4,551
  • 4
  • 34
  • 46
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I use this technique too, but as a very last resort for legacy code usually. If you need to test private code, you have a missing class/the class under test is doing too much. Extract said code into a new class which you can test in isolation. Tricks like this should be used rarely. – Finglas Aug 17 '11 at 16:10
  • But extracting a class and testing only that means that your tests lose the knowledge that the original class is actually _using_ the extracted code. How do you recommend you fill this gap in your tests? And if the answer is to just test the original class's public interface in a way that causes the extracted logic to be used, then why bother extracting it in the first place? (I'm not meaning to sound confrontational here, btw - I've just always wondered how people balance such trade-offs.) – Mal Ross Aug 18 '11 at 07:08
  • @mal I'd have a collaboration test. In other words a mock to verify the new class was invoked correctly. See my answer below. – Finglas Aug 19 '11 at 12:34
  • Could one write a test class that inherits from the class with internal logic that you want to test and provide a public method to use for testing the internal logic? – sholsinger Aug 22 '11 at 18:10
  • 1
    @sholsinger: in C# a public class may not inherit from an internal class. – Eric Lippert Aug 22 '11 at 20:35
62

Lots of answers suggest only testing the public interface, but IMHO this is unrealistic - if a method does something that takes 5 steps, you'll want to test those five steps separately, not all together. This requires testing all five methods, which (other than for testing) might otherwise be private.

The usual way of testing "private" methods is to give every class its own interface, and make the "private" methods public, but not include them in the interface. This way, they can still be tested, but they don't bloat the interface.

Yes, this will result in file- and class-bloat.

Yes, this does make the public and private specifiers redundant.

Yes, this is a pain in the ass.

This is, unfortunately, one of the many sacrifices we make to make code testable. Perhaps a future language (or a even a future version of C#/Java) will have features to make class- and module-testability more convenient; but in the meanwhile, we have to jump through these hoops.


There are some who would argue that each of those steps should be its own class, but I disagree - if they all share state, there is no reason to create five separate classes where five methods would do. Even worse, this results in file- and class-bloat. Plus, it infects the public API of your module - all those classes must necessarily be public if you want to test them from another module (either that, or include the test code in the same module, which means shipping the test code with your product).

Community
  • 1
  • 1
BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • 10
    +1: The best answer for me. If an auto maker does not test a part of its car for the same reason, I am not sure anybody would buy it. Important part of code should be tested even if we should change it to public. – Tae-Sung Shin Aug 19 '11 at 13:20
  • Testing private methods directly only results in tests that are testing things that may not be possible at all in production code that use the public API only. This results in code being adjusted or kept for the sake of testing. Example of this are `null` checks done in code only because otherwise tests fail (due to poorly faked test data or poor mocks) while in production `null` may never be supplied at all. This then becomes self perpetuating as removing such checks makes tests fail, and if the check is there, then perhaps it can happen in production? – john16384 Oct 13 '21 at 00:01
  • Alternatively, the private methods could be added to an interface and explicitly implemented. Then only code which has access to that interface can call them. – Grault Sep 21 '22 at 16:24
  • That's very true that if a public method algorithm is complex, and has multiple steps, that you need to test each of them separately, you need that feature of accessibility to unittest only! However, your suggestion is overkill! I prefer to make these steps public... – Amir Oct 06 '22 at 12:18
30

A unit test should test the public contract, the only way how a class could be used in other parts of the code. A private method is implementation details, you should not test it; as far as public API works correctly, the implementation doesn't matter and could be changed without changes in test cases.

Pang
  • 9,564
  • 146
  • 81
  • 122
kan
  • 28,279
  • 7
  • 71
  • 101
  • Afair, The rare situation I've ever decided to use privates in a test case was when I was verifying an object correctness - if it has closed all JNI handlers. Obviously, it is not exposed as public API, however, if it doesn't happen, a memory leak occurs. But funny, later on I've got rid of it too after I've introduced a memory leak detector as a part of public API. – kan Aug 16 '11 at 13:02
  • 15
    This is flawed logic. The point of unit test is to catch errors sooner than later. By not testing private methods, you are leaving gaps in your testing that may not be discovered until much later. In the case where private method is complex and not easily exercised by a public interface, it should be unit tested. – cmcginty Aug 16 '11 at 22:33
  • 1
    @Thilo: You are right that private methods will be exercised by public methods, but what is the benefit of testing only the public method? I can't see any harm in having test that will flag a problem in the smallest scope possible. – ArturPhilibin Aug 18 '11 at 22:50
  • 2
    @DaSilva_Ireland: It will be difficult to maintain test cases in future. The only real class use case is via public method. I.e. all other code could use the class via public API only. So, if you want to change implementation and private method test case failing, it doesn't mean that you are doing something wrong, moreover, if you are testing only private, but not testing public in full, it will not cover some potential bugs. Ideally a test case should cover all possible cases and only cases of the class real usage. – kan Aug 19 '11 at 08:07
  • @ArturPhilibin the main reason is that testing a stack should be about making sure that pushing n times and then poping n times should result in an empty stack. testing a stack by asserting that push calls the indexer method on an array with a certain name located at a certain memory location and calls it with the correct argument, precisely 7 CPU cycles after the null check was made gives you NO value whatsoever, and it makes your tests brittle and your production code harder to refactor. – sara Mar 24 '16 at 12:54
  • @cmcginty you are testing private methods not making them public but by calling public method which calls private methods. So they are still covered. Just that test setup becomes more complex. So not sure in this case is it better to extract those methods to separate class – Darius.V Feb 09 '23 at 20:02
21

IMO, you should write your tests not making deep assumptions on how your class implemented inside. You probably want to refactor it later using another internal model but still making the same guarantees that previous implementation gives.

Keeping that in mind I suggest you to focus on testing that your contract is still holds no matter what internal implementation your class currently have. Property based testing of your public APIs.

Alex
  • 7,460
  • 2
  • 40
  • 51
  • 8
    I agree that unit tests which don't need to be rewritten after a refactoring of the code are better. And not just because of the time you would spend rewriting the unit tests. A much more important reason is that I consider the most important purpose of unit tests to be that your code still behaves correctly after a refactoring. If you have to rewrite all your unit tests after a refactoring, then you lose that benefit of the unit tests. – kasperd Jul 27 '15 at 10:46
20

How about making it package private? Then your test code can see it (and other classes in your package as well), but it is still hidden from your users.

But really, you should not be testing private methods. Those are implementation details, and not part of the contract. Everything they do should be covered by calling the public methods (if they have code in there that is not exercised by the public methods, then that should go). If the private code is too complex, the class is probably doing too many things and in want of refactoring.

Making a method public is big commitment. Once you do that, people will be able to use it, and you cannot just change them anymore.

Thilo
  • 257,207
  • 101
  • 511
  • 656
17

Update: I have added a more expanded and more complete answer to this question in numerous other places. This is can be found on my blog.

If I ever need to make something public to test it, this usually hints that the system under test is not following the Single Reponsibility Principle. Hence there is a missing class that should be introduced. After extracting the code into a new class, make it public. Now you can test easily, and you are following SRP. Your other class simply has to invoke this new class via composition.

Making methods public/using langauge tricks such as marking code as visible to test assembilies should always be a last resort.

For example:

public class SystemUnderTest
{
   public void DoStuff()
   {
      // Blah
      // Call Validate()
   }

   private void Validate()
   {
      // Several lines of complex code...
   }
}

Refactor this by introducing a validator object.

public class SystemUnderTest
{
    public void DoStuff()
    {
       // Blah
       validator.Invoke(..)
    }
}

Now all we have to do is test that the validator is invoked correctly. The actual process of validation (the previously private logic) can be tested in pure isolation. There will be no need for complex test set up to ensure this validation passes.

Finglas
  • 15,518
  • 10
  • 56
  • 89
  • 3
    I personally think SRP can be taken too far, for example if I write an account service that deals with updating/creating/deleting accounts in an application, are you telling me you should create a separate class for each operation? And what about validation for example, is it really worth extracting validation logic to another class when it will never be used anywhere else? imo that just makes code less readable, less concise and packs your application with superfluous classes! – jcvandan Aug 17 '11 at 16:18
  • 2
    It all depends on the scenario. One person's definition of "doing one thing" may be diferent to anothers. In this case for yourself, clearly the private code you wish to test is complex/a pain to set up. The very fact you wish to test it proves it is doing too much. Therefore yes, having a new object will be ideal. – Finglas Aug 17 '11 at 16:23
  • 2
    As for making your code less readable I disagree strongly. Having a class for validation is easier to read than having the validation embedded in the other object. You still have the same amount of code, it's just not nested in one large class. I'd rather have several class which do one thing very well, than one large class. – Finglas Aug 17 '11 at 16:25
  • 4
    @dormisher, regarding your account service, think of the SRP as a "single reason to change." If your CRUD operations would naturally change together, then they would fit the SRP. Normally, you'd change these because the database schema might change, which would naturally affect insert and update (although perhaps not always delete). – Anthony Pegram Aug 22 '11 at 21:12
  • 1
    @finglas what you think about this: http://stackoverflow.com/questions/29354729/how-to-test-2-methods-with-common-logic . I don't feel like testing the private method, so maybe I should not separate it into a class, but it is being used in 2 other public methods, so I would end up testing twice the same logic. – Rodrigo Ruiz Mar 30 '15 at 22:11
  • @jcvandan same comment above. (couldn't reference 2 people in the same comment) – Rodrigo Ruiz Mar 30 '15 at 22:12
  • @RodrigoRuiz I have left an answer on your other question. – Finglas Apr 03 '15 at 10:39
15

Some great answers. One thing I didn't see mentioned is that with test-driven development (TDD), private methods are created during the refactoring phase (look at Extract Method for an example of a refactoring pattern), and should therefore already have the necessary test coverage. If done correctly (and of course, you're going to get a mixed bag of opinions when it comes to correctness), you shouldn't have to worry about having to make a private method public just so that you can test it.

csano
  • 13,266
  • 2
  • 28
  • 45
13

If you are using C# you can make method internal. That way you don't pollute public API.

Then add attribute to dll

[assembly: InternalsVisibleTo("MyTestAssembly")]

Now all the methods are visible in your MyTestAssembly project. Maybe not perfect, but better then making private method public just to test it.

Piotr Perak
  • 10,718
  • 9
  • 49
  • 86
12

Why not split out the stack management algorithm into a utility class? The utility class can manage the stacks and provide public accessors. Its unit tests can be focussed on implementation detail. Deep tests for algorithmically tricky classes are very helpful in wrinkling out edge cases and ensuring coverage.

Then your current class can cleanly delegate to the utility class without exposing any implementation details. Its tests will relate to the pagination requirement as others have recommended.

11

In java, there's also the option of making it package private (ie leaving off the visibility modifier). If your unit tests are in the same package as the class being tested it should then be able to see these methods, and is a bit safer than making the method completely public.

Tom Jefferys
  • 13,090
  • 2
  • 35
  • 36
10

Private methods are usually used as "helper" methods. Therefore they only return basic values and never operate on specific instances of objects.

You have a couple of options if you want to test them.

  • Use reflection
  • Give the methods package access

Alternatively you could create a new class with the helper method as a public method if it is a good enough candidate for a new class.

There is a very good article here on this.

adamjmarkham
  • 2,150
  • 2
  • 18
  • 26
  • 1
    Fair comment. Was just an option, perhaps a bad one. – adamjmarkham Aug 20 '11 at 18:22
  • @Finglas Why is testing private code using reflection a bad idea? – Anshul Jun 24 '14 at 19:00
  • @Anshul private methods are implementation details and not behaviour. In other words, they change. Names change. Parameters change. Contents change. This means these tests will break all the time. Public methods on the other hand are far more stable in terms of API so are more resilient. Providing your tests on public methods check for behaviour and not implementation details, you should be good. – Finglas Jun 28 '14 at 07:56
  • 1
    @Finglas Why wouldn't you want to test implementation details? It's code that needs to work, and if it changes more often, you expect it to break more often, which is more reason to test it more than the public API. After your code base is stable, let's say sometime in the future someone comes along and changes a private method which breaks how your class works internally. A test that tests the internals of your class would immediately tell them that they broke something. Yes it would make maintaining your tests harder, but that's something you come to expect from full coverage testing. – Anshul Jun 28 '14 at 18:52
  • @Anshul there is no point doing what you are suggesting. Private methods can only be called via public methods, therefore test via public methods and you cover the internals of those methods. You get the benefit of stable tests, without the need to test "internals". You still get "full coverage" without the brittle nature. If the private method isn't called from anyone it can safely be deleted. Testing implementation details is wrong as I've said. I want to test that I get an empty response back, not that an array contains 0 elements for example. Public methods don't make that assumption. – Finglas Jun 30 '14 at 17:59
  • 1
    @Finglas Testing implementation details is not wrong. That's your stance on it, but this is a widely debated topic. Testing the internals gives you a few advantages, even though they may not be required for full coverage. Your tests are more focused because you're testing the private methods directly rather than going through the public API, which may conflate your test. Negative testing is easier because you can manually invalidate the private members and make sure that the public API returns appropriate errors in response to the inconsistent internal state. There are more examples. – Anshul Jun 30 '14 at 19:01
  • @Finglas As to how your class can get in an inconsistent internal state without using the public API, I attribute that to not having full awareness of a class that becomes complex enough. In my experience, no matter how careful one is, there are edge cases we just don't anticipate, which end up being exposed in our public API. Because private method tests would be more focused, they'd be more likely to catch those edge case errors. I also believe that future-proofing your class is easier by testing the private methods because of a shorter feedback loop for the future developer. – Anshul Jun 30 '14 at 19:20
  • @Anshul I'm ending my replies now, but I will leave you with one point. If you need to test private methods - you are violating the SRP. You have an object that is waiting to escape. This is not just my stance, this is many peoples opinion. Correctly so. Apply the SOLID principles, and testing should be easier. – Finglas Jul 03 '14 at 21:51
7

Use reflection to access the private variables if you need to.

But really, you don't care about the internal state of the class, you just want to test that the public methods return what you expect in the situations you can anticipate.

Daniel Alexiuc
  • 13,078
  • 9
  • 58
  • 73
6

in terms of unit testing, you should definitely not add more methods; I believe you would better make a test case just about your first() method, which would be called before each test; then you can call multiple times the - next(), previous() and last() to see if the outcomes match your expectation. I guess if you don't add more methods to your class (just for testing purposes), you would stick to the "black box" principle of testing;

Johny
  • 315
  • 4
  • 9
6

You should never ever ever let your tests dictate your code. I'm not speaking about TDD or other DDs I mean, exactly what your asking. Does your app need those methods to be public. If it does then test them. If it does not then then don't make them public just for testing. Same with variables and others. Let your application's needs dictate the code, and let your tests test that the need is met. (Again I don't mean testing first or not I mean changing a classes structure to meet a testing goal).

Instead you should "test higher". Test the method that calls the private method. But your tests should be testing your applications needs and not your "implementation decisions".

For example (bod pseudo code here);

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

There is no reason to test "add" you can test "books" instead.

Never ever let your tests make code design decisions for you. Test that you get the expected result, not how you get that result.

coteyr
  • 273
  • 2
  • 7
  • 2
    "Never ever let your tests make code design decisions for you" - I must disagree. Being hard to test is a code smell. If you cannot test it, how do you know it's not broken? – Alfred Armstrong Jul 28 '15 at 14:53
  • For clarification, I agree that you should not change the external API design of a library, for example. But you might need to refactor to make it more testable. – Alfred Armstrong Jul 28 '15 at 15:00
  • I have found that if you need to refactor to test, that's not the code smell you should worry about. You have something else wrong. Of course that's personal experience, and we could both probably make an argument with solid data both ways. I just have never had "hard to test" that wasn't "poor design" in the first place. – coteyr Jul 28 '15 at 22:41
  • This solution is nice for a method like **books** that returns a value - you can check the return type in an **assert**; but how would you go about checking a **void** method? – IntelliData Jan 26 '17 at 14:41
  • 1
    void methods do something or they don't need to exist. Check the something that they do, – coteyr Mar 30 '20 at 23:04
6

In your update you say that it's good to just test using the public API. There is actually two schools here.

  1. Black box testing

    The black box school says that the class should be considered as a black box that no one can see the implementation inside it. The only way to test this is through the public API -- just like the user's of the class will be using it.

  2. white box testing.

    The white box school thinks it naturally to use the knowledge about the implementation of the class and then test the class to know that it works as it should.

I really cannot take side in the discussion. I just thought it would be interesting to know that there are two distinct ways to test a class (or a library or whatever).

Sathwick
  • 1,311
  • 1
  • 8
  • 10
bengtb
  • 61
  • 1
5

I would say it is a bad idea for I am not sure whether you get any benefit and potentially problems down the line. If you are changing the contract of a calls, just to test a private method, you're not testing the class in how it would be used, but creating an artificial scenario which you never intended to happen.

Furthermore, by declaring the method as public, what's to say that in six months time (after forgetting that the only reason for making a method public is for testing) that you (or if you've handed the project over) somebody completely different won't use it, leading to potentially unintended consequences and/or a maintenance nightmare.

beny23
  • 34,390
  • 5
  • 82
  • 85
  • 1
    what about if the class is the implementation of a service contract and it is only ever used via it's interface - this way even if the private method has been made public, it will not be called anyway as it is not visible – jcvandan Aug 16 '11 at 09:24
  • I'd still would want to test the class using the public API (interface), because otherwise if you refactor the class to split up the internal method, then you'd have to change the tests, which means you would have to spend some effort ensuring that the new tests work the same as the old tests. – beny23 Aug 16 '11 at 09:50
  • Also, if the only way for ensuring that the test coverage include every edge case inside a private method is to call it directly, it may be indicative that the class complexity warrants refactoring to simplify it. – beny23 Aug 16 '11 at 09:53
  • @dormisher - If the class is only ever used via the interface, then you only need to test that the public interface method(s) behave correctly. If the public methods do what they're meant to do, why do you care about the private ones? – Andrzej Doyle Aug 17 '11 at 07:16
  • @Andrzej - I suppose I shouldn't really but there are situations where I believe it could be valid, e.g. a private method which validates model state, it may be worth testing a method like this - but a method like this should be testable via the public interface anyway – jcvandan Aug 17 '11 at 08:39
5

First see if the method ought to be extracted into another class and made public. If that's not the case, make it package protected and in Java annotate with @VisibleForTesting.

Noel Yap
  • 18,822
  • 21
  • 92
  • 144
5

Private methods that you want to test in isolation are an indication that there's another "concept" buried in your class. Extract that "concept" to its own class and test it as a separate "unit".

Take a look at this video for a really interesting take on the subject.

Jordão
  • 55,340
  • 13
  • 112
  • 144
4

There are actually situations when you should do this (e.g. when you're implementing some complex algorithms). Just do it package-private and this will be enough. But in most cases probably you have too complex classes which requires factoring out logic into other classes.

Stanislav Bashkyrtsev
  • 14,470
  • 7
  • 42
  • 45
2

I usually leave those methods as protected and place the unit test within the same package (but in another project or source folder), where they can access all the protected methods because the class loader will place them within the same namespace.

hiergiltdiestfu
  • 2,339
  • 2
  • 25
  • 36
2

No, because there are better ways of skinning that cat.

Some unit test harnesses rely on macros in the class definition which automagically expand to create hooks when built in test mode. Very C style, but it works.

An easier OO idiom is to make anything you want to test "protected" rather than "private". The test harness inherits from the class under test, and can then access all protected members.

Or you go for the "friend" option. Personally this is the feature of C++ I like least because it breaks the encapsulation rules, but it happens to be necessary for how C++ implements some features, so hey ho.

Anyway, if you're unit testing then you're just as likely to need to inject values into those members. White box texting is perfectly valid. And that really would break your encapsulation.

Graham
  • 1,655
  • 9
  • 19
2

In .Net there is a special class called PrivateObject deigned specifically to allow you to access private methods of a class.

See more about it on the MSDN or here on Stack Overflow

(I am wondering that no one has mentioned it so far.)

There are situations though which this is not enough, in which case you will have to use reflection.

Still I would adhere to the general recommendation not to test private methods, however as usual there are always exceptions.

Community
  • 1
  • 1
yoel halb
  • 12,188
  • 3
  • 57
  • 52
2

Very answered question.
IHMO, the excellent answer from @BlueRaja - Danny Pflughoeft is one of the best.

Lots of answers suggest only testing the public interface, but IMHO this is unrealistic - if a method does something that takes 5 steps, you'll want to test those five steps separately, not all together. This requires testing all five methods, which (other than for testing) might otherwise be private.


Above all, I want to stress that the question "should we make a private method public to unit test it" is a question which an objectively correct answer depends on multiple parameters.
So I think that in some cases we have not to and in others we should.


Making public a private method or extracting the private method as a public method in another class (new or existing)?

It is rarely the best way.
A unit test has to test the behavior of one API method/function.
If you test a public method that invokes another public method belonging to the same component, you don't unit test the method. You test multiple public methods at the same time.
As a consequence, you may duplicate tests, test fixtures, test assertions, the test maintenance and more generally the application design.
As the tests value decreases, they often lose interest for developers that write or maintain them.

To avoid all this duplication, instead of making the private method public method, in many cases a better solution is extracting the private method as a public method in a new or an existing class.
It will not create a design defect.
It will make the code more meaningful and the class less bloat.
Besides, sometimes the private method is a routine/subset of the class while the behavior suits better in a specific structure.
At last, it also makes the code more testable and avoid tests duplication.
We can indeed prevent tests duplication by unit testing the public method in its own test class and in the test class of the client classes, we have just to mock the dependency.

Mocking private methods?

While it is possible by using reflection or with tools as PowerMock, I think that it is often a way to bypass a design issue.
A private member is not designed to be exposed to other classes.
A test class is a class as another. So we should apply the same rule for it.

Mocking public methods of the object under test?

You may want to change the modifier private to public to test the method.
Then to test the method that uses this refactored public method, you may be tempted to mock the refactored public method by using tools as Mockito (spy concept) but similarly to mocking private methods, we should avoid to mock the object under test.

The Mockito.spy() documentation says it itself :

Creates a spy of the real object. The spy calls real methods unless they are > > stubbed.

Real spies should be used carefully and occasionally, for example when dealing with legacy code.

By experience, using spy() generally decreases the test quality and its readability.
Besides, it is much more error-prone as the object under test is both a mock and a real object.
It is often the best way to write an invalid acceptance test.


Here is a guideline I use to decide whether a private method should stay private or be refactored.

Case 1) Never make a private method public if this method is invoked once.
It is a private method for a single method. So you could never duplicate test logic as it is invoke once.

Case 2) You should wonder whether a private method should be refactored as a public method if the private method is invoked more than once.

How to decide ?

  • The private method doesn't produce duplication in the tests.
    -> Keep the method private as it is.

  • The private method produces duplication in the tests. That is, you need to repeat some tests, to assert the same logic for each test that unit-tests public methods using the private method.
    -> If the repeated processing may make part of the API provided to clients (no security issue, no internal processing, etc...), extract the private method as a public method in a new class.
    -> Otherwise, if the repeated processing has not to make part of the API provided to clients (security issue, internal processing, etc...), don't widen the visibility of the private method to public.
    You may leave it unchanged or move the method in a private package class that will never make part of the API and would be never accessible by clients.


Code examples

The examples rely on Java and the following libraries : JUnit, AssertJ (assertion matcher) and Mockito.
But I think that the overall approach is also valid for C#.

1) Example where the private method doesn't create duplication in the test code

Here is a Computation class that provides methods to perform some computations.
All public methods use the mapToInts() method.

public class Computation {

    public int add(String a, String b) {
        int[] ints = mapToInts(a, b);
        return ints[0] + ints[1];
    }

    public int minus(String a, String b) {
        int[] ints = mapToInts(a, b);
        return ints[0] - ints[1];
    }

    public int multiply(String a, String b) {
        int[] ints = mapToInts(a, b);
        return ints[0] * ints[1];
    }

    private int[] mapToInts(String a, String b) {
        return new int[] { Integer.parseInt(a), Integer.parseInt(b) };
    }

}

Here is the test code :

public class ComputationTest {

    private Computation computation = new Computation();

    @Test
    public void add() throws Exception {
        Assert.assertEquals(7, computation.add("3", "4"));
    }

    @Test
    public void minus() throws Exception {
        Assert.assertEquals(2, computation.minus("5", "3"));
    }

    @Test
    public void multiply() throws Exception {
        Assert.assertEquals(100, computation.multiply("20", "5"));
    }

}

We could see that the invocation of the private method mapToInts() doesn't duplicate the test logic.
It is an intermediary operation and it doesn't produce a specific result that we need to assert in the tests.

2) Example where the private method creates undesirable duplication in the test code

Here is a MessageService class that provides methods to create messages.
All public methods use the createHeader() method :

public class MessageService {

    public Message createMessage(String message, Credentials credentials) {
        Header header = createHeader(credentials, message, false);
        return new Message(header, message);
    }

    public Message createEncryptedMessage(String message, Credentials credentials) {
        Header header = createHeader(credentials, message, true);
        // specific processing to encrypt
        // ......
        return new Message(header, message);
    }

    public Message createAnonymousMessage(String message) {
        Header header = createHeader(Credentials.anonymous(), message, false);
        return new Message(header, message);
    }

    private Header createHeader(Credentials credentials, String message, boolean isEncrypted) {
        return new Header(credentials, message.length(), LocalDate.now(), isEncrypted);
    }

}

Here is the test code :

import java.time.LocalDate;

import org.assertj.core.api.Assertions;
import org.junit.Test;

import junit.framework.Assert;

public class MessageServiceTest {

    private MessageService messageService = new MessageService();

    @Test
    public void createMessage() throws Exception {
        final String inputMessage = "simple message";
        final Credentials inputCredentials = new Credentials("user", "pass");
        Message actualMessage = messageService.createMessage(inputMessage, inputCredentials);
        // assertion
        Assert.assertEquals(inputMessage, actualMessage.getMessage());
        Assertions.assertThat(actualMessage.getHeader())
                  .extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
                  .containsExactly(inputCredentials, 9, LocalDate.now(), false);
    }

    @Test
    public void createEncryptedMessage() throws Exception {
        final String inputMessage = "encryted message";
        final Credentials inputCredentials = new Credentials("user", "pass");
        Message actualMessage = messageService.createEncryptedMessage(inputMessage, inputCredentials);
        // assertion
        Assert.assertEquals("Aç4B36ddflm1Dkok49d1d9gaz", actualMessage.getMessage());
        Assertions.assertThat(actualMessage.getHeader())
                  .extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
                  .containsExactly(inputCredentials, 9, LocalDate.now(), true);
    }

    @Test
    public void createAnonymousMessage() throws Exception {
        final String inputMessage = "anonymous message";
        Message actualMessage = messageService.createAnonymousMessage(inputMessage);
        // assertion
        Assert.assertEquals(inputMessage, actualMessage.getMessage());
        Assertions.assertThat(actualMessage.getHeader())
                  .extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
                  .containsExactly(Credentials.anonymous(), 9, LocalDate.now(), false);
    }

}

We could see that the invocation of the private method createHeader() creates some duplication in the test logic.
createHeader() creates indeed a specific result that we need to assert in the tests.
We assert 3 times the header content while a single assertion should be required.

We could also note that the asserting duplication is close between the methods but not necessary the same as the private method has a specific logic : Of course, we could have more differences according to the logic complexity of the private method.
Besides, at each time we add a new public method in MessageService that calls createHeader(), we will have to add this assertion.
Note also that if createHeader() modifies its behavior, all these tests may also need to be modified.
Definitively, it is not a very good design.

Refactoring step

Suppose we are in a case where createHeader() is acceptable to make part of the API.
We will start by refactoring the MessageService class by changing the access modifier of createHeader() to public :

public Header createHeader(Credentials credentials, String message, boolean isEncrypted) {
    return new Header(credentials, message.length(), LocalDate.now(), isEncrypted);
}

We could now test unitary this method :

@Test
public void createHeader_with_encrypted_message() throws Exception {
  ...
  boolean isEncrypted = true;
  // action
  Header actualHeader = messageService.createHeader(credentials, message, isEncrypted);
  // assertion
  Assertions.assertThat(actualHeader)
              .extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
              .containsExactly(Credentials.anonymous(), 9, LocalDate.now(), true);
}

@Test
public void createHeader_with_not_encrypted_message() throws Exception {
  ...
  boolean isEncrypted = false;
  // action
  messageService.createHeader(credentials, message, isEncrypted);
  // assertion
  Assertions.assertThat(actualHeader)
              .extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
              .containsExactly(Credentials.anonymous(), 9, LocalDate.now(), false);

}

But what about the tests we write previously for public methods of the class that use createHeader() ?
Not many differences.
In fact, we are still annoyed as these public methods still need to be tested concerning the returned header value.
If we remove these assertions, we may not detect regressions about it.
We should be able to naturally isolate this processing but we cannot as the createHeader() method belongs to the tested component.
That's why I explained at the beginning of my answer that in most of cases, we should favor the extraction of the private method in another class to the change of the access modifier to public.

So we introduce HeaderService :

public class HeaderService {

    public Header createHeader(Credentials credentials, String message, boolean isEncrypted) {
        return new Header(credentials, message.length(), LocalDate.now(), isEncrypted);
    }

}

And we migrate the createHeader() tests in HeaderServiceTest.

Now MessageService is defined with a HeaderService dependency:

public class MessageService {

    private HeaderService headerService;

    public MessageService(HeaderService headerService) {
        this.headerService = headerService;
    }

    public Message createMessage(String message, Credentials credentials) {
        Header header = headerService.createHeader(credentials, message, false);
        return new Message(header, message);
    }

    public Message createEncryptedMessage(String message, Credentials credentials) {
        Header header = headerService.createHeader(credentials, message, true);
        // specific processing to encrypt
        // ......
        return new Message(header, message);
    }

    public Message createAnonymousMessage(String message) {
        Header header = headerService.createHeader(Credentials.anonymous(), message, false);
        return new Message(header, message);
    }

}

And in MessageService tests, we don't need any longer to assert each header value as this is already tested.
We want to just ensure that Message.getHeader() returns what HeaderService.createHeader() has returned.

For example, here is the new version of createMessage() test :

@Test
public void createMessage() throws Exception {
    final String inputMessage = "simple message";
    final Credentials inputCredentials = new Credentials("user", "pass");
    final Header fakeHeaderForMock = createFakeHeader();
    Mockito.when(headerService.createHeader(inputCredentials, inputMessage, false))
           .thenReturn(fakeHeaderForMock);
    // action
    Message actualMessage = messageService.createMessage(inputMessage, inputCredentials);
    // assertion
    Assert.assertEquals(inputMessage, actualMessage.getMessage());
    Assert.assertSame(fakeHeaderForMock, actualMessage.getHeader());
}

Note the assertSame() use to compare the object references for the headers and not the contents.
Now, HeaderService.createHeader() may change its behavior and return different values, it doesn't matter from the MessageService tests point of view.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • 1
    Wow, that's a lot of overhead for some simple test. Well what I (and, thankfully, my company too) does is to put tests in the same package as the tested code, only in a parallel directory structure, and make the methods to be tested package-private. No changes to public interface required, but still providing full testability. Clean and easy, IMO of course. – Franz D. Jul 05 '18 at 14:29
2

I tend to agree that the bonuses of having it unit tested outweigh the problems of increasing the visibility of some of the members. A slight improvement is to make it protected and virtual, then override it in a test class to expose it.

Alternatively, if it's functionality you want to test separately does it not suggest a missing object from your design? Maybe you could put it in a separate testable class...then your existing class just delegates to an instance of this new class.

IanR
  • 4,703
  • 3
  • 29
  • 27
2

I generally keep the test classes in the same project/assembly as the classes under test.
This way I only need internal visibility to make functions/classes testable.

This somewhat complicates your building process, which needs to filter out the test classes. I achieve this by naming all my test classes TestedClassTest and using a regex to filter those classes.

This of course only applies to the C# / .NET part of your question

yas4891
  • 4,774
  • 3
  • 34
  • 55
2

I will often add a method called something like validate, verify, check, etc, to a class so that it can be called to test the internal state of an object.

Sometimes this method is wrapped in an ifdef block (I write mostly in C++) so that it isn't compiled for release. But it's often useful in release to provide validation methods that walk the program's object trees checking things.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
2

Guava has a @VisibleForTesting annotation for marking methods that have enlarged scope (package or public) that they would otherwise. I use a @Private annotation for the same thing.

While the public API must be tested, sometimes it's convenient and sensible to get at stuff that wouldn't normally be public.

When:

  • a class is made significantly less readable, in toto, by breaking it up into multiple classes,
  • just to make it more testable,
  • and providing some test access to the innards would do the trick

it seems like religion is trumping engineering.

Ed Staub
  • 15,480
  • 3
  • 61
  • 91
1

When you have complex logic in some method that you want to test, this is a good indicator that the class violates the single responsibility principle.

A good solution for that would be to:

  1. Create an interface for the functionality the original method had.
  2. Implement and test that interface in a class.
  3. Inject the interface into the original class.
Kolyunya
  • 5,973
  • 7
  • 46
  • 81
1

The point of the unit test is to confirm the workings of the public api for that unit. There should be no need to make a private method exposed only for testing, if so then your interface should be rethought. Private methods can be thought as 'helper' methods to the public interface and therefore are tested via the public interface as they would be calling into the private methods.

The only reason I can see that you have a 'need' to do this is that your class is not properly designed for testing in mind.

Chris Johnson
  • 2,631
  • 2
  • 18
  • 17
1

recently I have come to the same thinking when refactoring a big method(> 200 lines). I successfully split the big method into smaller methods for each logical step, so it is easy to reason about.

when comes to the refactored out small private methods, I wonder should I test them separately since if I only test the public method, I'm still testing the big method, testing is not benefiting from the refactoring at all

after some more thought I came to some realizations:

  1. if all the small methods are private and can't be reused by others, maybe I am doing something wrong: I am not pulling the right abstraction from the code, I am only splitting the big methods treating them like lines/strings, not like mental barrier
  2. when I came to the right small methods(I refactored again, completely changing the small methods), and move the small methods into another class exposed as public methods for others to use, now I can test them(and I should test them, they will be used more and deserver the attention)

Summary:

I still have many small private methods, but they are sharing lots of public methods and the small methods is really small(3-4 lines, mostly function call), but I will not test them, I only need to test the shared public method now in another class

danny
  • 1,095
  • 2
  • 12
  • 27
1

As is extensively noted by others' comments, unit tests should focus on the public API. However, pros/cons and justification aside, you can call private methods in a unit test by using reflection. You would of course need to make sure your JRE security allows it. Calling private methods is something that the Spring Framework employs with it's ReflectionUtils (see the makeAccessible(Method) method).

Here's a small example class with a private instance method.

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

And an example class that executes the private instance method.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

Executing B, will print Doing something private. If you really need it, reflection could be used in unit tests to access private instance methods.

Go Dan
  • 15,194
  • 6
  • 41
  • 65
0

Personally, I have the same problems when testing private methods and this is because the some of the testing tools are limited. It's not good your design to be driven by limited tools if they don't response to your need change the tool not the design. Because your asking for C# i can't propose good testing tools, but for Java there are two powerful tools: TestNG and PowerMock, and you can find corresponding testing tools for .NET platform

Xoke
  • 984
  • 2
  • 11
  • 17
0

Its all about pragmatism. Your unit tests are a client to the code in a way and in order to achieve a good level of code coverage you need to make your code very testable. Your solution will be a potential failure if the testing code is vastly comlplex in order for you to be able to setup the necessary corner-cases without effective public seams withing your code. Using IoC helps in this matter too.

Dean Chalk
  • 20,076
  • 6
  • 59
  • 90