Testing 什么';模拟和模拟的区别是什么;树桩?
我读过很多关于测试中模拟与存根的文章,包括但仍然不理解其中的区别。模拟只是测试行为,确保调用某些方法。 存根是特定对象的可测试版本(本身)Testing 什么';模拟和模拟的区别是什么;树桩?,testing,mocking,stub,Testing,Mocking,Stub,我读过很多关于测试中模拟与存根的文章,包括但仍然不理解其中的区别。模拟只是测试行为,确保调用某些方法。 存根是特定对象的可测试版本(本身) 苹果方式是什么意思?存根是一个简单的假对象。它只是确保测试顺利运行。 模仿是更聪明的存根。您验证您的测试是否通过了它。存根 我相信最大的区别是你已经用预定的行为写了一个存根。因此,您将拥有一个实现依赖关系的类(最有可能是抽象类或接口),这些依赖关系是您为了测试目的而伪造的,而这些方法只会被设置的响应打断。他们不会做任何花哨的事情,您可能已经在测试之外为其编写
苹果方式是什么意思?存根是一个简单的假对象。它只是确保测试顺利运行。
模仿是更聪明的存根。您验证您的测试是否通过了它。存根 我相信最大的区别是你已经用预定的行为写了一个存根。因此,您将拥有一个实现依赖关系的类(最有可能是抽象类或接口),这些依赖关系是您为了测试目的而伪造的,而这些方法只会被设置的响应打断。他们不会做任何花哨的事情,您可能已经在测试之外为其编写了存根代码 Mock 模拟测试是测试的一部分,你必须根据自己的期望进行设置。模拟不是以预先确定的方式设置的,所以您有代码在测试中进行模拟。模拟在某种程度上是在运行时确定的,因为设置期望的代码必须在执行任何操作之前运行 模拟和存根之间的差异 使用mock编写的测试通常遵循
initialize->set expections->exercise->verify
模式进行测试。而预写存根将遵循初始化->练习->验证
模拟和存根之间的相似性
两者的目的都是为了消除测试类或函数的所有依赖关系,从而使您的测试更集中,更简单。存根不会让您的测试失败,mock can。在本课程中,他们给出了以下术语的定义:
存根
用于用返回指定结果的代码替换方法
嘲弄
一个存根,其断言方法被调用
因此,正如肖恩·科本哈(Sean Copenhaver)在他的回答中所描述的,不同之处在于Mock设定了期望(即,对他们是否或如何被呼叫做出断言)。我认为他们之间最重要的区别是他们的意图
让我试着在《为什么存根》和《为什么模仿》中解释一下
假设我正在为我的MacTwitter客户端的公共时间线控制器编写测试代码
下面是测试示例代码
twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
- STUB:到twitterapi的网络连接非常慢,这使得我的测试很慢。我知道它会返回时间线,所以我制作了一个模拟HTTPTwitterAPI的存根,这样我的测试将运行得非常快,即使我离线也可以运行测试李>
- MOCK:我还没有编写任何UI方法,我也不确定需要为UI对象编写哪些方法。我希望通过编写测试代码来了解控制器将如何与ui对象协作
通过编写mock,您可以通过验证是否满足期望来发现对象协作关系,而存根只模拟对象的行为
如果您想了解更多关于mock的知识,我建议您阅读本文:前言
有几种对象的定义,它们不是真实的。一般术语为双倍测试。该术语包括:伪、假、存根、模拟
参考文献
根据:
- Dummy对象被传递,但从未实际使用过。通常它们只是用来填充参数列表
- Fake对象实际上有工作实现,但通常会采取一些快捷方式,使其不适合生产(内存中的数据库就是一个很好的例子)
- 存根为测试期间拨打的电话提供固定答案,通常对测试程序之外的任何内容都没有响应。存根还可以记录有关呼叫的信息,例如电子邮件网关存根,它可以记住它“发送”的消息,或者可能只记录它“发送”的消息数
- mock就是我们在这里讨论的内容:对象预先编程,并带有期望值,这些期望值形成了它们期望接收的调用的规范
风格
模拟与存根=行为测试与状态测试
原则
根据每个测试只测试一件事情的原则,一个测试中可能有几个存根,但通常只有一个模拟
生命周期
使用存根的测试生命周期:
设置-准备正在测试的对象及其存根协作者
练习-测试功能
验证状态-使用断言检查对象的状态
拆卸-清理资源
模拟的测试生命周期:
设置数据-准备正在测试的对象
设置期望值-在主要对象正在使用的模拟中准备期望值
练习-测试功能
验证期望值-验证是否在模拟中调用了正确的方法
验证状态-使用断言检查对象的状态
拆卸-清理资源
总结
模拟和存根测试都给出了问题的答案:结果是什么?
模拟测试也感兴趣:结果是如何实现的?如果将其与调试进行比较:
存根类似于确保方法返回正确的值
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";
}
}
}
public void Analyze(string filename)
{
if(filename.Length<8)
{
try
{
errorService.LogError("long file entered named:" + filename);
}
catch (Exception e)
{
mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
}
}
}
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
import os
import os.path
def rm(filename):
if os.path.isfile(filename):
os.remove(filename)
from mymodule import rm
import mock
import unittest
class RmTestCase(unittest.TestCase):
@mock.patch('mymodule.os')
def test_rm(self, mock_os):
rm("any path")
# test that rm called os.remove with the right parameters
mock_os.remove.assert_called_with("any path")
if __name__ == '__main__':
unittest.main()
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
@Mock Foo fooMock
when(fooMock.hello()).thenReturn("hello you!");
verify(fooMock).hello()
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}