我读过各种关于测试中模仿和存根的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但我仍然不理解其中的区别。
当前回答
Stub帮助我们运行测试。怎么做?它给出了有助于运行测试的值。这些值本身不是真实的,我们创建这些值只是为了运行测试。例如,我们创建一个HashMap来提供与数据库表中的值相似的值。因此,我们不直接与数据库交互,而是与Hashmap交互。
Mock是一个运行测试的伪对象。我们在这里输入assert。
其他回答
参见下面使用c#和Moq框架的mock和存根的例子。Moq对于存根没有特殊的关键字,但是您也可以使用Mock对象来创建存根。
namespace UnitTestProject2
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestClass]
public class UnitTest1
{
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
}
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(0);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
}
/// <summary>
/// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
{
// Arrange
var stubEntityRepository = new Mock<IEntityRepository>();
stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
.Returns("Stub");
const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
var entity = new EntityClass(stubEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
}
}
public class EntityClass
{
private IEntityRepository _entityRepository;
public EntityClass(IEntityRepository entityRepository)
{
this._entityRepository = entityRepository;
}
public string Name { get; set; }
public string GetNameWithPrefix(int id)
{
string name = string.Empty;
if (id > 0)
{
name = this._entityRepository.GetName(id);
}
return "Mr. " + name;
}
}
public interface IEntityRepository
{
string GetName(int id);
}
public class EntityRepository:IEntityRepository
{
public string GetName(int id)
{
// Code to connect to DB and get name based on Id
return "NameFromDb";
}
}
}
从论文模拟角色,而不是对象,由jMock的开发人员:
存根是返回罐装的产品代码的虚拟实现 结果。Mock对象充当存根,但也包括到的断言 测量目标对象与其邻居的交互作用。
所以,主要的区别是:
在存根上设置的期望通常是通用的,而在mock上设置的期望可能更“聪明”(例如,在第一次调用时返回this,在第二次调用时返回this等)。 存根主要用于设置SUT的间接输入,而mock可用于测试SUT的间接输入和间接输出。
综上所述,同时也试图驱散福勒文章标题中的困惑:mock是存根,但它们不仅仅是存根。
模拟:帮助模拟和检查结果交互。这些交互 SUT调用它的依赖项来改变它们的状态。
存根:帮助模拟传入的交互。这些相互作用称为 SUT对其依赖项进行处理以获取输入数据。
来源:单元测试原则、实践和模式- Manning
我偶然看到了《小嘲笑者鲍勃叔叔》的一篇有趣的文章。它以一种非常容易理解的方式解释了所有的术语,因此对初学者很有用。Martin fowler的文章很难读,尤其是对于像我这样的初学者。
测试对象根据某些提示(函数调用)或其他刺激来执行动作。下面是测试情况的具体例子。
场景——EMT学生考试
一名学生学习成为一名紧急医疗技术员。如果你对这个测试情况不熟悉,可以去看Ian Gallagher在《无耻之徒》第六季第十集的节目。
为了测试目的,寻找各种疾病的患者太昂贵了。相反,我们使用角色。我们问测试对象(Ian)"当你到达现场发现病人已经不能动弹,失去知觉你首先要做什么?"伊恩回答说:“我检查现场是否安全。”测试指导老师说“现场是安全的”。
教师(和演员)能够向测试对象的问题注入任意答案。
在这里,教练(和演员)是一个模拟。医学培训与计算机科学家一样使用这个术语(例如模拟代码模拟)。
场景——注册一个网站
你正在测试雅虎,一个你听说过的新的电子邮件服务。为了注册,你必须提供你的生日和其他侵入性问题的答案。
该网站要求你年满21岁。输入1970年1月1日。它满足了需求,并且将您从实现记住我的生日并输入它的工作流的费力过程中拯救出来。
这个日期是存根。这个词的用法只适用于计算机科学。