Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unit testing c# and issue with testing a private method

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);
            }
        }
    }
}
like image 893
James Radford Avatar asked Nov 06 '13 10:11

James Radford


3 Answers

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.

like image 117
Rob Avatar answered Sep 20 '22 22:09

Rob


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.
like image 29
Anatolii Gabuza Avatar answered Sep 18 '22 22:09

Anatolii Gabuza


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).

like image 37
BartoszKP Avatar answered Sep 21 '22 22:09

BartoszKP