havij
havij

Reputation: 1180

How to mock a method that takes an action?

How can I unit test DoWork method?

public class SomeClass {
  private readonly IClass1 class1;
  private readonly Class2 class2;

  // IClass1 and Class2 are injected in ctor

  public string DoWork() {
    // do some work

    if(this.class2.Method2(() => this.class1.Method1())) {
      return "done!";
    }

    // other work 
  }

}

public interface IClass1 {
  void Method1();
}

public class Class2 {
  public virtual bool Method2(Action action) {
      // return true or false
  }
}

Upvotes: 1

Views: 503

Answers (2)

Sergey K
Sergey K

Reputation: 4114

From your code above I see that the injected IClass1 is used just to pass as parameter in injected class2. This is code smell!! The main class SomeClass should not know nothing about Class1. So I suggest to refactor your code in a way that Class2 will know about Class1 look at the code

    public class SomeClass {
    private readonly IClass2 class2;

    public SomeClass(IClass2 class2)
    {
        this.class2 = class2;
    }

    // class1 and class2 are injected in ctor

    public string DoWork() {
        // do some work

        if(this.class2.Method2()) {
            return "done!";
        }

        // other work 

        return string.Empty;
    }

}

public interface IClass1 {
    void Method1();
}

public interface IClass2
{
    bool Method2();
}

public class Class2 : IClass2
{
    private IClass1 class1;

    public Class2(IClass1 class1)
    {
        this.class1 = class1;
    }

    public virtual bool Method2() {
        // return true or false
        class1.Method1();
        return true;
    }
}

And then you can easy test it.

       [Test]
    public void WhenMethod2ReturnTrue_ThenDoWork_ShouldReturn_Done()
    {
        Mock<IClass2> class2 = new Mock<IClass2>();
        SomeClass someClass = new SomeClass(class2.Object);

        class2.Setup(x => x.Method2()).Returns(true);

        var doWork = someClass.DoWork();

        Assert.That(doWork,Is.EqualTo("done!"));
    }

    [Test]
    public void WhenMethod2ReturnFalse_ThenDoWork_ShouldReturn_Empty()
    {
        Mock<IClass2> class2 = new Mock<IClass2>();
        SomeClass someClass = new SomeClass(class2.Object);

        class2.Setup(x => x.Method2()).Returns(false);

        var doWork = someClass.DoWork();

        Assert.That(doWork,Is.Empty);
    }

I've used Moq library for Mocking interfaces

Upvotes: 1

Nkosi
Nkosi

Reputation: 247531

The following example uses Moq and FluentAssertions to test the SomeClass.DoWork test case that involves Class2 invoking a member Method2 that takes an action that invokes IClass1.Method1

[TestClass]
public class SomeClassTests {
    [TestMethod]
    public void DoWork_Should_Be_Done() {
        //Arrange
        IClass1 class1 = Mock.Of<IClass1>();
        Mock<Class2> class2 = new Mock<Class2>();
        class2.Setup(_ => _.Method2(It.IsAny<Action>()))
            .Returns(true)
            .Callback((Action passedAction) => passedAction?.Invoke());

        SomeClass subject = new SomeClass(class1, class2.Object);
        string expected = "done!";

        //Act
        var actual = subject.DoWork();

        //Assert
        actual.Should().Be(expected); //actual == expected
        Mock.Get(class1).Verify(_ => _.Method1(), Times.Once); //was action invoked
    }
}

Ideally Class2 dependency sould be abstracted behind an interface

public interface IClass2 {
     bool Method2(Action action);
}

public class Class2 : IClass2 {
    //...
}

but that is outside of the scope of the original question.

The above test mocks the dependencies and their expected behavior. Variations of that test can be done to cover alternative scenarios that would allow for the complete coverage of that method.

Upvotes: 1

Related Questions