如何仅在依赖方法2时测试方法1
How to test method1 only when it depends on method 2
假设我有一个带有(示例)实现的 WCF 服务:
public interface IFoo
{
void Bar();
void Baz();
}
public interface ISomeDependency
{
void DoStuff();
}
public class Foo : IFoo
{
ISomeDependency _someDependency;
public Foo(ISomeDependency someDependency)
{
this._someDependency = someDependency;
}
public void Bar()
{
// Some stuff
_someDependency.DoStuff();
if (1 == 1) // some condition that won't always be true
this.Baz();
}
public void Baz()
{
// some stuff
_someDependency.DoStuff();
}
}
如何在不关心 Foo.Baz
的结果的情况下进行单元测试 Foo.Bar
的实施?具体来说,我想知道 Foo.Baz();被调用(或未被调用)取决于我如何模拟对 Foo.Bar
的调用,但不一定希望 Foo.Baz
的逻辑对 "fire"。
我原本想做这样的事情:
public class Foo : IFoo
{
// ... same as previous
public virtual void Baz()
{
// some stuff
_someDependency.DoStuff();
}
}
然后在我的单元测试项目中有:
public class TestFoo : Foo
{
public bool IsBazFired { get; private set; }
public TestFoo(ISomeDependency someDependency)
: base (someDependency)
{
IsBazFired = false;
}
public override void Baz()
{
IsBazFired = true;
}
}
这样我可以看到 Foo.Baz
在我的测试中被触发(尽管我必须使用 TestFoo
而不是 Foo
进行测试。还有另一种方法我可以去做这个?现在看起来工作还不够,但是如果试图在所有地方实现它,代码 could/would 就会变得乱七八糟 类.
的测试实现
我不一定喜欢将我的函数标记为 virtual
只是为了我可以存根用于测试的实现...所以我希望有另一种方式。
我目前刚开始使用模拟框架 Moq
,如果这对如何实现我想要的结果有影响的话。
以下是我如何使用 Moq
完成测试。
首先,将需要 "stubbed" 的方法标记为虚拟方法(希望这不是不好的做法):
public virtual void Baz()
{
// some stuff
_someDependency.DoStuff();
}
在您的测试中,您可以设置 class 的模拟对象,而不是接口。到目前为止,我只是在模拟接口:
[TestClass]
public class FooTests
{
Mock<Foo> _mockFoo;
Mock<ISomeDependency> _mockSomeDependency;
[TestInitialize]
public void Setup()
{
_mockSomeDependency = new Mock<ISomeDependency>();
_mockFoo = new Mock<Foo>(_mockSomeDependency.Object);
}
[TestMethod]
public void Testing_BazIsNotCalled()
{
_mockFoo.CallBase = true;
_mockFoo.Setup(s => s.Baz());
_mockFoo.Object.Bar();
_mockFoo.Verify(v => v.Baz(), Times.Never);
}
[TestMethod]
public void Testing_BazIsCalled()
{
_mockFoo.CallBase = true;
_mockFoo.Setup(s => s.Baz());
_mockFoo.Object.Bar();
_mockFoo.Verify(v => v.Baz(), Times.Once);
}
}
在我的模拟上使用 CallBase = true
表示任何未明确模拟的东西都将使用 classes 实现。
在我的 _mockFoo.Setup(s => s.Baz());
中,我覆盖了 Baz()
的 "base" 功能并提供了一个新的实现(在本例中为存根)。然后我实际上调用了基础 Bar()
,并使用 _mockFoo.Verify(...);
测试 Baz()
调用是否继续。这个验证检查当然是基于 if (1==1)
的,在这种情况下它总是正确的,但我只是想在那里得到一些 "fake logic" 所以我会有一个块来检查。
请注意,如果您忘记将尝试存根的方法标记为虚拟,您的代码将可以正常编译,但您会在 运行 时间内遇到类似于:
的异常
Result StackTrace:
...
at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo method)
at Moq.Mock.<>c__DisplayClass1c`2.<Setup>b__1b()
at Moq.PexProtector.Invoke[T](Func`1 function)
at Moq.Mock.Setup[T,TResult](Mock`1 mock, Expression`1 expression, Condition condition)
at Moq.Mock`1.Setup[TResult](Expression`1 expression)
at FooTests.Testing_BazIsNotCalled() in UnitTests.cs:line 23
Result Message: Initialization method Testing_BazIsNotCalled threw exception. System.NotSupportedException: System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: s => s.Baz()).
我不知道我使用的上述术语是否正确 (stubs/mocks),但它仍然有效。
假设我有一个带有(示例)实现的 WCF 服务:
public interface IFoo
{
void Bar();
void Baz();
}
public interface ISomeDependency
{
void DoStuff();
}
public class Foo : IFoo
{
ISomeDependency _someDependency;
public Foo(ISomeDependency someDependency)
{
this._someDependency = someDependency;
}
public void Bar()
{
// Some stuff
_someDependency.DoStuff();
if (1 == 1) // some condition that won't always be true
this.Baz();
}
public void Baz()
{
// some stuff
_someDependency.DoStuff();
}
}
如何在不关心 Foo.Baz
的结果的情况下进行单元测试 Foo.Bar
的实施?具体来说,我想知道 Foo.Baz();被调用(或未被调用)取决于我如何模拟对 Foo.Bar
的调用,但不一定希望 Foo.Baz
的逻辑对 "fire"。
我原本想做这样的事情:
public class Foo : IFoo
{
// ... same as previous
public virtual void Baz()
{
// some stuff
_someDependency.DoStuff();
}
}
然后在我的单元测试项目中有:
public class TestFoo : Foo
{
public bool IsBazFired { get; private set; }
public TestFoo(ISomeDependency someDependency)
: base (someDependency)
{
IsBazFired = false;
}
public override void Baz()
{
IsBazFired = true;
}
}
这样我可以看到 Foo.Baz
在我的测试中被触发(尽管我必须使用 TestFoo
而不是 Foo
进行测试。还有另一种方法我可以去做这个?现在看起来工作还不够,但是如果试图在所有地方实现它,代码 could/would 就会变得乱七八糟 类.
我不一定喜欢将我的函数标记为 virtual
只是为了我可以存根用于测试的实现...所以我希望有另一种方式。
我目前刚开始使用模拟框架 Moq
,如果这对如何实现我想要的结果有影响的话。
以下是我如何使用 Moq
完成测试。
首先,将需要 "stubbed" 的方法标记为虚拟方法(希望这不是不好的做法):
public virtual void Baz()
{
// some stuff
_someDependency.DoStuff();
}
在您的测试中,您可以设置 class 的模拟对象,而不是接口。到目前为止,我只是在模拟接口:
[TestClass]
public class FooTests
{
Mock<Foo> _mockFoo;
Mock<ISomeDependency> _mockSomeDependency;
[TestInitialize]
public void Setup()
{
_mockSomeDependency = new Mock<ISomeDependency>();
_mockFoo = new Mock<Foo>(_mockSomeDependency.Object);
}
[TestMethod]
public void Testing_BazIsNotCalled()
{
_mockFoo.CallBase = true;
_mockFoo.Setup(s => s.Baz());
_mockFoo.Object.Bar();
_mockFoo.Verify(v => v.Baz(), Times.Never);
}
[TestMethod]
public void Testing_BazIsCalled()
{
_mockFoo.CallBase = true;
_mockFoo.Setup(s => s.Baz());
_mockFoo.Object.Bar();
_mockFoo.Verify(v => v.Baz(), Times.Once);
}
}
在我的模拟上使用 CallBase = true
表示任何未明确模拟的东西都将使用 classes 实现。
在我的 _mockFoo.Setup(s => s.Baz());
中,我覆盖了 Baz()
的 "base" 功能并提供了一个新的实现(在本例中为存根)。然后我实际上调用了基础 Bar()
,并使用 _mockFoo.Verify(...);
测试 Baz()
调用是否继续。这个验证检查当然是基于 if (1==1)
的,在这种情况下它总是正确的,但我只是想在那里得到一些 "fake logic" 所以我会有一个块来检查。
请注意,如果您忘记将尝试存根的方法标记为虚拟,您的代码将可以正常编译,但您会在 运行 时间内遇到类似于:
的异常Result StackTrace: ... at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo method) at Moq.Mock.<>c__DisplayClass1c`2.<Setup>b__1b() at Moq.PexProtector.Invoke[T](Func`1 function) at Moq.Mock.Setup[T,TResult](Mock`1 mock, Expression`1 expression, Condition condition) at Moq.Mock`1.Setup[TResult](Expression`1 expression) at FooTests.Testing_BazIsNotCalled() in UnitTests.cs:line 23 Result Message: Initialization method Testing_BazIsNotCalled threw exception. System.NotSupportedException: System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: s => s.Baz()).
我不知道我使用的上述术语是否正确 (stubs/mocks),但它仍然有效。