2013-08-26 3 views
4

인터페이스 나 추상 클래스가없는 프로젝트를 상속했습니다. 구체적 클래스 만 있으므로 단위 테스트를 도입하고 싶습니다. 클래스에는 비즈니스 로직 및 데이터 로직을 포함하는 많은 함수가 포함되어 있습니다. 고체의 모든 규칙을 깨기 (http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29).유닛 콘크리트 클래스 테스트

나는 생각했다. 나는 저조한 클래스 각각에 대한 인터페이스를 생성하고 모든 함수를 노출하려고 생각하고있었습니다. 그렇다면 적어도 수업을 모의 할 수 있습니다.

나는 단위 테스트에 비교적 익숙하다 (적절한 장소에서 인터페이스를 사용하여 잘 개발 된 프로젝트 경험이있다). 단 한 번의 테스트를 위해 모든 구체적인 클래스 (모든 함수와 서브 루틴을 표시)에 대한 인터페이스를 만드는 것이 좋습니다.

나는 이것을 연구하는 데 시간을 할애했지만 답변을 찾지 못했습니다.

답변

1

는 네,하지만, 인터페이스를 갖는 주입 종속성을 가진보다 우선 순위 이하, 좋은 시작이다)

는 "추출물 및 재정"에 대한 예를 들어 필요하면 알려주세요. 모든 레거시 클래스가 인터페이스를 얻지 만 내부적으로 숨겨진다면 여전히 상호 의존적이므로 클래스는 여전히 테스트하기가 쉽지 않습니다. 예를 들어, 당신이이 닮은 두 개의 클래스를 가지고 가정 해 봅시다 :

Public Class LegacyDataAccess 
    Public Function GetAllSales() As List(Of SaleDto) 
     ' Do work with takes a long time to run against real DB 
    End Function 
End Class 

Public Class LegacyBusiness 
    Public Function GetTotalSales() As Integer 
     Dim dataAccess As New LegacyDataAccess() 
     Dim sales As List(Of SaleDto) = dataAccess.GetAllSales() 
     ' Calculate total sales 
    End Function 
End Class 

나는 당신이 이미 ... 말을하는지 "나는 레거시 코드는 적어도 층이 아니라 좋겠어요"하지만 수 있습니다 알고 이를 테스트하기 어려운 기존 코드의 예제로 사용하십시오. 테스트하기 어려운 이유는 코드가 데이터베이스에 도달하여 데이터베이스에서 시간이 많이 소요되는 쿼리를 실행 한 다음 그 결과를 계산하기 때문입니다. 따라서 현재 상태에서 테스트하려면 먼저 테스트 데이터 묶음을 데이터베이스에 작성한 다음 코드를 실행하여 삽입 된 데이터를 기반으로 올바른 결과를 반환하는지 확인해야합니다. 때문에 그런 테스트를 작성할 수있는 것은 문제가있다 : 그것은 테스트를 셋업에 코드를 작성하는 고통이다

  • 포함
  • 제대로 작동 외부 데이터베이스에 의존하기 때문에 시험은 부서지기 쉬운 것입니다 그것에 모든 올바른 지원 데이터는
  • 시험은 올바르게 관찰, 인터페이스는 단위 테스트에 매우 중요하다

실행하는 데 시간이 너무 오래 걸릴 것입니다. 당신이 추천 그래서, 그것은 수 있는지 확인하기 위해 인터페이스를 추가 할 수 있습니다 그것을 시험에 쉽게 하나를

Public Interface ILegacyDataAccess 
    Function GetAllSales() As List(Of SaleDto) 
End Interface 

Public Interface ILegacyBusiness 
    Function GetTotalSales() As Integer 
End Interface 

Public Class LegacyDataAccess 
    Implements ILegacyDataAccess 

    Public Function GetAllSales() As List(Of SaleDto) _ 
      Implements ILegacyDataAccess.GetAllSales 
     ' Do work with takes a long time to run against real DB 
    End Function 
End Class 

Public Class LegacyBusiness 
    Implements ILegacyBusiness 

    Public Function GetTotalSales() As Integer _ 
      Implements ILegacyBusiness.GetTotalSales 
     Dim dataAccess As New LegacyDataAccess() 
     Dim sales As List(Of SaleDto) = dataAccess.GetAllSales() 
     ' Calculate total sales 
    End Function 
End Class 

