validation方法被调用

使用Moq,我有一个非常奇怪的问题,即如果我设置的方法是公共的,模拟上的设置似乎只有效。 我不知道这是一个Moq bug还是我错了(Moq的新手)。 以下是测试用例:

public class TestClass { public string Say() { return Hello(); } internal virtual string Hello() { return ""; } } [TestMethod] public void Say_WhenPublic_CallsHello() { Mock mock = new Mock(); mock.Setup(x => x.Hello()).Returns("Hello World"); string result = mock.Object.Say(); mock.Verify(x => x.Hello(), Times.Exactly(1)); Assert.AreEqual("Hello World", result); } 

该消息失败了:

Say_WhenPublic_CallsHello失败:Moq.MockException:未在模拟上执行1次调用:x => x.Hello()在Moq.Mock.ThrowVerifyException(预期IProxyCall,表达式,时间)…

如果我像这样公开Hello方法,则测试通过。 这是什么问题?

 public virtual string Hello() { return ""; } 

提前致谢!

Hello()是内部的时,测试失败,因为在这种情况下Moq无法提供方法的覆盖。 这意味着Hello()的内部实现将运行,而不是mock的版本,导致Verify()失败。

顺便说一下,你在这里做的事情在unit testing的背景下是没有意义的。 unit testing不应该关心Say()调用内部Hello()方法。 这是程序集内部的实现,而不是消耗代码的问题。

我不太清楚它是如何在封面下工作的,为你提供一个技术答案,确切地知道为什么是这种行为,但我想我可以帮助你解决问题。

在您的示例中,您正在调用方法Say(),并返回预期的文本。 您的期望不应强制执行Say()的特定实现,即它调用一个名为Hello()的内部方法来返回该字符串。 这就是为什么它没有通过validation,以及为什么返回的字符串是“”,即调用了Hello()的实际实现。

通过使Hello方法公开,似乎这使Moq能够拦截对它的调用,并使用它的实现。 因此,在这种情况下,测试似乎通过了。 但是,在这种情况下,你还没有真正实现任何有用的东西,因为你的测试说当你调用Say()时结果是“Hello World”,当实际上结果为“”时。

我已经重写了你的例子来展示我将如何使用Moq(不一定是明确的,但希望清楚。

 public interface IHelloProvider { string Hello(); } public class TestClass { private readonly IHelloProvider _provider; public TestClass(IHelloProvider provider) { _provider = provider; } public string Say() { return _provider.Hello(); } } [TestMethod] public void WhenSayCallsHelloProviderAndReturnsResult() { //Given Mock mock = new Mock(); TestClass concrete = new TestClass(mock.Object); //Expect mock.Setup(x => x.Hello()).Returns("Hello World"); //When string result = concrete.Say(); //Then mock.Verify(x => x.Hello(), Times.Exactly(1)); Assert.AreEqual("Hello World", result); } 

在我的例子中,我介绍了一个IHelloProvider的接口。 您会注意到没有IHelloProvider的实现。 这是我们通过使用模拟解决方案实现的目标的核心。

我们正在尝试测试一个类(TestClass),它依赖于外部的东西(IHelloProvider)。 如果你正在使用测试驱动开发,那么你可能还没有编写过IHelloProvider,但是你知道在某些时候你需要一个。 你想让TestClass首先工作,而不是分心。 或者IHelloProvider可能使用数据库或平面文件,或者很难配置。

即使你有一个完全正常工作的IHelloProvider,你仍然只是试图测试TestClass的行为,所以使用具体的HelloProvider可能会使你的测试更容易失败,例如,如果HelloProvider的行为发生了变化,您不希望更改使用它的每个类的测试,您只想更改HelloProvider测试。

回到代码,我们现在有一个类TestClass,它依赖于一个接口IHelloProvider,它的实现是在构造时提供的(这是dependency injection)。

Say()的行为是它调用IHelloProvider上的方法Hello()。

如果回顾一下测试,我们已经创建了一个实际的TestClass对象,因为我们实际上想要测试我们编写的代码。 我们已经创建了一个模拟IHelloProvider,并且说我们希望它调用Hello()方法,并且当它返回字符串“Hello World”时。

然后我们调用Say(),并像以前一样validation结果。

要意识到的重要一点是我们对IHelloProvider的行为不感兴趣,因此我们可以模拟它以使测试更容易。 我们对TestClass的行为感兴趣,所以我们创建了一个实际的TestClass而不是Mock,以便我们可以测试它的实际行为。

我希望这有助于澄清正在发生的事情。

Moq不进行部分模拟,只能模拟公共虚拟方法或接口。 创建模拟时,您将创建一个全新的T并删除所有公共虚拟方法的实现。

我的建议是专注于测试物体的公共表面区域而不是它们的内部。 你的内部人员将得到报道。 只要确保你清楚地知道你的目标类是什么,并且不要嘲笑它 (在大多数情况下)。

只有当您想要使用模拟的抽象方法实现来测试抽象类的function时,部分模拟才有用 。 如果你不这样做, 你可能不会看到做部分模拟的好处

如果这不是一个抽象类,你将需要更多地关注Modan建议的方法,也就是说你应该模拟依赖关系,而不是你的目标类本身。 所有这些归结为规则不要嘲笑你正在测试的东西