有没有人或地方可以用通俗易懂的语言解释这个问题,而不是“用自身定义术语”?
所以,你有一个依赖于其他东西的类。
让我们使用一个比喻:汽车需要发动机。
汽车依赖于发动机。测试汽车和发动机协同工作很容易,但是如果没有发动机测试汽车,或者汽车正确地“调用”发动机呢?我们可以把一些东西(模拟对象)放在发动机的位置上,然后按下油门(进行调用),并验证虚假的(模拟的)发动机是否收到了节气门的正确输入。通过使用模拟对象测量,而不是验证整个系统,您只需隔离地测试您想要的东西。
实际上,它变得更加复杂和更加强大,但是......
在这种情况下,通常会用一种不使用任何资源的虚拟服务来替换服务对象。这被称为存根或模拟对象。存根和模拟之间的区别似乎是一些讨论的主题,但本质上它们是相同的。
像Rhino Mock这样的模拟框架可以帮助你创建模拟对象,这些对象的响应方式与实际服务的响应方式相同。你可以将它们与实际服务调用的录音进行比较,每次执行测试时都可以回放。
class SomeClass
{
IStringAnalyzer stringAnalizer;
ILogger logger;
public SomeClass(IStringAnalyzer stringAnalyzer, ILogger logger)
{
this.logger = logger;
this.stringAnalyzer = stringAnalyzer;
}
public void SomeMethod(string someParameter)
{
if (stringAnalyzer.IsValid(someParameter))
{
logger.Log("Invalid string");
}else
{
//do something with someParameter
}
}
}
class StringAnalyzerStub : IStringAnalyzer
{
public bool whatToReturn;
public StubStringAnalyzerStub(bool whatToReturn)
{
this.whatToReturn = whatToReturn;
}
public bool IsValid(string parameter)
{
return whatToReturn;
}
}
class LoggerMock : ILogger
{
public string WhatWasPassedIn;
public void Log(string message)
{
WhatWasPassedIn = message;
}
}
以下是测试内容:
[Test]
public void SomeMethod_InvalidParameter_CallsLogger
{
IStringAnalyzer s = new StringAnalyzerStub(false); //will always return false when IsValid is called
ILogger l = new LoggerMock();
SomeClass someClass = new SomeClass(s, l);
someClass.SomeMethod("What you put here doesnt really matter because the stub will always return false");
Assert.AreEqual(l.WhatWasPassedIn, "Invalid string");
}
手动进行此操作容易出错且难以维护,因此需要像Rhino Mocks这样的隔离框架。它们允许您动态创建这些模拟和存根。以下是使用Rhino Mocks(使用排列、行动、断言语法)的相同测试的示例:
[Test]
public void SomeMethod_InvalidParameter_CallsLogger
{
Rhino.Mocks.MockRepository mockRepository = new Rhino.Mocks.MockRepository();
IStringAnalyzer s = mockRepository.Stub<IStringRepository>();
s.Expect(s => s.IsValid("something, doesnt matter").IgnoreParameters().Return(false);
ILogger l = mockRepository.DynamicMock<ILogger>();
l.Log("Invalid string");
SomeClass someClass = new SomeClass(s, l);
mockRepository.ReplayAll();
someClass.SomeMethod("What you put here doesnt really matter because the stub will always return false");
l.AssertWasCalled(l => l.Log("Invalid string"));
}
不允许用自身来定义术语 :)
声明:我在文本编辑器中编写了所有内容,因此代码中可能会有一些语法错误...
Mock是一种代理,您可以指示其以某种方式运行。这在测试中非常有用,因为您可以通过使用模拟对象来替换实际实例来消除依赖性。
与存根不同,模拟对象会跟踪实际使用情况,因此您的测试可以验证模拟对象是否按预期使用。
有关更多信息,请参阅Fowler的文章here。