5

I want to test GetParameters() to assert the returned value includes "test=" within the value. Unfortunately the method responsible for this is private. Is there a way I can provide test coverage for this? The problem I have is highlighted below:

if (info.TagGroups != null)

The problem is that info.TagGroups equals null in my test

Thanks,

Test

[Test]
public void TestGetParameters()
{
    var sb = new StringBuilder();
    _renderer.GetParameters(sb);
    var res = sb.ToString();
    Assert.IsTrue(res.IndexOf("test=") > -1, "blabla");
}   

Implementation class to test

internal void GetParameters(StringBuilder sb)
{
    if (_dPos.ArticleInfo != null)
    {
        var info = _dPos.ArticleInfo;

        AppendTag(sb, info);

    }
}

private static void AppendTag(StringBuilder sb, ArticleInfo info)
{
    if (info.TagGroups != null)  // PROBLEM - TagGroups in test equals null
    {
        foreach (var tGroups in info.TagGroups)
        {
            foreach (var id in tGroups.ArticleTagIds)
            {
                sb.AppendFormat("test={0};", id);
            }
        }
    }
}
James Radford
  • 1,815
  • 4
  • 25
  • 40
  • No one will tell you exactly how to make `TagGroups` non null - how can anyone guess the implementations of `ArticleInfo`? If you can't inject values easily to ensure it's not `null` then it means your class is not testable. In this case again I recommend TDD approach (see my answer). – BartoszKP Nov 06 '13 at 10:29
  • Every time someone asks about how to test private method holy war begins ). In real life there are legacy systems where you can't afford much refactoring. James asked about how to deal with the problem and reflection is the answer. – nikita Nov 06 '13 at 10:50
  • 1
    @nikita Yes we all know about "real life" and "legacy". But there was nothing about legacy code in this context. In general improving the design is the answer. Reflection *only* for legacy code, or when you really have no other option. Not sure why this exceptional solution is constantly being advocated as a good one in general, but being backed up only by this single, exceptional argument. – BartoszKP Nov 06 '13 at 10:58

4 Answers4

5

Testing private methods directly (as opposed to indirectly through your class's public API) is generally a bad idea. However, if you need to, you can do this via reflection.

A better choice would be to refactor your code to be more testable. There are any number of ways to do this, here's one option:

It looks like the logic you want to test is in AppendTag. This is a static method, and doesn't modify any state, so why not make it public and callable by tests directly?

In the same way, you could also make GetParameters public static by giving it an additional ArticleInfo parameter.

Rob
  • 4,327
  • 6
  • 29
  • 55
3

You can make internals visible to your testing project: in AssemblyInfo use InternalsVisibleToAttribute.


DISCLAIMER:
Testing private/internal methods is bad! It's not TDD and in most cases this is a sign to stop and rethink design.
BUT in case you're forced to do this (legacy code that should be tested) - you can use this approach.
Anatolii Gabuza
  • 6,184
  • 2
  • 36
  • 54
2

Yes, you need just to inject appropriate contents of _dPos.ArticleInfo into your class. This way you can ensure that all paths will be covered in the private method. Another possibility is to rewrite this simple code using TDD - this will give you nearly 100% coverage.

Also note, that in general you shouldn't really care about how the private method works. As long as your class exposes desired behavior correctly everything is fine. Thinking too much about internals during testing makes your tests coupled with the details of your implementation and this makes tests fragile and hard to maintain.

See this and this for example, on why not to test implementation details.

UPDATE:

I really don't get why people always go with the InternalsVisible approach and other minor arguments encouraging testing of private methods. I'm not saying that it's always bad. But most of the time it's bad. The fact that some exceptional cases exist that force you to test private methods doesn't mean that this should be advised in general. So yes, do present a solution that makes testing implementation details possible, but after you've describes what is a valid approach in general. There are tons of blog posts and SO questions on this matter, why do we have to go through this over and over again? (Rhetorical question).

Community
  • 1
  • 1
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
0

In your properties/assemblyinfo.cs add:

  [assembly: InternalsVisibleTo("**Insert your test project here**")]

EDIT Some people like testing internals and private methods and others don't. I personally prefer it and therefor I use InternalsVisibleTo. However, Internals are internal for a reason and this should therefore be used with caution.

  • Testing private methods is a bad practice, leading to fragile and unmaintainable tests. – BartoszKP Nov 06 '13 at 10:27
  • I've added this line into my test class. How does this change my test? – James Radford Nov 06 '13 at 10:29
  • @StijndeVoogd I've given you an explicit example why this is wrong. Too much coupling leads to problems. – BartoszKP Nov 06 '13 at 10:31
  • @JamesRadford You should add this line to the AssemblyInfo.cs file. You can find this in your project/properties folder. Make sure that you put it in the same project as the internal method. – Stijn de Voogd Nov 06 '13 at 10:32
  • @BartoszKP even a private method does has a function in your program. Testing them can be valueble. There is nothing wrong with testing private or internal functionality/behavoir in unittests. If they are good designed, they are just a unit of work to be tested. Testing them to get 100% coverage is wrong. – Peter Nov 06 '13 at 10:35
  • 1
    @StijndeVoogd lol, sorry, but it's not a matter of opinion whether coupling is increased or not. It is, as you tie your tests to the implementation details. And so, you're no longer free to change the implementation without changing the behaviour without fixing the tests over and over. This breaks encapsulation. Implementation details should be unimportant as long as the public interface exposes the correct behavior. Please refer to some example links I've given in my answer. – BartoszKP Nov 06 '13 at 10:39