IT박스

모의 객체의 목적은 무엇입니까?

itboxs 2020. 6. 5. 21:05
반응형

모의 객체의 목적은 무엇입니까?


나는 단위 테스트를 처음 접했고, '모의 객체'라는 단어를 많이 들었습니다. 평신도의 관점에서 누군가 모의 객체가 무엇인지, 단위 테스트를 작성할 때 일반적으로 사용되는 것을 설명 할 수 있습니까?


유닛 테스트에 익숙하지 않고 "레이맨의 용어"로 모의 객체를 요청 했으므로 평신도의 예를 들어 보겠습니다.

단위 테스트

이 시스템에 대한 단위 테스트를 상상해보십시오.

cook <- waiter <- customer

일반적으로 cook다음 과 같은 저수준 구성 요소를 테스트하는 것이 쉽습니다 .

cook <- test driver

테스트 드라이버는 단순히 다른 요리를 주문하고 요리사가 각 주문에 맞는 올바른 요리를 반환하는지 확인합니다.

다른 구성 요소의 동작을 활용하는 웨이터와 같은 중간 구성 요소를 테스트하기가 더 어렵습니다. 순진한 테스터는 cook 컴포넌트를 테스트 한 것과 동일한 방식으로 웨이터 컴포넌트를 테스트 할 수 있습니다.

cook <- waiter <- test driver

시험 운전사는 다른 접시를 주문하고 웨이터가 올바른 접시를 반환하는지 확인합니다. 불행히도, 웨이터 구성 요소에 대한이 테스트는 쿡 구성 요소의 올바른 동작에 따라 달라질 수 있습니다. 요리사 구성 요소가 비 결정적 행동 (메뉴에 요리사의 놀라움이 포함 된 메뉴), 많은 의존성 (요리사가 전체 직원없이 요리하지 않음)과 같은 테스트에 익숙하지 않은 특성이있는 경우 이러한 종속성은 더욱 악화됩니다. 자원 (일부 요리는 고가의 재료가 필요하거나 요리하는데 1 시간이 걸립니다)

이것은 웨이터 테스트이므로 이상적으로는 요리사가 아닌 웨이터 만 테스트하고 싶습니다. 특히, 웨이터가 고객의 주문을 요리사에게 정확하게 전달하고 요리사의 음식을 고객에게 올바르게 전달하도록합니다.

단위 테스트는 단위를 독립적으로 테스트하는 것을 의미하므로 Fowler가 테스트 배가 (더미, 스텁, 가짜, 모의)라고 부르는 것을 사용하여 테스트 대상 구성 요소 (웨이터)를 격리하는 것이 더 나은 방법입니다 .

    -----------------------
   |                       |
   v                       |
test cook <- waiter <- test driver

여기서 테스트 쿡은 테스트 드라이버와 함께 "cahoots"입니다. 테스트중인 시스템은 생산 코드를 변경하지 않고 (예 : 웨이터 코드를 변경하지 않고) 웨이터와 함께 작업 할 수 있도록 테스트 쿡을 쉽게 대체 ( 주입 ) 할 수 있도록 설계되었습니다 .

모의 객체

이제 테스트 쿡 (테스트 더블)은 다른 방식으로 구현 될 수 있습니다.

  • 가짜 요리사-냉동 저녁 식사와 전자 레인지를 사용하여 요리사 인 척하는 사람,
  • 스텁 쿡 (Stub Cook)-주문한 제품에 상관없이 항상 핫도그를 제공하는 핫도그 공급 업체
  • 모의 쿡-찌르기 작업에서 쿡인 척하는 대본을 따르는 비밀 경찰.

가짜 대 스터브 대 모의 대 인형에 대한 자세한 내용은 Fowler의 기사를 참조하십시오 . 그러나 지금은 모의 요리사에 초점을 맞 춥니 다.

    -----------------------
   |                       |
   v                       |
mock cook <- waiter <- test driver

웨이터 구성 요소를 테스트하는 단위의 상당 부분은 웨이터가 요리 구성 요소와 상호 작용하는 방식에 중점을 둡니다. 모의 기반 접근 방식은 올바른 상호 작용이 무엇인지 완전히 지정하고 잘못되었을 때 감지하는 데 중점을 둡니다.

모의 객체는 테스트 중에 발생해야 할 사항 (예 : 호출을 호출 할 메소드 등)을 미리 알고 있으며 모의 객체는 어떻게 반응해야 하는지를 알고 있습니다 (예 : 제공 할 반환 값). 모의는 실제로 일어나는 일이 일어날 일과 다른지 여부를 나타냅니다. 테스트 케이스마다 예상되는 동작을 실행하기 위해 각 테스트 케이스에 대해 사용자 정의 모의 객체를 처음부터 만들 수 있지만, 모의 프레임 워크는 이러한 동작 사양을 테스트 케이스에 직접 명확하고 쉽게 표시 할 수 있도록 노력합니다.

모의 테스트를 둘러싼 대화는 다음과 같습니다.

