문제

얼마 전에 나는 다음을 읽었다. Mock은 스텁이 아니다 Martin Fowler의 기사입니다. 추가된 복잡성과 관련하여 외부 종속성이 약간 두렵다는 점을 인정해야 하므로 다음과 같이 질문하고 싶습니다.

단위 테스트를 할 때 사용하는 가장 좋은 방법은 무엇입니까?

테스트 중인 메서드의 종속성을 자동으로 모의하기 위해 항상 모의 프레임워크를 사용하는 것이 더 낫습니까, 아니면 테스트 스텁과 같은 더 간단한 메커니즘을 사용하는 것을 선호합니까?

도움이 되었습니까?

해결책

만트라에는 '효과가 있을 수 있는 가장 간단한 것을 선택하라.'가 있습니다.

  1. 가짜 수업을 통해 작업을 완료할 수 있다면 함께 가세요.
  2. 모의할 여러 메서드가 있는 인터페이스가 필요한 경우 모의 프레임워크를 사용하세요.

모의 사용을 피하세요 언제나 테스트를 취약하게 만들기 때문입니다.이제 모의 인터페이스나 구현이 변경되는 경우 테스트는 구현에서 호출하는 메서드에 대한 복잡한 지식을 갖게 됩니다.테스트가 중단되었습니다.SUT를 실행하는 대신 테스트를 실행하는 데 추가 시간을 소비하게 되므로 이는 좋지 않습니다. 테스트는 구현과 부적절하게 친밀해서는 안됩니다.
그러니 최선의 판단을 하세요..나는 n>>3 메소드를 사용하여 가짜 클래스를 작성하고 업데이트하는 데 도움이 될 때 모의를 선호합니다.

업데이트 에필로그/심의:
(모의 테스트의 예를 제공한 Toran Billups에게 감사드립니다.아래 참조)
안녕 Doug, 내 생각에 우리는 또 다른 성전, 즉 Classic TDDers 대 Mockist TDDers로 넘어간 것 같습니다.나는 전자에 속한다고 생각한다.

  • test#101 Test_ExportProductList에 있는 경우 IProductService.GetProducts()에 새 매개변수를 추가해야 합니다.나는 이 테스트를 녹색으로 만듭니다.나는 리팩토링 도구를 사용하여 다른 모든 참조를 업데이트합니다.이제 나는 이 멤버를 호출하는 모든 모의객체 테스트가 이제 폭발하는 것을 발견했습니다.그런 다음 돌아가서 모든 테스트를 업데이트해야 합니다. 시간 낭비입니다.ShouldPopulateProductsListOnViewLoadWhenPostBackIsFalse가 실패한 이유는 무엇입니까?코드가 깨졌기 때문일까요?오히려 테스트가 깨졌습니다.나는 선호한다 한 번의 테스트 실패 = 수정할 곳 1곳.조롱 주파수는 이에 반대됩니다.스텁이 더 좋을까요?만약 내가 fake_class.GetProducts()를 가지고 있었다면..여러 예상 호출에 대한 산탄총 수술 대신 변경할 수 있는 한 곳이 있습니다.결국 스타일의 문제죠..공통 유틸리티 메소드 MockHelper.SetupExpectForGetProducts()가 있다면 그것으로도 충분합니다.그러나 이것이 흔하지 않다는 것을 알게 될 것입니다.
  • 시험 이름에 흰색 줄을 표시하면 시험 내용을 읽기 어렵습니다.모의 프레임워크에 대한 많은 배관 코드는 수행되는 실제 테스트를 숨깁니다.
  • 모의 프레임워크의 특별한 특징을 배워야 합니다.

다른 팁

나는 일반적으로 기대 때문에 모의를 사용하는 것을 선호합니다.값을 반환하는 스텁에서 메서드를 호출하면 일반적으로 값만 반환됩니다.그러나 모의 객체에서 메서드를 호출하면 값을 반환할 뿐만 아니라 애초에 해당 메서드가 호출되었다고 설정했다는 기대도 강제됩니다.즉, 기대치를 설정한 다음 해당 메서드를 호출하지 않으면 예외가 발생합니다.당신이 기대를 설정하면, 당신은 본질적으로 "이 방법이 호출되지 않으면 무언가 잘못되었다"고 말하는 것입니다. 그리고 그 반대는 사실입니다. 만약 당신이 모의에서 메소드를 부르고 기대를 설정하지 않았다면, "이 방법을 기대하지 않았을 때이 방법을 부르는 것은 무엇을하고 있습니까?"라는 예외를 던질 것입니다.

때로는 호출하는 모든 메서드에 대한 기대치를 원하지 않기 때문에 일부 모의 프레임워크에서는 설정한 기대치만 적용되고 다른 모든 메서드 호출은 처리된다는 점에서 모의/스텁 하이브리드와 같은 "부분" 모의를 허용합니다. 값만 반환한다는 점에서는 스텁과 비슷합니다.

하지만 내가 생각할 수 있는 스텁을 사용할 수 있는 유효한 장소 중 하나는 레거시 코드에 테스트를 도입할 때입니다.때로는 모의 작업을 쉽거나 가능하게 만들기 위해 모든 것을 리팩토링하는 것보다 테스트 중인 클래스를 서브클래싱하여 스텁을 만드는 것이 더 쉽습니다.

그리고 이에...