그래서 지금 우리가 인터페이스를 가지고 있지만, 정말, 어떻게이 쉽게 어떤 테스트 할 수 있도록 하는가? 이제 동일한 인터페이스를 구현하는 모의 데이터 액세스 객체를 쉽게 만들 수 있습니다.하지만 실제로 핵심적인 문제는 아닙니다. 문제는 실제 객체의 대신이라는 모의 데이터 액세스 객체를 사용하도록 비즈니스 객체를 얻는 방법입니다. 그렇게하려면 의존성 주입을 도입하여 리팩토링을 한 차원 높여야합니다.진짜 범인은 비즈니스 클래스의 다음 줄에 New 키워드입니다 :

Dim dataAccess As New LegacyDataAccess() 

비즈니스 클래스는 명확하게 데이터 액세스 클래스에 따라 다르지만 현재는 그 사실을 숨기고있다. 의존성에 대해 거짓말하고 있습니다. 말하자면, 쉽고 간단합니다.이 메서드를 호출하면 결과를 반환 할 것입니다. 정말로, 그것은 그것보다 더 많은 것을 필요로합니다. 이제, 우리가 종속의 거짓말에서 중지와 같이, 그것은 그래서 당당를 언급했다 가정 해 봅시다 :

Public Class LegacyBusiness 
    Implements ILegacyBusiness 

    Public Sub New(dataAccess As ILegacyDataAccess) 
     _dataAccess = dataAccess 
    End Sub 

    Private _dataAccess As ILegacyDataAccess 

    Public Function GetTotalSales() As Integer _ 
      Implements ILegacyBusiness.GetTotalSales 
     Dim sales As List(Of SaleDto) = _dataAccess.GetAllSales() 
     ' Calculate total sales 
    End Function 
End Class 

를 이제, 당신이 볼 수 있듯이,이 클래스는 테스트 훨씬 쉽다. 모의 데이터 액세스 객체를 쉽게 만들 수있을뿐만 아니라 모의 데이터 액세스 객체를 비즈니스 객체에 쉽게 삽입 할 수 있습니다. 이제는 우리가 반환하고자하는 데이터를 빠르고 쉽게 반환 한 다음 비즈니스 클래스가 올바른 계산을 반환하는지 (데이터베이스가 관련되지 않음) 볼 수있는 모의 객체를 만들 수 있습니다.

불행히도 기존 클래스에 인터페이스를 추가하는 것은 쉽지만 종속성 주입을 사용하도록 리팩터링하는 데는 일반적으로 훨씬 많은 작업이 필요합니다. 어떤 수업이 가장 먼저 다루기에 가장 적합한지를 계획해야 할 것입니다. 코드를 사용하는 방식으로 작동하는 중개 구식 래퍼를 만들어야 할 수 있으므로 코드를 리팩터링하는 동안 기존 코드를 손상시키지 않아야합니다. 쉽고 빠르지는 않습니다. 그러나 장거리 노선을 위해 오래도록 인내심을 갖고 있다면, 그렇게 할 수 있으며, 기꺼이 할 수 있습니다.

+1

DI 알림을 보내 주셔서 감사합니다. +1. – w0051977

+1

그리고 포괄적 인 대답입니다. – w0051977

+1

감사. 내 데이터웨어 하우스/VB.NET 질문을 여기에서 볼 수 있다면 감사하겠습니다. http://stackoverflow.com/questions/18495622/data-warehouse-type-solution#18495622. 디자인 패턴 유형 질문에 대한 좋은 답변을 제공하므로 요청할 것입니다. – w0051977

1

난 당신이 interface 경로를 이동하지만 다음 해결책을 지불 할 경우 이들 중 하나 시도 추천 할 것입니다 : 테스트 인터페이스를 만들기

+0

유료 경로입니다. 구체적인 수업을 모의하기 위해 사용할 수있는 무료 도구를 알고 있습니까? 그래도 필요한 경우 회사에서 지불 할 것이라고 확신합니다. – w0051977

+0

필자는'Microsoft Moles'와'Microsoft Fakes'는 그렇게 할 것이라고 생각하지만 확실치 않습니다. 'Microsoft Fakes'는 Visual Studio의 '궁극적 인'버전을 가져야하기 때문에 실제로 무료가 아닙니다. 'Microsoft Moles'는 한 번 보일만한 가치가 있습니다. –

0

