简体   繁体   中英

How can I correctly unit test methods that depend on each other?

Consider this code:

    private readonly Dictionary<Type, Component> _components = new Dictionary<Type, Component>();

    public Component this [Type type]
    {
        get
        {
            Component component;
            _components.TryGetValue(type, out component);
            return component;
        }
    }

    public void AddComponent(Component component)
    {
        _components.Add(component.GetType(), component);
    }

As you can see, AddComponent adds to the private _components variable. But the only way to test this is happening is by using the indexer. That's fine, but in order to test the indexer I'd have to call AddComponent too!

In other words, in the unit tests for the indexer and AddComponent , each test would have to call both of these methods. It seems like this is creating unnecessary coupling. If there is a bug in the indexer, there is no reason for my TestAddComponent to fail.

What is the best practice here? Do I use reflection to get at _components ? Mocking? Something else?

In my opinion, unit tests shouldn't do reflection to force it's goals . I think that in this kind of test, both should be tested toghether , in the same test. But that is just a point of view.

However, you can make multiple tests, changing the order of the instructions. Try to add multiple, and the access the first, then the last, then one from the middle. Each test, is one scenario, with different order, number of insertions. You can even test exceptional states that must happen... for example, if you try to get something that was not inserted.

I think that unit test exists to mimic usage, or to enforce specification . Not to see if every single bit of the program is right, because that kills flexibility.

Well you have 2 options:

  1. Use reflection or the MSTest private accessor classes to get and set private field values during your test.
  2. Just don't worry about it and test the exposed behaviour, even if that means your test depends on other properties or method that are being tested elsewhere.

As you can probably tell from the wording, my choice would be with #2 - You should test the exposed behaviour . In your case the exposed behaviour that you are testing is:

  • If I use AddComponent then the added component should be accessible through the indexer
  • If I use the indexer, I should be able to access any components that were added through AddComponent

In this case its fairly obvious that these are pretty much the same thing, so we only really have one unit case / exposed behaviour to test here. Yes this unit test covers two different things, but that shouldn't really matter - we aren't trying to test that each method / property behaves as expected, rather we want to test that each exposed behaviour works as expected.


As an alternative, suppose that we go for option 1 and used private reflection to check the state of _components ourselves. In this case the bevahour that we are actually testing is:

  • If I use AddComponent then the added component should be added to _components
  • If I use the indexer, I should be able to access any components that are in _components

Not only are we now testing the internal behaviour of the class (so that if the implementation changes the tests fail, even if the class is working as expected), but we have just doubled the number of tests we are writing.

On top of that, by increasing the complexity of our tests we are increasing the chance that the tests themselves have a bug - for example what if we made a mistake and in test 2. we checked some completely different private field? In this case not only have we made more work for ourselves, but we aren't even testing the actual behaviour that we want to test!

When you're using Microsoft Unit Test Framework, the framework generates a private accessor class. This should allow you to access your private types. Take a look at this page from Microsoft for more info:

http://msdn.microsoft.com/en-us/library/dd293546.aspx

Specially this section: Create unit tests that can access internal, private, and friend methods.

May I suggest using interfaces and or virtual methods and MOQ. That way you can MOQ the calls to the methods you are not wanting to test and make them return what you want.

There's two options here.

  1. Test the functionality together as has already been mentioned.
  2. Modify your class so you can wrap it in a test harness.

Your test harness should be defined with your unit tests and expose the elements which you need to verify that the functionality worked correctly. You should use your test harness instead of the class directly in your unit tests.


public class MyClass
{    
    protected readonly Dictionary<Type, Component> _components = new Dictionary<Type, Component>();

    public Component this [Type type]
    {
        get
        {
            Component component;
            _components.TryGetValue(type, out component);
            return component;
        }
    }

    public void AddComponent(Component component)
    {
        _components.Add(component.GetType(), component);
    }
}

public class MyClassTestHarness : MyClass
{
    public Dictionary<Type, Component> Components
    {
        get
        {
            return _components;
        }
    }
}


Forgot to mention the other option which is dependency injection with mocking. If you were to mock IDictionary then you could verify your test.


public class MyClass
{    
    protected IDictionary _components;

    public MyClass()
    {
         _components = new Dictionary();
    }

    public MyClass(IDictionary components)
    {
         _components = components;
    }

    public Component this [Type type]
    {
        get
        {
            Component component;
            _components.TryGetValue(type, out component);
            return component;
        }
    }

    public void AddComponent(Component component)
    {
        _components.Add(component.GetType(), component);
    }
}

If you want to do it this way, move out of the class into the constructor (ignoring IoC frameworks for the moment) and use the interface to reference the dependency:

class ComponentManager
{
    private readonly IDictionary<Type, Component> _components;

    public ComponentManager()
        : this(new Dictionary<Type, Component>())
    { }

    public ComponentManager(IDictionary<Type, Component> components)
    {
        _components = components;
    }

    public Component this[Type type]
    {
        get
        {
            Component component;
            _components.TryGetValue(type, out component);
            return component;
        }
    }

    public void AddComponent(Component component)
    {
        _components.Add(component.GetType(), component);
    }
}

Now you can mock the dependency and verify the interactions.

However, given the lack of added behavior, I believe the truly practical approach is expose the member directly and throw away the aggregated object's accessors:

class ComponentManager
{
    public Dictionary<Type, Component> Components { get; private set; }

    public ComponentManager()
    {
        Components = new Dictionary<Type, Component>();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM