code

위조, 조롱 및 스터 빙의 차이점은 무엇입니까?

codestyles 2020. 10. 2. 22:21
반응형

위조, 조롱 및 스터 빙의 차이점은 무엇입니까?


이 용어를 어떻게 사용하는지 알고 있지만 단위 테스트를 위해 faking , mockingstubbing에 대한 정의가 허용되는지 궁금합니다 . 테스트를 위해 어떻게 정의합니까? 각각을 사용할 수있는 상황을 설명하십시오.

사용 방법은 다음과 같습니다.

Fake : 인터페이스를 구현하지만 고정 데이터를 포함하고 논리는 포함하지 않는 클래스입니다. 구현에 따라 단순히 "양호"또는 "불량"데이터를 반환합니다.

Mock : 인터페이스를 구현하고 특정 메서드에서 throw 할 반환 / 예외 값을 동적으로 설정하는 기능을 허용하고 특정 메서드가 호출되었는지 / 호출되지 않았는지 확인하는 기능을 제공하는 클래스입니다.

Stub : 모의 클래스와 비슷 하지만 메서드가 호출되었는지 / 호출되지 않았는지 확인하는 기능을 제공하지 않습니다.

모의와 스텁은 모의 프레임 워크에 의해 직접 생성되거나 생성 될 수 있습니다. 가짜 클래스는 손으로 생성됩니다. 나는 주로 내 클래스와 종속 클래스 간의 상호 작용을 확인하기 위해 모의를 사용합니다. 상호 작용을 확인하고 코드를 통해 대체 경로를 테스트하고 나면 스텁을 사용합니다. 나는 주로 데이터 종속성을 추상화하거나 모의 / 스텁이 매번 설정하기에 너무 지루할 때 가짜 클래스를 사용합니다.


몇 가지 정보를 얻을 수 있습니다.

Mock and Stub에 대한 Martin Fowler 에서

가짜 객체는 실제로 작동하는 구현을 가지고 있지만 일반적으로 프로덕션에 적합하지 않은 지름길을 사용합니다.

스텁 은 테스트 중에 이루어진 통화에 대해 미리 준비된 답변을 제공하며, 일반적으로 테스트를 위해 프로그래밍 된 내용 이외의 항목에는 전혀 응답하지 않습니다. 스텁은 또한 '보낸'메시지를 기억하는 이메일 게이트웨이 스텁 또는 '보낸 메시지 수'와 같은 통화에 대한 정보를 기록 할 수 있습니다.

모의 는 우리가 여기서 이야기하고있는 것입니다 : 그들이받을 것으로 예상되는 호출의 사양을 형성하는 기대치로 미리 프로그래밍 된 객체.

에서 xunitpattern :

가짜 : 우리는 SUT가 의존하는 구성 요소에서 제공하는 것과 동일한 기능의 매우 가벼운 구현을 획득하거나 구축하고 SUT가 실제 대신 사용하도록 지시합니다.

Stub :이 구현은 SUT 내에서 테스트되지 않은 코드 (X 페이지의 프로덕션 버그 참조)를 실행할 값 (또는 예외)으로 SUT의 호출에 응답하도록 구성됩니다. 테스트 스텁 사용에 대한 주요 징후는 SUT의 간접 입력을 제어 할 수 없기 때문에 테스트되지 않은 코드가 있다는 것입니다.

SUT (System Under Test)가 의존하는 객체와 동일한 인터페이스를 구현하는 모의 객체 . SUT에서 메서드 호출의 부작용을 관찰 할 수 없어서 발생하는 테스트되지 않은 요구 사항 (X 페이지의 프로덕션 버그 참조)을 피하기 위해 동작 검증을 수행해야 할 때 모의 객체를 관찰 지점으로 사용할 수 있습니다.

몸소

Mock과 Stub을 사용하여 단순화하려고합니다. 테스트 된 클래스로 설정된 값을 반환하는 객체 인 경우 Mock을 사용합니다. Stub을 사용하여 테스트 할 Interface 또는 Abstract 클래스를 모방합니다. 사실, 그것은 당신이 그것을 무엇이라고 부르는지는 중요하지 않습니다. 그들은 모두 프로덕션에서 사용되지 않는 클래스이며 테스트를위한 유틸리티 클래스로 사용됩니다.


Stub- 메서드 호출에 대해 미리 정의 된 응답을 제공하는 개체입니다.

모의 -기대치를 설정하는 대상.