모의 객체는 테스트를 취약하게 만들기 때문에 항상 사용하지 마세요.이제 모의 인터페이스가 변경되면 테스트에서 구현에 의해 호출되는 메서드에 대한 복잡한 지식을 갖게 됩니다.테스트가 중단되었습니다.그러니 최선의 판단을 하시기 바랍니다..<

...인터페이스가 변경되면 테스트가 중단되는 것이 더 나을 것이라고 말합니다.단위 테스트의 핵심은 현재 존재하는 코드를 정확하게 테스트하는 것이기 때문입니다.

조합을 사용하는 것이 가장 좋으며, 스스로 판단해야 합니다.내가 사용하는 지침은 다음과 같습니다.

  • 외부 코드에 대한 호출이 코드의 예상되는(외부 지향) 동작의 일부인 경우 이를 테스트해야 합니다.모의를 사용하십시오.
  • 호출이 실제로 외부 세계가 신경 쓰지 않는 구현 세부 사항인 경우 스텁을 선호하십시오.하지만:
  • 테스트된 코드의 이후 구현이 실수로 스텁을 우회할 수 있다는 우려가 있고 그런 일이 발생하는지 확인하려면 모의 코드를 사용하세요.테스트를 코드에 연결하지만 이는 스텁이 더 이상 충분하지 않고 테스트를 다시 작업해야 한다는 점을 알아차리기 위한 것입니다.

두 번째 종류의 조롱은 일종의 필요악이다.여기서 실제로 일어나는 일은 스텁을 사용하든 모의 객체를 사용하든 어떤 경우에는 원하는 것보다 더 많은 코드를 결합해야 한다는 것입니다.그런 일이 발생하면 스텁보다 모의를 사용하는 것이 더 낫습니다. 왜냐하면 연결이 끊어지고 코드가 더 이상 테스트에서 생각한 대로 작성되지 않을 때를 알 수 있기 때문입니다.이 작업을 수행할 때 테스트에 주석을 남겨서 이를 위반하는 사람이 자신의 코드가 잘못된 것이 아니라는 것을 알 수 있도록 하는 것이 가장 좋습니다.

그리고 다시 말하지만 이것은 코드 냄새이자 최후의 수단입니다.이 작업을 자주 수행해야 한다면 테스트 작성 방식을 다시 생각해 보세요.

어떤 유형의 테스트를 수행하고 있는지에 따라 다릅니다.동작 기반 테스트를 수행하는 경우 종속성과의 일부 상호 작용이 발생하는지 확인할 수 있도록 동적 모의가 필요할 수 있습니다.그러나 상태 기반 테스트를 수행하는 경우 스텁이 필요할 수 있으므로 값 등을 확인할 수 있습니다.

예를 들어 아래 테스트에서는 속성 값이 설정되었는지 확인할 수 있도록 뷰를 스텁아웃했습니다(상태 기반 테스트).그런 다음 서비스 클래스의 동적 모의를 생성하여 테스트(상호작용/행동 기반 테스트) 중에 특정 메서드가 호출되는지 확인할 수 있습니다.

<TestMethod()> _
Public Sub Should_Populate_Products_List_OnViewLoad_When_PostBack_Is_False()
    mMockery = New MockRepository()
    mView = DirectCast(mMockery.Stub(Of IProductView)(), IProductView)
    mProductService = DirectCast(mMockery.DynamicMock(Of IProductService)(), IProductService)
    mPresenter = New ProductPresenter(mView, mProductService)
    Dim ProductList As New List(Of Product)()
    ProductList.Add(New Product())
    Using mMockery.Record()
        SetupResult.For(mView.PageIsPostBack).Return(False)
        Expect.Call(mProductService.GetProducts()).Return(ProductList).Repeat.Once()
    End Using
    Using mMockery.Playback()
        mPresenter.OnViewLoad()
    End Using
    'Verify that we hit the service dependency during the method when postback is false
    Assert.AreEqual(1, mView.Products.Count)
    mMockery.VerifyAll()
End Sub

통계주의자 vs.상호 작용.역할과 관계에 대해 생각해 보세요.객체가 작업을 완료하기 위해 이웃과 협력하는 경우 해당 관계(인터페이스에 표현됨)는 모의 객체를 사용하여 테스트할 후보입니다.객체가 약간의 동작을 포함하는 단순한 값 객체인 경우 직접 테스트하세요.나는 손으로 모의(또는 스텁)를 작성하는 요점을 알 수 없습니다.이것이 우리 모두가 그것에서 벗어나 리팩토링을 시작하고 리팩토링한 방법입니다.

더 긴 토론을 원하시면 다음을 살펴보십시오. http://www.mockobjects.com/book

이 질문에 대한 Luke Kanies의 토론을 읽어보세요. 이 블로그 게시물.그가 언급한 Jay Fields의 게시물 이는 [ruby's/mocha's와 동등한 것] stub_everything을 사용하는 것이 테스트를 더욱 강력하게 만드는 것이 더 낫다는 것을 암시합니다.Fields의 마지막 말을 인용하자면:"Mocha를 사용하면 스텁을 정의하는 것만큼 모의를 정의하는 것이 쉽지만 그렇다고 해서 항상 모의를 선호해야 한다는 의미는 아닙니다.사실 저는 일반적으로 스텁을 선호하고 필요할 때 모의를 사용합니다."

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top