如何模拟采取行动的方法?

How to mock a method that takes an action?

如何对 DoWork 方法进行单元测试?

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
  }
}

以下示例使用 Moq and FluentAssertions 来测试涉及 Class2 调用成员 Method2SomeClass.DoWork 测试用例,该成员 Method2 执行调用 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
    }
}

理想情况下Class2 依赖应该在接口后面被抽象化

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

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

但这超出了原始问题的范围。

上述测试模拟了依赖项及其预期行为。可以对该测试进行变体以涵盖允许完全覆盖该方法的替代方案。

从你上面的代码中我看到注入的 IClass1 只是作为参数传递给注入的 class2。这是代码味道!!主要class SomeClass应该对Class1一无所知。 因此,我建议以 Class2 了解 Class1 的方式重构您的代码,请查看代码

    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;
    }
}

然后就可以轻松测试了。

       [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);
    }

我使用了 Moq 模拟接口库