Fake- 제한된 기능을 가진 객체 (테스트 목적으로), 예 : 가짜 웹 서비스.

Test Double은 스텁, 모의 및 가짜의 일반적인 용어입니다. 그러나 비공식적으로 사람들이 단순히 조롱이라고 부르는 것을 종종 듣게 될 것입니다.


나는이 질문이 너무 오랫동안 존재 해왔고 아직 누구도 Roy Osherove의 "The Art of Unit Testing"에 기반한 답변을 제공하지 않았다는 사실에 놀랐습니다 .

"3.1 Introducing stubs"에서는 stub을 다음과 같이 정의합니다.

스텁은 시스템의 기존 종속성 (또는 협력자)을 제어 할 수있는 대체물입니다. 스텁을 사용하면 종속성을 직접 처리하지 않고도 코드를 테스트 할 수 있습니다.

그리고 스텁과 모의 차이점을 다음과 같이 정의합니다.

모의 대 스텁에 대해 기억해야 할 주요 사항은 모의가 스텁과 비슷하지만 모의 객체에 대해 주장하는 반면 스텁에 대해서는 주장하지 않는다는 것입니다.

Fake는 스텁과 모의 모두에 사용되는 이름입니다. 예를 들어 스텁과 모의 구분에 신경 쓰지 않는 경우입니다.

Osherove가 스텁과 모의를 구분하는 방식은 테스트를 위해 가짜로 사용되는 모든 클래스가 스텁 또는 모의 일 수 있음을 의미합니다. 특정 테스트를위한 것은 전적으로 테스트에서 수표를 작성하는 방법에 따라 다릅니다.

  • 테스트에서 테스트중인 클래스의 값을 확인하거나 실제로 가짜를 제외한 모든 곳에서 가짜가 스텁으로 사용되었습니다. 호출에 의해 반환 된 값을 통해 직접적으로 또는 호출의 결과로 (일부 상태에서) 부작용을 유발하여 간접적으로 테스트중인 클래스에 사용할 값을 제공했습니다.
  • 테스트에서 가짜 값을 확인할 때 모의로 사용되었습니다.

클래스 FakeX가 스텁으로 사용되는 테스트의 예 :

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, cut.SomeProperty);

fake(가) 때문에 인스턴스는 그루터기로 사용됩니다 Assert사용하지 않는 fake전혀.

테스트 클래스 X가 모의로 사용되는 테스트의 예 :

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, fake.SomeProperty);

이 경우에서 Assert값을 확인하여 fake가짜로 만듭니다.

물론 이러한 예는 매우 고안된 것입니다. 그러나 저는이 구별에서 큰 장점을 봅니다. 그것은 당신이 당신의 물건을 테스트하는 방법과 당신의 테스트의 의존성이 어디에 있는지 알 수있게합니다.

나는 Osherove의 것에 동의합니다

순수한 유지 관리 관점에서 볼 때 모의를 사용하는 테스트에서 사용하지 않는 것보다 더 많은 문제가 발생합니다. 그것은 내 경험 이었지만 나는 항상 새로운 것을 배우고 있습니다.

가짜에 대한 주장은 테스트가 전혀 테스트 대상이 아닌 클래스의 구현에 크게 의존하게하므로 피하고 싶은 것입니다. ActualClassUnderTest, 구현이 ClassUsedAsMock변경 되었기 때문에 클래스 테스트가 중단 될 수 있습니다 . 그리고 그것은 나에게 더러운 냄새를 풍깁니다. 에 대한 테스트 ActualClassUnderTest는 가급적 ActualClassUnderTest변경 될 때만 중단되어야합니다 .

나는 가짜에 대한 주장을 작성하는 것이 일반적인 관행이라는 것을 알고 있습니다. 특히 당신이 모의 주의자 유형의 TDD 가입자 인 경우 더욱 그렇습니다. 나는 고전주의 캠프에서 마틴 파울러와 확고한 관계를 유지하고 있고 ( Martin Fowler의 "Mocks are n't Stubs"참조 ) Osherove처럼 상호 작용 테스트 (가짜에 대한 주장으로 만 가능)를 최대한 피하는 것 같습니다.

여기에 정의 된대로 모의를 피해야하는 이유를 재미있게 읽으려면 google for "fowler mockist classicist". 많은 의견을 찾을 수 있습니다.


최고 투표 답변에서 언급했듯이 Martin Fowler는 Mocks Are n't Stubs , 특히 부제목 The Difference Between Mocks and Stubs 에서 이러한 차이점에 대해 논의 하므로 해당 기사를 반드시 읽으십시오.

