Martin Fowler의 Mocks Are n't Stubs를 포함하여 테스트에서 조롱과 스터 빙에 대한 다양한 기사를 읽었 지만 여전히 차이점을 이해하지 못합니다.
Martin Fowler의 Mocks Are n't Stubs를 포함하여 테스트에서 조롱과 스터 빙에 대한 다양한 기사를 읽었 지만 여전히 차이점을 이해하지 못합니다.
답변:
그루터기
가장 큰 차이점은 미리 정해진 행동으로 작성한 스텁입니다. 따라서 테스트 목적으로 가짜 인 의존성 (추상 클래스 또는 인터페이스)을 구현하는 클래스가 있으며 설정된 응답으로 메소드가 스텁 아웃됩니다. 그들은 멋진 일을하지 않을 것이며 이미 테스트 외부에서 스텁 된 코드를 작성했을 것입니다.
모조품
모의는 테스트의 일부로 기대치로 설정해야하는 것입니다. 모의는 미리 정해진 방식으로 설정되지 않았으므로 테스트에서 수행하는 코드가 있습니다. 예상치를 설정하는 코드는 작업을 수행하기 전에 실행해야하므로 런타임에 mocks가 결정됩니다.
Mocks와 Stubs의 차이점
모의로 작성된 테스트는 일반적 initialize -> set expectations -> exercise -> verify
으로 테스트 패턴을 따릅니다 . 미리 작성된 스텁은initialize -> exercise -> verify
.
Mocks와 Stubs의 유사성
두 가지의 목적은 클래스 또는 함수의 모든 종속성 테스트를 제거하여 테스트하려는 항목에 더 집중하고 단순하게 테스트하는 것입니다.
실제가 아닌 객체에 대한 몇 가지 정의가 있습니다. 일반적인 용어는 test double 입니다. 이 용어는 더미 , 가짜 , 스터브 , 모의 됩니다.
Martin Fowler의 기사 에 따르면 :
- 더미 객체는 전달되지만 실제로는 사용되지 않습니다. 일반적으로 매개 변수 목록을 채우는 데 사용됩니다.
- 가짜 객체는 실제로 작동하는 구현이 있지만 일반적으로 생산에 적합하지 않은 바로 가기를 사용합니다 (메모리 데이터베이스가 좋은 예입니다).
- 스텁 은 일반적으로 테스트를 위해 프로그래밍 된 외부의 항목에는 전혀 응답하지 않는 테스트 중에 호출에 대해 미리 준비된 답변을 제공합니다. 스텁은 '보낸'메시지를 기억하는 전자 메일 게이트웨이 스텁 또는 '보낸'메시지 수와 같은 통화에 대한 정보를 기록 할 수도 있습니다.
- Mocks 는 우리가 여기서 말하는 것입니다. 기대되는 사전 프로그래밍 된 객체는 그들이받을 전화의 사양을 형성합니다.
Mocks vs Stubs = 행동 테스트 대 상태 테스트
테스트 원칙에 따라 테스트 당 한 가지만 에는 여러 개의 스터브가있을 수 있지만 일반적으로 하나의 모의가 있습니다.
스텁을 사용하여 수명주기를 테스트하십시오.
모의 테스트 라이프 사이클 :
모의 테스트와 스텁 테스트 모두 질문에 대한 답변을 제공합니다 . 결과는 무엇입니까?
모의 테스트도 관심이 있습니다 : 결과는 어떻게 달성 되었습니까?
스텁은 단순한 가짜 개체입니다. 테스트가 순조롭게 진행되도록합니다.
모의는 더 똑똑한 스텁입니다. 테스트가 통과했는지 확인합니다.
다음은 실제 샘플과 함께 각각에 대한 설명입니다.
더미 -단지 가짜 값을 만족시킵니다 API
.
예제 : 테스트에 영향 을 미치지 않는 생성자에 많은 필수 매개 변수가 필요한 클래스의 메서드를 테스트하는 경우 클래스 의 새 인스턴스를 만들기 위해 더미 객체를 만들 수 있습니다.
모조품 -일부 외부 인프라에 종속 될 수있는 클래스의 테스트 구현을 작성하십시오. 단위 테스트가 실제로 외부 인프라와 상호 작용 하지 않는 것이 좋습니다 .
예 : 데이터베이스에 액세스하기 위해 가짜 구현을 작성하십시오.
in-memory
컬렉션으로 .
그루터기 - 재정의 방법은 하드 코딩 된 값이라고도을 반환합니다 state-based
.
예 : 테스트 수업은
Calculate()
완료 하는 데 5 분이 걸리는 방법에 따라 다릅니다 . 5 분 동안 기다리지 않고 실제 구현을 하드 코딩 된 값을 반환하는 스텁으로 바꿀 수 있습니다. 시간의 작은 부분 만 차지합니다.
모조품 - 매우 유사 Stub
하지만, interaction-based
오히려 상태 기반보다. 이것은 Mock
어떤 값을 반환하지는 않지만 특정 순서의 메소드 호출이 있다고 가정한다는 것을 의미합니다 .
예 : 사용자 등록 클래스를 테스트하고 있습니다. 를 호출 한 후에
Save
는SendConfirmationEmail
.
Stubs
과 Mocks
의 하위 유형 실제로 Mock
, 테스트 구현 모두 스왑 실제 구현하지만 다른에 대한 구체적인 이유는.
에서 codeschool.com의 과정, 레일 테스트 좀비에 대한 , 그들은 용어의 정의를 제공합니다 :
그루터기
지정된 결과를 리턴하는 코드로 메소드를 대체합니다.
모조품
메소드가 호출되는 주장이있는 스텁.
Sean Copenhaver가 그의 답변에서 설명했듯이, 차이점은 모의가 기대치를 설정한다는 것입니다 (즉, 호출 여부와 방법에 대한 주장을합니다).
이 질문에 대한 가장 간단하고 명확한 대답은 Roy Osherove 가 그의 저서 The Unit of Art Testing (85 페이지)에서 제공 한 것 같습니다.
스텁을 처리하는 가장 쉬운 방법은 스텁이 테스트에 실패하지 않을 수 있다는 것입니다. 테스트에서 사용하는 어설 션은 항상 테스트중인 클래스에 대한 것입니다.
반면에 테스트는 모의 객체를 사용하여 테스트가 실패했는지 여부를 확인합니다. [...]
다시 모의 객체는 테스트 실패 여부를 확인하는 데 사용하는 객체입니다.
즉, 가짜에 대해 어설 션을 작성하는 경우 가짜를 모의로 사용하고 있다는 것을 의미합니다. 가짜를 사용하여 테스트를하지 않고 테스트를 실행하는 경우 가짜를 스텁으로 사용하고 있습니다.
위의 모든 설명을 읽고 응축하려고합니다.
모의는 행동을 테스트하고 특정 방법이 호출되도록합니다. 스텁은 특정 객체의 테스트 가능한 버전 (자체)입니다.
애플 방식이란 무엇입니까?
정신 모델을 사용하면 실제로 "싱크"하지 않은 모든 설명과 기사가 아니라 이것을 이해하는 데 실제로 도움이되었습니다.
당신의 아이가 테이블 위에 유리판을 가지고 있고 그것을 가지고 놀고 있다고 상상해보십시오. 자, 당신은 그것이 깨질 것을 두려워합니다. 그래서 당신은 그에게 대신 플라스틱 접시를 제공합니다. 그것은 모의 일 것이다 (같은 행동, 동일한 인터페이스, "더 부드러운"구현).
이제 플라스틱 교체품이 없다고 말하고 "계속 재생하면 고장납니다!"라고 설명합니다. 그것은 Stub 입니다. 사전에 미리 정의 된 상태를 제공했습니다.
더미는 그는 심지어 사용하지 않은 분기점이 될 것입니다 ... 그리고 스파이는 이미 일한 사용되는 것과 동일한 설명을 제공하는 같은 수 있습니다.
그들 사이의 가장 중요한 차이점은 그들의 의도라고 생각합니다.
왜 스텁 대 왜 모의 에서 설명하려고
Mac Twitter 클라이언트의 공개 타임 라인 컨트롤러 용 테스트 코드를 작성한다고 가정합니다.
테스트 샘플 코드는 다음과 같습니다.
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
모의를 작성하면 예상이 충족되는지 확인하여 오브젝트 협업 관계를 발견하는 한편 스텁은 오브젝트의 동작 만 시뮬레이션합니다.
모의에 대해 더 많이 알고 싶다면이 기사를 읽으십시오 : http://jmock.org/oopsla2004.pdf
매우 명확하고 실용적입니다.
스텁 : 위조 될 클래스 / 객체의 메소드를 구현하고 항상 원하는 것을 반환하는 클래스 또는 객체.
JavaScript의 예 :
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
모의 : 스텁과 동일하지만 메소드가 호출 될 때 "확인"하는 로직을 추가하므로 일부 구현이 해당 메소드를 호출하는지 확인할 수 있습니다.
@mLevan이 말했듯이 사용자 등록 클래스를 테스트하고 있다고 예를 들어보십시오. 저장을 호출 한 후 SendConfirmationEmail을 호출해야합니다.
매우 어리석은 코드 예 :
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
테스트 복식을 보자 :
스텁 : 스텁은 사전 정의 된 데이터를 보유하고 테스트 중에 호출에 응답하는 데 사용하는 객체입니다. 같은 : 메서드 호출에 응답하려면 데이터베이스에서 일부 데이터를 잡아해야하는 객체입니다.
Mocks : Mocks는 수신 통화를 등록하는 객체입니다. 테스트 어설 션에서 Mocks에서 모든 예상 작업이 수행되었는지 확인할 수 있습니다. 등의 서비스를 보내는 전자 메일을 호출하는 기능 :. 자세한 내용은 이것을 확인 하십시오 .
가짜 실제 개체 같은 사람들 때문에 모두보기, 스텁 또는 모의 객체 (필기 또는 기타) 중 하나를 설명하는 데 사용 될 수있는 일반적인 용어이다.
가짜가 스텁인지 모의인지는 현재 테스트에서 사용되는 방법에 따라 다릅니다. 상호 작용을 확인하는 데 사용되는 경우 (모의 대상) 모의 객체입니다. 그렇지 않으면 스텁입니다.
가짜 는 테스트가 원활하게 진행되도록합니다. 이는 미래의 테스트 독자가 외부 코드에 의존하지 않고 소스 코드를 읽을 필요없이 가짜 객체의 동작을 이해할 것임을 의미합니다.
테스트 실행이 원활하게 무엇을 의미합니까?
아래 코드의 예 :
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");
}
}
}
mailService.SendEMail () 메소드 를 테스트하려고합니다. 테스트 메소드에서 예외를 시뮬레이트해야하므로 가짜 스텁 errorService 클래스를 작성하여 해당 결과를 시뮬레이트하면 테스트 코드에서 테스트 할 수 있습니다. mailService.SendEMail () 메소드. 보시다시피 다른 외부 종속성 ErrorService 클래스의 결과를 시뮬레이션해야합니다.
jMock 개발자가 작성한 Objects가 아닌 Mock Roles 논문에서 바로 :
스텁은 미리 작성된 결과를 리턴하는 프로덕션 코드의 더미 구현입니다. 모의 객체는 스텁 역할을하지만 대상 객체와 해당 이웃의 상호 작용을 계측하기위한 어설 션도 포함합니다.
따라서 주요 차이점은 다음과 같습니다.
요약하자면 Fowler의 기사 제목 에서 혼란을 해소하려고 시도하는 동안 모의는 스텁이지만 스텁은 아닙니다 .
나는 The Unit of Unit Testing 을 읽고 있었고 다음과 같은 정의를 우연히 발견했습니다.
가짜는 실제 개체 같은 사람들 때문에 모두보기, 스텁 또는 모의 객체 (필기 또는 기타) 중 하나를 설명하는 데 사용 될 수있는 일반적인 용어이다. 가짜가 스텁인지 모의인지는 현재 테스트에서 사용되는 방법에 따라 다릅니다. 상호 작용을 확인하는 데 사용되는 경우 ( 모의 대상) 모의 객체 입니다. 그렇지 않으면 스텁 입니다.
UncleBob The Little Mocker 의이 흥미로운 기사를 보았습니다 . 모든 용어를 이해하기 쉬운 방식으로 설명하므로 초보자에게 유용합니다. Martin Fowlers 기사는 저와 같은 초보자에게 특히 어려운 기사입니다.
스텁 은 테스트 실행에 도움이됩니다. 어떻게? 테스트 실행에 도움이되는 값을 제공합니다. 이 값 자체는 실제 값이 아니며 테스트를 실행하기 위해 이러한 값을 만들었습니다. 예를 들어 데이터베이스 테이블의 값과 유사한 값을 제공하기 위해 HashMap을 만듭니다. 따라서 데이터베이스와 직접 상호 작용하는 대신 Hashmap과 상호 작용합니다.
모의 는 테스트를 실행하는 가짜 개체입니다. 우리가 주장하는 곳에
C # 및 Moq 프레임 워크를 사용하는 모의 대 스터브 예는 아래를 참조하십시오. Moq에는 Stub에 대한 특별한 키워드가 없지만 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";
}
}
}
스텁 및 모의 테스트 관점 :
Stub 은 정적 방식 즉, 구현 코드 작성 Stub에서 사용자에 의해 수행되는 더미 구현 입니다. 따라서 서비스 정의 및 동적 조건을 처리 할 수 없습니다. 일반적으로 이는 모의 프레임 워크를 사용하지 않고 JUnit 프레임 워크에서 수행됩니다.
Mock 은 또한 더미 구현이지만 Mockito와 같은 Mocking 프레임 워크를 사용하여 구현이 역동적 으로 이루어 졌습니다. 따라서 조건과 서비스 정의를 동적 방식으로 처리 할 수 있습니다. 즉, 런타임시 코드에서 모형을 동적으로 만들 수 있습니다. 따라서 mock을 사용하여 스텁을 동적으로 구현할 수 있습니다.
차이점을 설명하기 위해 대답에 파이썬 예제를 사용했습니다.
Stub -Stubbing은 개발 수명주기 초기에 클래스의 메소드를 구현하는 데 사용되는 소프트웨어 개발 기술입니다. 이들은 일반적으로 알려진 인터페이스의 구현을위한 자리 표시 자로 사용되며, 여기서 인터페이스는 마무리되거나 알려져 있지만 구현은 아직 알려 지거나 마무리되지 않았습니다. 스텁으로 시작합니다. 이는 단순히 함수의 정의를 작성하고 나중에 실제 코드를 남겨둔다는 것을 의미합니다. 장점은 메서드를 잊어 버리지 않고 코드에서 디자인을 보면서 디자인에 대해 계속 생각할 수 있다는 것입니다. 스텁이 정적 응답을 리턴하여 응답을 코드의 다른 부분에서 즉시 사용할 수 있도록 할 수도 있습니다. 스텁 객체는 유효한 응답을 제공하지만 어떤 입력을 전달하더라도 정적이므로 항상 동일한 응답을받습니다.
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
모조품 객체는 모의 테스트 사례에서 사용되며 특정 메소드가 해당 객체에서 호출되는지 확인합니다. 모의 객체는 실제 객체의 동작을 제어 된 방식으로 모방하는 시뮬레이션 된 객체입니다. 일반적으로 다른 객체의 동작을 테스트하기 위해 모의 객체를 만듭니다. Mocks를 사용하면 단위 테스트에 사용할 수 없거나 다루기 어려운 리소스를 시뮬레이션 할 수 있습니다.
mymodule.py :
import os
import os.path
def rm(filename):
if os.path.isfile(filename):
os.remove(filename)
test.py :
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()
이것은 rm을 실행하고 호출 된 매개 변수를 주장하는 매우 기본적인 예입니다. 여기에 표시된 기능뿐만 아니라 객체와 함께 mock을 사용할 수 있으며 값을 반환하여 mock 객체를 사용하여 테스트 용 스텁을 교체 할 수 있습니다.
unittest.mock 에 대한 추가 정보 내용은 python 2.x mock의 참고 사항은 에 포함되어 있지 않지만 pip (pip install mock)를 통해 다운로드 할 수있는 다운로드 가능한 모듈입니다.
또한 Roy Osherove의 "The Unit of Art Testing"을 읽었으며 비슷한 책이 Python 및 Python 예제를 사용하여 작성되면 좋을 것 같습니다. 누구든지 그러한 책을 알고 있다면 공유하십시오. 건배 :)
스텁은 테스트 목적으로 만들어진 가짜 개체입니다. 모의는 예상 통화가 효과적으로 발생했는지 기록하는 스텁입니다.
스텁은 테스트 중에 처리되지 않은 예외를 피하기 위해 사용되는 빈 함수입니다.
function foo(){}
모의는 테스트 중에 OS, 환경 또는 하드웨어 종속성을 피하기 위해 사용되는 인공 기능입니다.
function foo(bar){ window = this; return window.toString(bar); }
주장과 상태 측면에서 :
참고 문헌
거기에 많은 유효한 답변이 있지만이 삼촌 밥 양식을 언급 할 가치가 있다고 생각합니다. https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html
예를 들어 최고의 설명!
모의는 기술적이고 기능적인 대상입니다.
모의는 기술적 인 것 입니다. 바이트 코드 생성 덕분에 실제로 조롱 라이브러리 (EasyMock, JMockit 및 더 최근에는 Mockito가 알려져 있음)에 의해 생성 됩니다.
모의 구현은 우리가 계측 할 수있는 방식으로 생성 됩니다 메소드가 호출 될 때 특정 값을 반환하도록 할 되지만 모의 메소드가 특정 매개 변수 (엄격한 검사) 또는 어떤 매개 변수 ( 엄격한 검사 없음).
모의 인스턴스화 :
@Mock Foo fooMock
행동 기록 :
when(fooMock.hello()).thenReturn("hello you!");
호출 확인 :
verify(fooMock).hello()
Foo 클래스 / 동작을 인스턴스화 / 재정의하는 자연스러운 방법은 아닙니다. 이것이 제가 기술적 인 측면을 언급하는 이유입니다.
그러나 모의는 또한 SUT와 분리해야 할 클래스의 인스턴스이기 때문에 기능적 입니다. 그리고 그것에 기록 된 행동으로, 우리는 그루터기를 할 때와 같은 방식으로 SUT에서 사용할 수 있습니다.
그루터기는 단지 기능적인 객체 일뿐입니다 . 그것은 우리가 SUT와 분리해야하는 클래스의 인스턴스입니다. 즉, 단위 테스트 중에 필요한 스터브 클래스와 모든 동작 픽스처가 명시 적으로 정의되어야합니다.
예를 들어, 스텁 hello()
하려면 Foo
클래스 를 서브 클래 싱 하거나 인터페이스를 구현 해야합니다 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;
}
}
다른 시나리오 : 부작용 방법 (반환 없음)이 있고 해당 메소드가 호출되었는지 확인하려면 스텁 클래스에 부울 또는 카운터를 추가하여 메소드 호출 횟수를 계산해야합니다.
결론
스텁은 종종 단위 테스트를 위해 많은 오버 헤드 / 코드가 필요합니다. 즉시 레코딩 / 검증 기능을 제공하여 모의를 방지합니다.
그렇기 때문에 요즘 스텁 접근법은 훌륭한 모의 라이브러리의 출현과 함께 실제로 거의 사용되지 않습니다.
Martin Fowler 정보 기사 : 모의를 사용하는 동안 "모의가"프로그래머라고 생각하지 않으며 스텁을 피합니다.
그러나 필자는 실제로 필요할 때 모의를 사용하고 (성가신 종속성) 모의가 오버 헤드가되는 종속성이있는 클래스를 테스트 할 때 테스트 슬라이싱 및 미니 통합 테스트를 선호합니다.