2012-12-18 1 views
5

"서비스 레이어"/ "어플리케이션 외관 레이어"메소드를 테스트하려고합니다.FakeItEasy는 MustHaveHappened가 발생하지 않았다고 말합니다.하지만 그랬습니다.

// Create a new order in the database for a customer. Given a customer id, 
// will create a new order and return an OrderDto for use in the presentation 
// layer. 
public OrderDto CreateOrderForCustomer(int customerId) 
{ 
    // Find the customer 
    var customer = _customerRepository.GetCustomerById(customerId); 

    // Create an order and apply special logic to get it ready for use. 
    var orderFactory = new OrderFactory(); 
    var order = orderFactory.CreateOrder(customer); 

    // IMPORTANT: This is what I'm trying to unit test ... 
    _orderRepository.Save(order); 

    order.Status = "Editing"; 

    // Using AutoMapper to turn this into a DTO that will be returned 
    // to the Presentation layer. The Mappings are created in the 
    // constructor and not depicted in this code snippet. 
    var orderDto = Mapper.Map<Order, OrderDto>(order); 

    return orderDto; 
} 

(.. 나는 명확성을 위해 여기에 풍부한 메모를 추가했습니다 ... 참고 나는 보통이 수다스러운 아니에요)

이 방법의 가입일 : 이것은 단위 테스트에 내가 노력하고있어 방법 직업은 Domain Layer 메소드와 Persistence Layer 메소드를 조종하여 빈 주문을 작성하고이를 유지 한 다음 간단한 DTO로 반환하는 것입니다. FakeItEasy의 훌륭한 작업이라고 생각했습니다. 중요한 메소드가 FakeItEasy의 MustHaveHappened()를 사용하여 제대로 호출되도록 조정됩니다.

그래서 염두에두고, 여기에 내가 만든 단위 테스트입니다 :

[TestMethod] 
public void CreateOrderForCustomer_ValidCustomer_CreatesNewOrder() 
{ 
    // Arrange 
    var customer = _customerRepository.GetCustomerById(1); 
    Assert.AreEqual(0, customer.Orders.Count); 

    // Act 
    var orderDto = _orderEntryService.CreateOrderForCustomer(1); 

    // Assert 

    // Here I'm trying to make sure to re-create the order that was actually 
    // sent into the _customerRepository.Save() ... I should be able to 
    // simple un-map the OrderDto back to an Order, and undo the property 
    // change. 
    var order = Mapper.Map<OrderDto, Order>(orderDto); 
    order.Status = "New"; 

    A.CallTo(() => _customerRepository.GetCustomerById(1)).MustHaveHappened(); 

    // **THIS CAUSES AN EXCEPTION** 
    A.CallTo(() => _orderRepository.Save(order)).MustHaveHappened(); 
    Assert.AreEqual(1, customer.Orders.Count); 
} 

단위 테스트에서, 나는 시험 방법에서 생성 된 실제 주문에 액세스 할 수 없습니다, 나는 시도 다음으로 최선을 다하십시오 ... 테스트중인 메소드에 의해 반환 된 Order의 DTO 버전을 가져오고 Order의 DTO 버전을 도메인 모델 Order의 새 인스턴스로 다시 매핑하고 속성이 동일한 지 확인하십시오 FakeItEasy의 MustHaveHappened()에게 보내기 전에.

단위 테스트를 디버깅하고 실제 주문 속성과 FAKED 주문 속성을 비교해 보았습니다 ... 동일하다는 점을 확신합니다. 또한 디버깅을 통해 _customerRepository.Save (order)가 실제로 호출되고 있음을 확인할 수 있습니다.

질문 이 (.MustHaveHappened되어) 나는 기본적으로 주문 개체의 두 가지 경우에 보낸다 때문에 실패 - 그 속성이 동일하더라도? 프로퍼티는 같지만, FakeItEasy는 메소드 호출이 발생했는지 확인하기 위해 입력 매개 변수의 동일한 인스턴스가 필요합니까?

또한 이런 종류의 테스트 (예 : 오케스트레이션/서비스/"애플리케이션 외관"/ what-ever-you-want-to-call-it 레이어 방법)에 대한 제안 사항은 무엇입니까?

답변

16

본질적으로 Order 객체의 서로 다른 두 인스턴스를 전송하기 때문에 .MustHaveHappened()가 실패합니다. 속성이 동일하더라도?

예. FakeItEasy는 참조 유형이 인 경우 (클래스가 대체하지 않는 한) .Equals을 사용합니다. 기본값은이며 동등 함을 나타냅니다.

(...) FakeItEasy는 메소드 호출이 발생했는지 확인하기 위해 입력 매개 변수의 동일한 인스턴스가 필요합니까? 이 문제는 코드와 함께 문제를 밝혀,

A.CallTo(() => _orderRepository.Save(A<Order>.That.Matches(o => 
    o.Status == "New" && 
    o.Id == 10 
))).MustHaveHappened(); 

을하지만 :

번호 당신은 다음과 같이 정의 인수 매칭을 할 수 있습니다. 당신의 샘플로부터 당신은 의존성으로서 _customerRepository을 주입하고 있다는 것이 분명합니다. 훌륭합니다. 너 OrderFactory과 똑같이하지 그래? 인터페이스/기본 클래스 종속성을 통해 주입 된 경우 쉽게 모방 (가짜)하여 현재 문제가 존재하지 않을 수 있습니다.

당신은 내가 공장 주입 좋을 것 코드를 변경할 수있는 경우 (단순 지침 다음 -! "더 new의 좋은 소식입니다"). 그렇지 않은 경우 위의 샘플에서와 같이 사용자 지정 matcher를 사용하여 주문 속성을 확인하십시오.

+0

감사합니다. 감사합니다. 감사합니다. 특히 OrderFactory에 관한 훌륭한 팁! –

+0

참조 비교가 사용되는 것은 아니며 Equals 구현이 사용된다는 것입니다. 그래서 Equals-method를 오버라이드하여 동등한 명령을 동등하다고 생각하면 문제도 해결할 수 있습니다. –

+0

@ PatrikHägne : 예, 정신 지름길이 지나치게 달려있어 알림을 보내 주셔서 감사합니다. –

관련 문제