2013-06-28 1 views
1

나는이 팩토리 클래스를 가지고 있으며 올바르게 테스트하려고합니다. 나는 많은 자식 (상속)을 가진 추상 클래스를 가지고 있다고 가정 해 보겠습니다.모의 정적 메서드 Activator.CreateInstance 다른 클래스의 모의를 반환합니다

제 Factory 클래스에서 BuildChild 메소드에서 볼 수 있듯이 런타임에 하위 클래스의 인스턴스를 만들 수 있기를 원합니다. 런타임 전에 유형을 알 수 없기 때문에 런타임 중에이 인스턴스를 작성할 수 있어야합니다. 그리고이 프로젝트에 Unity를 사용할 수는 없습니다 (그렇다면이 방법을 묻지 않습니다).

public class Factory 
{ 
    public AnAbstractClass BuildChild(Type childType, object parameter) 
    { 
     AnAbstractClass child = (AnAbstractClass) Activator.CreateInstance(childType); 
     child.Initialize(parameter); 
     return child; 
    } 
} 

가 나는 자식 클래스 내 자신의 조롱 개체를 반환하는 모의 Activator.CreateInstance로 할 수있는 방법을 찾으려면,이 테스트하려면 :

는 여기에 테스트 할 내 공장 클래스입니다. 이것을 어떻게 할 수 있습니까? 또는 Activator.CreateInstance (및 Unity)를 사용하지 않고이 작업을 수행 할 수있는 더 좋은 방법이 있다면 테스트하고 조롱하기가 더 쉽습니다.

현재 내 모의 객체를 만드는 데 Moq을 사용하고 있지만 Activator.CreateInstance는 정적 클래스의 정적 메소드이므로이 작업을 수행하는 방법을 알 수 없습니다. Moq가 모의 인스턴스를 만들 수 있다는 것을 이미 알고 있습니다. 사물).

나는 마이크로 소프트에서 Fakes를 보았지만 성공하지는 못했습니다. (어떻게 작동하는지 이해하고 잘 설명 된 예제를 찾는 데 어려움이있었습니다.)

도와주세요!

편집 :

나는 또 다른 조롱 개체를 반환이 방법을 강제로 원하기 때문에 Activator.CreateInstance로 조롱 할 필요가

. 내가 원하는 올바른 방법은이 방법을 스터핑하는 것뿐입니다 (조롱하지 않기).

그래서 나는이 같은 위해 buildChild 테스트 할 때 : 유형 및 매개 변수를 사용하여 호출

[TestMethod] 
public void TestBuildChild() 
{ 
    var mockChildClass = new Mock(AChildClass); 
    // TODO: Stub/Mock Activator.CreateInstance to return mockChildClass when called with "type" and "parameter" as follow. 
    var type = typeof(AChildClass); 
    var parameter = "A parameter"; 

    var child = this._factory.BuildChild(type, parameters); 
} 

Activator.CreateInstance로 대신 실제 아이 클래스의 새 인스턴스를 만드는 내 조롱 객체를 반환합니다 (아직 구현되지 않음).

답변

4

글쎄, 내가 테스트 해보는 것이 신뢰할 수 있어야하므로 조롱해야 할 사항이 아니라고 말하고 싶다.하지만 유형이 외부 소스 라이브러리에 있다면 문제가있을 수있다. 즉, 당신이 이것을 달성 할 수있는 유일한 방법은 Activator를 래핑하여 정적 클래스가 아니어야한다는 것입니다. 이 같은

뭔가 :

public ActivatorWrapper 
{ 
    public virtual object CreateInstance(Type type) 
    { 
     return Activator.CreateInstance(type); 
    } 
} 
+0

신뢰할 수 있어야한다고 말하면 조롱 할 수 있습니다. 따라서 조롱 할 필요가 없습니다. 나는 왜 내가이 수업을 조롱 할 필요가 있는지 설명하기 위해 나의 글을 편집한다. – Jeep87c

+0

@ Jeep87c 그래서 나는 이것에 대한 해결책을 제시했다. 당신이 더 강력한 프레임 워크에 들어가기를 원하지 않는다면 ...하지만 그것들은 나쁜 OO 행동을 조장합니다. –

+0

오, 미안합니다. 내가 편집하는 동안 당신은 아마 당신의 글을 편집했을 것입니다. 이 해결 방법에 대해 신경 쓰지 말고 감사드립니다. 당신이 말한 이유 때문에 더 강력한 프레임 워크를 사용하고 싶지 않습니다. – Jeep87c

1

당신은 유형에서 인스턴스를 생성하는 방법을 노출하는 인터페이스를 도입해야합니다. 클래스가 생성자에서 인터페이스의 구현을 가져 오게합니다.

그러면 조롱 할 수 있습니다.

그러면 Production에서 사용하는 Activator.CreateInstance에 위임 한 구현이 생깁니다.

그렇다면 왜 이것을 조롱해야합니까? 테스트에서 메서드 호출에 지정된 형식을 다시 가져 오는 지 확인하지 못하는 이유는 무엇입니까?

다음 편집을 수행하면 공장 호출 대신 BuildChild 팩토리 호출을 모의 할 수 없습니다.그것은 당신이 조롱하고 싶은 의존성처럼 보입니다.

당신은 팩토리가 올바른 유형을 반환하는지 테스트하고 싶습니다. 아무 것도 모방 할 필요가 없거나 공장 용 인터페이스를 소개하고 모의하고 싶습니다.

