michael
michael

Reputation: 15282

Moq + Unit Testing -- How can I pass an Action to my class to test if that Action gets invoked?

Basically, I have a method on my class that invokes an Action<T> if certain conditions are met. How can I unit test to ensure that the action is invoked?

public class MyClass<T>
{
    private IDBService _dbService;

    private Action<T> _action;

    public MyClass(IDBService dbService, Action<T> action)
    {
        if (dbService == null) throw new ArgumentNullException("dbService");
        if (action == null) throw new ArgumentNullException("action");

        _dbService = dbService;
        _action = action;
    }

    public void CallActionIfPossible(T param)
    {
        if (_dbService.IsTopUser)
            action(param);
    }
}

Upvotes: 3

Views: 7048

Answers (2)

James World
James World

Reputation: 29776

Jason's answer is good, but one caveat that is often overlooked is that you will often need to test for a certain number of invocations (e.g. not only was it called, but it was called exactly once). So I often do something like this:

var callCount = 0;
Action<Foo> action = _ => callCount++;
Bar<Foo> bar = new Bar<Foo>();
// set up conditions that should guarantee action is invoked
bar.M(action);
Assert.That(callCount, Is.EqualTo(1));

Upvotes: 5

jason
jason

Reputation: 241591

Well, the basic idea is that the Action<T> produces some state change somewhere (if it doesn't, what's the point?). So, unit test that the expected state change occurs when the conditions hold, and that the expected state change doesn't occur when the conditions do not hold.

Of course, ideally, you mock the Action<T> so that the state testing is super easy. You do not need Moq or any other mocking framework for this:

bool actionWasInvoked = false;
Action<Foo> action = foo => actionWasInvoked = true;
Bar<Foo> bar = new Bar<Foo>();
// set up conditions that should guarantee action is invoked
bar.M(action);
Assert.IsTrue(actionWasInvoked);

and

bool actionWasInvoked = false;
Action<Foo> action = foo => actionWasInvoked = true;
Bar<Foo> bar = new Bar<Foo>();
// set up conditions that should guarantee action is NOT invoked
bar.M(action);
Assert.IsFalse(actionWasInvoked);

Of course, I don't know your exact setup. Maybe you pass in action on construction of Bar, or maybe you have some other way of setting the action. But the idea should be clear.

Upvotes: 10

Related Questions