이러한 것들이 어떻게 다른지에 초점을 맞추기보다는 이것이 왜 별개의 개념 인지 에 초점을 맞추는 것이 더 계몽 적이라고 생각합니다 . 각각 다른 목적을 위해 존재합니다.

가짜

가짜는 그 동작합니다 "자연"을 구현 한 것입니다 만, "진짜"아니다. 이것들은 모호한 개념이고 그래서 다른 사람들은 무엇이 가짜인지에 대해 다른 이해를 가지고 있습니다.

가짜의 한 예는 메모리 내 데이터베이스입니다 (예 : :memory:상점에서 sqlite 사용 ). (데이터가 지속되지 않기 때문에) 프로덕션에는 절대 사용하지 않지만 테스트 환경에서 사용하기에는 데이터베이스로 완벽하게 적합합니다. 또한 "실제"데이터베이스보다 훨씬 가볍습니다.

As another example, perhaps you use some kind of object store (e.g. Amazon S3) in production, but in a test you can simply save objects to files on disk; then your "save to disk" implementation would be a fake. (Or you could even fake the "save to disk" operation by using an in-memory filesystem instead.)

As a third example, imagine an object that provides a cache API; an object that implements the correct interface but that simply performs no caching at all but always returns a cache miss would be a kind of fake.

The purpose of a fake is not to affect the behavior of the system under test, but rather to simplify the implementation of the test (by removing unnecessary or heavyweight dependencies).

Stubs

A stub is an implementation that behaves "unnaturally". It is preconfigured (usually by the test set-up) to respond to specific inputs with specific outputs.

The purpose of a stub is to get your system under test into a specific state. For example, if you are writing a test for some code that interacts with a REST API, you could stub out the REST API with an API that always returns a canned response, or that responds to an API request with a specific error. This way you could write tests that make assertions about how the system reacts to these states; for example, testing the response your users get if the API returns a 404 error.

A stub is usually implemented to only respond to the exact interactions you've told it to respond to. But the key feature that makes something a stub is its purpose: a stub is all about setting up your test case.

Mocks

A mock is similar to a stub, but with verification added in. The purpose of a mock is to make assertions about how your system under test interacted with the dependency.

For example, if you are writing a test for a system that uploads files to a website, you could build a mock that accepts a file and that you can use to assert that the uploaded file was correct. Or, on a smaller scale, it's common to use a mock of an object to verify that the system under test calls specific methods of the mocked object.

Mocks are tied to interaction testing, which is a specific testing methodology. People who prefer to test system state rather than system interactions will use mocks sparingly if at all.

Test doubles

Fakes, stubs, and mocks all belong to the category of test doubles. A test double is any object or system you use in a test instead of something else. Most automated software testing involves the use of test doubles of some kind or another. Some other kinds of test doubles include dummy values, spies, and I/O blackholes.


To illustrate the usage of stubs and mocks, I would like to also include an example based on Roy Osherove's "The Art of Unit Testing".

Imagine, we have a LogAnalyzer application which has the sole functionality of printing logs. It not only needs to talk to a web service, but if the web service throws an error, LogAnalyzer has to log the error to a different external dependency, sending it by email to the web service administrator.

Here’s the logic we’d like to test inside LogAnalyzer:

if(fileName.Length<8)
{
 try
  {
    service.LogError("Filename too short:" + fileName);
  }
 catch (Exception e)
  {
    email.SendEmail("a","subject",e.Message);
  }
}

How do you test that LogAnalyzer calls the email service correctly when the web service throws an exception? Here are the questions we’re faced with:

  • How can we replace the web service?

  • How can we simulate an exception from the web service so that we can test the call to the email service?

  • How will we know that the email service was called correctly or at all?

We can deal with the first two questions by using a stub for the web service. To solve the third problem, we can use a mock object for the email service.

A fake is a generic term that can be used to describe either a stub or a mock.In our test, we’ll have two fakes. One will be the email service mock, which we’ll use to verify that the correct parameters were sent to the email service. The other will be a stub that we’ll use to simulate an exception thrown from the web service. It’s a stub because we won’t be using the web service fake to verify the test result, only to make sure the test runs correctly. The email service is a mock because we’ll assert against it that it was called correctly.

