如何模拟采取行动的方法?
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
调用成员 Method2
的 SomeClass.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 模拟接口库
如何对 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
调用成员 Method2
的 SomeClass.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 模拟接口库