Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: Unittesting with private static members?

I have a class with a construct like this:

private static Dictionary<Contract, IPriceHistoryManager> _historyManagers = new Dictionary<Contract, IPriceHistoryManager>();

and lets say 2 methods like:

 public void AddSth()
 {
    _historManagers.Add(new Contract(), new PriceHistoryManager());
 }

 public int CountDic()
 {
    return _historyManagers.Count(); 
 }

Problem: When running unittests there is no way to "reset" the Dictionary and when i create multiple unittests with seperate instances of the class, then "CountDic" gives unpredictable results and i can't test the listentries.

Question: Is this generally considered a "bad" approach and if yes: how to do it better/more unittestable? And if not: How to unittest this best?

Thx.

like image 563
David Avatar asked Oct 04 '11 11:10

David


2 Answers

Don't be afraid to expose public operations for testing purposes. Paraphrased from "The Art of Unit Testing" by Roy Osherove: When Toyota builds a car, there are testing points available. When Intel builds a chip, there are testing points available. There are interfaces to the car or chip that exist only for testing. Why don't we do the same for software? Would a ResetHistory() method completely destroy your API?

If that answer is yes, then create the method, but make the method internal. You can then use the assembly InternalsVisibleTo to expose the guts to your unit test library. You have a method available to you created 100% for testing, but there's no change to your public API.

like image 192
Jarrett Meyer Avatar answered Oct 06 '22 00:10

Jarrett Meyer


In your example, CountDic isn't unpredictable: it should return one more than before the call to AddSth().

So:

[Test]
public void Test()
{
    var item = new ClassUnderTest();
    int initialCount = item.CountDic();

    item.AddSth();

    int finalCount = item.CountDic();

    Assert.That(finalCount == initialCount + 1);
}

In general, though, testing classes that maintain state can be tricky. Sometimes it's necessary to break out the part of the class that maintains state (in your case, the dictionary) and move it to another class. Then, you can mock that "storage" class and pass it in through a constructor.

like image 41
Jeremy McGee Avatar answered Oct 05 '22 23:10

Jeremy McGee