을 클래스는 나쁜 생각이 아닙니다. 단위 테스트의 목표는 클래스의 함수가 예상대로 작동 하는지를 연습하는 것입니다. 당신이 작업하고있는 클래스에 따라, 이것은 말하기가 더 쉬울 수도 있습니다 - 글로벌 상태에 많은 의존성이 있다면, 그에 따라 조롱해야 할 것입니다.

단위 테스트가 얼마나 소중한지 감안할 때 약간의 작업을 수행하면 (제한적으로) 귀사와 함께 일하는 개발자에게 도움이됩니다.

3

이것은 어려운 일입니다. 나는 네가 제대로 된 것 같아. 몇 가지 모 놀리 식 클래스에 대해 header interfaces을 만드는 것과 같은 추악한 코드로 끝나지 만, 이는 단지 중간 단계 여야합니다.

나는 Working Effectively with Legacy Code 사본에 투자 할 것을 제안합니다. 먼저 this distillation을 읽어보십시오.

칼의 옵션 (차단을 통해 조롱하게 함) 외에도 Microsoft Fakes & Stubs을 사용할 수도 있습니다. 그러나 이러한 도구는 코드를 솔리드 원칙을 준수하도록 리팩토링하거나 코드를 권장하지 않습니다.

+0

+1 링크입니다. – w0051977

5

프로젝트에 전혀 테스트가없는 경우 단위 테스트를 추가하기 전에 상위 레벨 테스트 (예 : 수락, 기능 및 통합 테스트)를 작성하는 것이 좋습니다.

이러한 테스트를 수행하면 시스템이 정상적으로 작동하는지, 특정 레벨의 '외부'품질 (프로그램의 입력 및 출력이 예상되는 것임을 의미 함)을 알 수 있습니다. .

일단 상위 테스트가 작동하면 이미 존재하는 클래스에 단위 테스트를 추가 할 수 있습니다.

나는 당신이 당신이 그들을 단위 테스트 할 수 있도록하려면 당신이 말할 것 안전망로 높은 수준의 테스트를 사용할 수 있도록 기존 클래스의 일부를 리팩토링 할 필요에서 자신을 발견 할 것이다 내기 네가 무엇인가를 망가 뜨린다면 너에게.

+0

마지막 단락의 경우 +1입니다. 나는 Test Driven Development를 사용하여 코드 기반을 리팩토링하고 있습니다. 통합 테스트를 위해 권장 할 수있는 테스트 도구가 있습니까? 기능 테스트 또는 수락 테스트? – w0051977

+0

몇 가지 클래스가 함께 작동해야하는 통합 테스트의 경우 xUnit 라이브러리 중 하나를 사용할 수 있습니다. 더 높은 수준의 테스트를 위해서는 프로그램의 인터페이스에 따라 달라집니다. Selenium Webdriver, Fitnesse 등과 같은 것들을 살펴보십시오. – jsanchez

+0

높은 수준의 테스트가 여기에 우선되어야한다고 강력히 동의합니다. 버그를 수정하고 시스템이 올바르게 작동한다고 주장 할 수 있다는 것은 일반적으로 코드 디자인을 개선하는 것보다 더 시급합니다. xUnit 프레임 워크를 고수하고 그 프레임 워크로 얻을 수있는 가장 높은 레벨을 테스트하는 것이 좋습니다. –

0

필자는 모든 것을 미리 테스트하지 않고 테스트 할 때 필요한 인터페이스와 클래스를 만드는 것을 선호합니다.

인터페이스 외에도 일부 기술을 사용하여 레거시 코드를 테스트 할 수 있습니다. 내가 자주 사용하는 방법은 "Extract And Override"입니다. 여기서 다른 방법의 "untestable"코드에서 추출한 코드를 덮어 쓸 수 있습니다. 이 클래스는 테스트하려는 클래스를 파생시키고 "감지 할 수없는"메소드를 일부 감지 코드로 대체합니다.

모의 프레임 워크를 사용하면 메서드에 재정의 할 수있는 키워드를 추가하는 것만 큼 쉬우 며 모의 프레임 워크를 사용하여 반환을 설정합니다.

많은 기술은 "Working Effectively with Legacy Code"책에서 찾을 수 있습니다.

기존 코드에 대한 한 가지 사실은 단위 테스트보다 통합 테스트를 작성하는 것이 더 나을 때가 있습니다. 테스트를 통해 문제가 발생하면 단위 테스트를 만듭니다.

또 다른 팁은 더 적은 의존성을 가진 모듈/클래스로 시작하는 것입니다. 그렇게하면 고통이 적은 코드에 익숙해집니다.

관련 문제