요리사조롱하는 테스트 드라이버 : 핫도그 주문을 기대하고 그 에게이 더미 핫도그를 제공하십시오

웨이터 에게 테스트 드라이버 (고객으로 배치) : 나는 핫도그가
웨이터모의 쿡으로 만들고 싶습니다 : 1 핫도그웨이터로
모의합니다 : 주문 : 1 핫도그 준비 (더미 핫도그에게 웨이터에게 제공) 웨이터드라이버테스트합니다. : 여기 핫도그가 있습니다 (드라이버에게 더미 핫도그 제공)

테스트 드라이버 : 테스트 성공!

그러나 웨이터가 새롭기 때문에 다음과 같은 일이 발생할 수 있습니다.

요리사조롱하는 테스트 드라이버 : 핫도그 주문을 기대하고 그 에게이 더미 핫도그를 제공하십시오

웨이터 에게 테스트 드라이버 (고객으로 배치) : 나는 핫도그가 요리조롱하기 위해
웨이터부탁합니다 : 1 햄버거 제발 모의 요리사 가 테스트를 중지하십시오 : 나는 핫도그 주문을 기대한다고 들었습니다!

테스트 드라이버 가 문제를 확인했습니다 : 테스트 실패! -웨이터가 주문을 변경했습니다

또는

요리사조롱하는 테스트 드라이버 : 핫도그 주문을 기대하고 그 에게이 더미 핫도그를 제공하십시오

웨이터 에게 테스트 드라이버 (고객으로 배치) : 나는 핫도그가
웨이터모의 쿡으로 만들고 싶습니다 : 1 핫도그웨이터로
모의합니다 : 주문 : 1 핫도그 준비 (더미 핫도그에게 웨이터에게 제공) 웨이터드라이버테스트합니다. : 여기 감자 튀김이 있습니다 (프렌치 프라이는 다른 순서로 드라이버를 테스트합니다)

테스트 드라이버 는 예상치 못한 감자 튀김을 테스트합니다 : 테스트 실패! 웨이터가 그릇된 그릇을 주었다

대조적 인 스터브 기반 예제가 없으면 모의 객체와 스터브의 차이점을 명확하게 파악하기 어려울 수 있지만이 답변은 이미 너무 길었습니다 :-)

또한 이것은 매우 단순한 예이며, 모의 프레임 워크는 구성 요소에서 포괄적 인 테스트를 지원하기 위해 예상되는 동작의 매우 정교한 사양을 허용합니다. 더 많은 정보를 위해 모의 객체 및 모의 프레임 워크에 대한 많은 자료가 있습니다.


모의 객체는 실제 객체를 대체하는 객체입니다. 객체 지향 프로그래밍에서 모의 ​​객체는 실제 객체의 동작을 제어 된 방식으로 모방하는 시뮬레이션 된 객체입니다.

컴퓨터 프로그래머는 일반적으로 자동차 디자이너가 충돌 테스트 더미를 사용하여 차량 충격에서 사람의 동적 행동을 시뮬레이션하는 것과 거의 같은 방식으로 다른 객체의 행동을 테스트하기 위해 모의 객체를 만듭니다.

http://en.wikipedia.org/wiki/Mock_object

Mock objects allow you to set up test scenarios without bringing to bear large, unwieldy resources such as databases. Instead of calling a database for testing, you can simulate your database using a mock object in your unit tests. This frees you from the burden of having to set up and tear down a real database, just to test a single method in your class.

The word "Mock" is sometimes erroneously used interchangeably with "Stub." The differences between the two words are described here. Essentially, a mock is a stub object that also includes the expectations (i.e. "assertions") for proper behavior of the object/method under test.

For example:

class OrderInteractionTester...
  public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    Mock warehouse = mock(Warehouse.class);
    Mock mailer = mock(MailService.class);
    order.setMailer((MailService) mailer.proxy());

    mailer.expects(once()).method("send");
    warehouse.expects(once()).method("hasInventory")
      .withAnyArguments()
      .will(returnValue(false));

    order.fill((Warehouse) warehouse.proxy());
  }
}

Notice that the warehouse and mailer mock objects are programmed with the expected results.


Mock objects are simulated objects that mimic the behavior of the real ones. Typically you write a mock object if:

  • The real object is too complex to incorporate it in a unit testing (For example a networking communication, you can have a mock object that simulate been the other peer)
  • The result of your object is non deterministic
  • The real object is not yet available

A Mock object is one kind of a Test Double. You are using mockobjects to test and verify the protocol/interaction of the class under test with other classes.

Typically you will kind of 'program' or 'record' expectations : method calls you expect your class to do to an underlying object.

Let's say for example we are testing a service method to update a field in a Widget. And that in your architecture there is a WidgetDAO which deals with the database. Talking with the database is slow and setting it up and cleaning afterwards is complicated, so we will mock out the WidgetDao.

let's think what the service must do : it should get a Widget from the database, do something with it and save it again.

So in pseudo-language with a pseudo-mock library we would have something like :