[TestFixture]
public class LogAnalyzer2Tests
{
[Test]
 public void Analyze_WebServiceThrows_SendsEmail()
 {
   StubService stubService = new StubService();
   stubService.ToThrow= new Exception("fake exception");
   MockEmailService mockEmail = new MockEmailService();

   LogAnalyzer2 log = new LogAnalyzer2();
   log.Service = stubService
   log.Email=mockEmail;
   string tooShortFileName="abc.ext";
   log.Analyze(tooShortFileName);

   Assert.AreEqual("a",mockEmail.To); //MOCKING USED
   Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED
   Assert.AreEqual("subject",mockEmail.Subject);
 }
}

It's a matter of making the tests expressive. I set expectations on a Mock if I want the test to describe a relationship between two objects. I stub return values if I'm setting up a supporting object to get me to the interesting behaviour in the test.


the thing that you assert on it,is called a mock object and everything else that just helped the test run, is a stub.


If you are familiar with Arrange-Act-Assert, then one way of explaining the difference between stub and mock that might be useful for you, is that stubs belong to the arrange section, as they are for arranging input state, and mocks belong to the assert section as they are for asserting results against.

Dummies don't do anything. They are just for filling up parameter lists, so that you don't get undefined or null errors. They also exist to satisfy the type checker in strictly typed languages, so that you can be allowed to compile and run.


stub and fake are objects in that they can vary their response based on input parameters. the main difference between them is that a Fake is closer to a real-world implementation than a stub. Stubs contain basically hard-coded responses to an expected request. Let see an example:

public class MyUnitTest {

 @Test
 public void testConcatenate() {
  StubDependency stubDependency = new StubDependency();
  int result = stubDependency.toNumber("one", "two");
  assertEquals("onetwo", result);
 }
}

public class StubDependency() {
 public int toNumber(string param) {
  if (param == “one”) {
   return 1;
  }
  if (param == “two”) {
   return 2;
  }
 }
}

A mock is a step up from fakes and stubs. Mocks provide the same functionality as stubs but are more complex. They can have rules defined for them that dictate in what order methods on their API must be called. Most mocks can track how many times a method was called and can react based on that information. Mocks generally know the context of each call and can react differently in different situations. Because of this, mocks require some knowledge of the class they are mocking. a stub generally cannot track how many times a method was called or in what order a sequence of methods was called. A mock looks like:

public class MockADependency {

 private int ShouldCallTwice;
 private boolean ShouldCallAtEnd;
 private boolean ShouldCallFirst;

 public int StringToInteger(String s) {
  if (s == "abc") {
   return 1;
  }
  if (s == "xyz") {
   return 2;
  }
  return 0;
 }

 public void ShouldCallFirst() {
  if ((ShouldCallTwice > 0) || ShouldCallAtEnd)
   throw new AssertionException("ShouldCallFirst not first thod called");
  ShouldCallFirst = true;
 }

 public int ShouldCallTwice(string s) {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallTwice called before ShouldCallFirst");
  if (ShouldCallAtEnd)
   throw new AssertionException("ShouldCallTwice called after ShouldCallAtEnd");
  if (ShouldCallTwice >= 2)
   throw new AssertionException("ShouldCallTwice called more than twice");
  ShouldCallTwice++;
  return StringToInteger(s);
 }

 public void ShouldCallAtEnd() {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallAtEnd called before ShouldCallFirst");
  if (ShouldCallTwice != 2) throw new AssertionException("ShouldCallTwice not called twice");
  ShouldCallAtEnd = true;
 }

}

Stub, Fakes and Mocks have different meanings across different sources. I suggest you to introduce your team internal terms and agree upon their meaning.

I think it is important to distinguish between two approaches: - behaviour validation (implies behaviour substitution) - end-state validation (implies behaviour emulation)

Consider email sending in case of error. When doing behaviour validation - you check that method Send of IEmailSender was executed once. And you need to emulate return result of this method, return Id of the sent message. So you say: "I expect that Send will be called. And I will just return dummy (or random) Id for any call". This is behaviour validation: emailSender.Expect(es=>es.Send(anyThing)).Return((subject,body) => "dummyId")

When doing state validation you will need to create TestEmailSender that implements IEmailSender. And implement Send method - by saving input to some data structure that will be used for future state verification like array of some objects SentEmails and then it tests you will check that SentEmails contains expected email. This is state validation: Assert.AreEqual(1, emailSender.SentEmails.Count)

From my readings I understood that Behaviour validation usually called Mocks. And State validation usually called Stubs or Fakes.

참고URL : https://stackoverflow.com/questions/346372/whats-the-difference-between-faking-mocking-and-stubbing

반응형