나는 Activator.CreateInstance를 모의하고 싶다는 것이 무언가가 당신의 디자인에 맞지 않다는 것을 알려주는 코드라고 생각한다.

당신이 AnAbstractClass의 구현을하지 않고 나 같이 아무것도 조롱 할 필요없이 공장 구현을 테스트 할 수 생각 :

public class TestAnAbstractClass : AnAbstractClass 
{ 
    public object ConstructorParameter; 

    public TestAnAbstractClass(object constructorParameter) 
    { 
      this.constructorParameter = constructorParameter; 
    } 
} 

다음이 당신의 공장 전화 :

테스트 구현을 만들 테스트 : 그럼

[TestMethod] 
public void TestBuildChild() 
{ 
    var type = typeof(TestAnAbstractClass); 
    var parameter = "A parameter"; 

    var child =(TestAnAbstractClass) this._factory.BuildChild(type, parameters); 
    Assert.That(child.ConstructorParameter, Is.EqualTo(parameter)); 
} 

당신은 공장 실제 기능과도 구현 변경하는 경우를 테스트 테스트가 필요하지 않으며 모든 코드를 테스트합니다.

+0

테스트 자체는 Factory 클래스와 메소드를 테스트하기위한 목적이기 때문입니다. 공장 호출을 조롱하면 쓸데없는 테스트가 끝납니다. 이 클래스를 사용하여 다른 클래스를 테스트 할 때 이미이 팩토리를 모의합니다. – Jeep87c

+0

그리고 또한 "왜 당신의 테스트가 메서드 호출에 지정된 타입을 되찾았는지 검사 할 수없는 이유는 무엇입니까?"하위 클래스가 아직 구현되지 않았기 때문에 정의에 따라 클래스의 단위 테스트는 다른 클래스에 의존해서는 안됩니다. – Jeep87c

+1

Activator.CreateInstance를 래핑하는 클래스를 도입하면 어떻게 될까요? 그 구현을 테스트하려면? 초기화 된 메서드는 초기화 된 메서드를 사용하여 반환 된 개체를 테스트 할 수있을 것이라고 가정합니다. 그러나 initialize 메서드는 냄새가 있어야하며 피해야합니다. 매개 변수는 아마도 추상 클래스에 선언 된 생성자를 통해 전달되어야합니다. 그 매개 변수가 선택 사항이 아닌 것 같습니다.) 대신이 메소드를 '비어있는'것으로 만들 것입니다. Activator.CreateInstance (매개 변수)에 대한 호출을 단순히 감쌀 것이므로 –

0

아래 예제와 같이 주변 환경을 사용해 볼 수 있습니다.

public static class SystemActivator 
    { 
     private static Dictionary<Type, object> _mockObjects; 

     private static Dictionary<Type, object> MockObjects 
     { 
      get { return _mockObjects; } 
      set 
      { 
       if (value.Any(keyValuePair => keyValuePair.Value.GetType() != keyValuePair.Key)) 
       { 
        throw new InvalidCastException("object is not of the correct type"); 
       } 
       _mockObjects = value; 
      } 
     } 

     [Conditional("DEBUG")] 
     public static void SetMockObjects(Dictionary<Type, object> mockObjects) 
     { 
      MockObjects = mockObjects; 
     } 

     public static void Reset() 
     { 
      MockObjects = null; 
     } 

     public static object CreateInstance(Type type) 
     { 
      if (MockObjects != null) 
      { 
       return MockObjects.ContainsKey(type) ? MockObjects[type] : Activator.CreateInstance(type); 
      } 
      return Activator.CreateInstance(type); 
     } 
    } 

이 방법은 사용할 수있는 방법에 대한 아이디어를 제공하는 매우 기본적인 수업입니다. 테스트 할 때 MockObjects 사전을 설정하여 유형을 반환하려는 인스턴스 (즉, 모의 객체)와 페어링 할 수 있습니다. 그런 다음 클래스에서 Activator를 사용하는 대신 SystemActivator를 사용합니다.

이 클래스의 다른 장점은 단위 테스트가 어느 정도까지 가능하다는 것입니다. 설정되면 모의 (mock)을 반환하고 그렇지 않은 경우 activator 인스턴스를 반환하는지 테스트합니다.

Activator는 요청한 유형과 다른 유형의 인스턴스를 반환하지 않으므로 SystemActivator가이 동작을 모방해야합니다. 그 이유는 예외를 포함 시켰기 때문입니다. (예를 들어 액티베이터가이 예제에 표시된 CreateInstance 과부하를 사용하여 문자열을 만들 수 없지만 문자열 값을 반환하도록 MockObject를 설정하면 발생하지 않아야합니다.) 허용되지 않아야합니다.

이 접근 방식의 위험성 때문에 생산 코드에서 MockObject를 설정해서는 안됩니다. 이 위험을 줄이기 위해이 메소드에 [Conditional ("DEBUG")] 속성을 지정했습니다. 이것은 개발자가 프로덕션 코드에서 MockObject를 설정하는 것을 멈추지는 않지만 릴리스 빌드가 손상 될 수 있습니다.

또한 SystemActivator를 사용하는 테스트 클래스에서는 모의 객체를 재설정하는 티어 다운 메소드를 포함해야합니다. 그렇지 않으면 테스트 종속성을 만들 위험이 있습니다.

관련 문제