Widget sampleWidget = new Widget();
WidgetDao mock = createMock(WidgetDao.class);
WidgetService svc = new WidgetService(mock);

// record expected calls on the dao
expect(mock.getById(id)).andReturn(sampleWidget);   
expect(mock.save(sampleWidget);

// turn the dao in replay mode
replay(mock);

svc.updateWidgetPrice(id,newPrice);

verify(mock);    // verify the expected calls were made
assertEquals(newPrice,sampleWidget.getPrice());

In this way we can easily test drive development of classes which depend on other classes.


I highly recommend a great article by Martin Fowler explaining what exactly are mocks and how they differ from stubs.


When unit testing some part of a computer program you ideally want to test just the behavior of that particular part.

For example, look at the pseudo-code below from an imaginary piece of a program that uses another program to call print something:

If theUserIsFred then
    Call Printer(HelloFred)
Else
   Call Printer(YouAreNotFred)
End

If you were testing this, you would mainly want to test the part that looks at if the user is Fred or not. You don't really want to test the Printer part of things. That would be another test.

This is where Mock objects come in. They pretend to be other types of things. In this case you would use a Mock Printer so that it would act just like a real printer, but wouldn't do inconvenient things like printing.


There are several other types of pretend objects that you can use that aren't Mocks. The main thing that makes Mocks Mocks is that they can be configured with behaviors and with expectations.

Expectations allow your Mock to raise an error when it is used incorrectly. So in the above example, you could want to be sure that the Printer is called with HelloFred in the "user is Fred" test case. If that doesn't happen your Mock can warn you.

Behavior in Mocks means that for example, of your code did something like:

If Call Printer(HelloFred) Returned SaidHello Then
    Do Something
End

Now you want to test what your code does when the Printer is called and returns SaidHello, so you can set up the Mock to return SaidHello when it is called with HelloFred.

One good resource around this is Martin Fowlers post Mocks Aren't Stubs


Mock and stub objects are a crucial part of unit testing. In fact they go a long way to make sure you are testing units, rather than groups of units.

In a nutshell, you use stubs to break SUT's (System Under Test) dependency on other objects and mocks to do that and verify that SUT called certain methods/properties on the dependency. This goes back to fundamental principles of unit testing - that the tests should be easily readable, fast and not requiring configuration, which using all real classes might imply.

Generally, you can have more than one stub in your test, but you should only have one mock. This is because mock's purpose is to verify behavior and your test should only test one thing.

Simple scenario using C# and Moq:

public interface IInput {
  object Read();
}
public interface IOutput {
  void Write(object data);
}

class SUT {
  IInput input;
  IOutput output;

  public SUT (IInput input, IOutput output) {
    this.input = input;
    this.output = output;
  }

  void ReadAndWrite() { 
    var data = input.Read();
    output.Write(data);
  }
}

[TestMethod]
public void ReadAndWriteShouldWriteSameObjectAsRead() {
  //we want to verify that SUT writes to the output interface
  //input is a stub, since we don't record any expectations
  Mock<IInput> input = new Mock<IInput>();
  //output is a mock, because we want to verify some behavior on it.
  Mock<IOutput> output = new Mock<IOutput>();

  var data = new object();
  input.Setup(i=>i.Read()).Returns(data);

  var sut = new SUT(input.Object, output.Object);
  //calling verify on a mock object makes the object a mock, with respect to method being verified.
  output.Verify(o=>o.Write(data));
}

In the above example I used Moq to demonstrate stubs and mocks. Moq uses the same class for both - Mock<T> which makes it a bit confusing. Regardless, at runtime, the test will fail if output.Write is not called with data as parameter, whereas failure to call input.Read() will not fail it.


As another answer suggested via a link to "Mocks Aren't Stubs", mocks are a form of "test double" to use in lieu of a real object. What makes them different from other forms of test doubles, such as stub objects, is that other test doubles offer state verification (and optionally simulation) whereas mocks offer behavior verification (and optionally simulation).

With a stub, you might call several methods on the stub in any order (or even repitiously) and determine success if the stub has captured a value or state you intended. In contrast, a mock object expects very specific functions to be called, in a specific order, and even a specific number of times. The test with a mock object will be considered "failed" simply because the methods were invoked in a different sequence or count - even if the mock object had the correct state when the test concluded!

In this way, mock objects are often considered more tightly coupled to the SUT code than stub objects. That can be a good or bad thing, depending on what you are trying to verify.


Part of the point of using mock objects is that they don't have to be really implemented according to spec. They can just give dummy responses. E.g. if you have to implement components A and B, and both "call" (interact with) each other, then you can't test A until B is implemented, and vice versa. In test-driven development, this is a problem. So you create mock ("dummy") objects for A and B, that are very simple, but they give some sort of response when they're interacted with. That way, you can implement and test A using a mock object for B.


For php and phpunit is well explained in phpunit documentaion. see here phpunit documentation

In simple word mocking object is just dummy object of your original one and is return its return value, this return value can be used in the test class

참고URL : https://stackoverflow.com/questions/3622455/what-is-the-purpose-of-mock-objects

반응형