Mocking은 생산 코드에서 처리를 소개합니다


15

IReader 인터페이스, IReader 인터페이스 ReaderImplementation의 구현 및 리더에서 데이터를 소비하고 처리하는 클래스 ReaderConsumer를 가정합니다.

public interface IReader
{
     object Read()
}

이행

public class ReaderImplementation
{
    ...
    public object Read()
    {
        ...
    }
}

소비자:

public class ReaderConsumer()
{
    public string location

    // constructor
    public ReaderConsumer()
    {
        ...
    }

    // read some data
    public object ReadData()
    {
        IReader reader = new ReaderImplementation(this.location)
        data = reader.Read()
        ...
        return processedData    
    }
}

ReaderConsumer 및 처리를 테스트하기 위해 IReader를 사용합니다. 따라서 ReaderConsumer는 다음과 같이됩니다.

public class ReaderConsumer()
{
    private IReader reader = null

    public string location

    // constructor
    public ReaderConsumer()
    {
        ...
    }

    // mock constructor
    public ReaderConsumer(IReader reader)
    {
        this.reader = reader
    }

    // read some data
    public object ReadData()
    {
        try
        {
            if(this.reader == null)
            {
                 this.reader = new ReaderImplementation(this.location)
            }

            data = reader.Read()
            ...
            return processedData    
        }
        finally
        {
            this.reader = null
        }
    }
}

이 솔루션에서는 조롱 생성자 만 인터페이스 인스턴스를 제공하므로 조롱은 프로덕션 코드에 대한 if 문장을 소개합니다.

작성하는 동안 try-finally 블록은 응용 프로그램 런타임 동안 위치를 변경하는 사용자를 처리하기 때문에 다소 관련이 없음을 알고 있습니다.

전반적으로 냄새가납니다. 어떻게 더 잘 처리 할 수 ​​있습니까?


16
일반적으로 종속성이 주입 된 생성자가 유일한 생성자이므로 문제가되지 않습니다. ReaderConsumer독립적으로 만드는 것이 문제가 ReaderImplementation아닌가?
Chris Wohlert

현재 종속성을 제거하기가 어렵습니다. 조금 더 살펴보면 ReaderImplemenatation에 대한 종속성보다 더 깊은 문제가 있습니다. ReaderConsumer는 공장에서 시작하는 동안 만들어 지므로 응용 프로그램 수명 동안 지속되며 사용자의 추가 변경이 필요합니다. 구성 / 사용자 입력이 개체로 존재할 수 있으며 대신 ReaderConsumer 및 ReaderImplementation을 만들 수 있습니다. 주어진 답변 모두보다 일반적인 경우를 꽤 잘 해결합니다.
kristian mo

3
. 이것은 TDD 의 요점 입니다. 테스트를 먼저 작성해야 한다는 것은 더 분리 된 디자인을 의미 합니다 (그렇지 않으면 단위 테스트 를 작성할 수 없습니다 ...). 이를 통해 코드를 유지 관리하고 확장 할 수 있습니다.
Bakuriu

Dependency Injection으로 해결할 수있는 냄새를 감지하는 좋은 방법은 키워드 'new'를 찾는 것입니다. 의존성을 새롭게하지 마십시오. 대신 주입하십시오.
Eternal21

답변:


67

메소드에서 리더를 초기화하는 대신이 행을 이동하십시오.

{
    this.reader = new ReaderImplementation(this.location)
}

기본 매개 변수가없는 생성자로.

public ReaderConsumer()
{
    this.reader = new ReaderImplementation(this.location)
}

public ReaderConsumer(IReader reader)
{
    this.reader = reader
}

"모의 생성자"와 같은 것은 없습니다. 만약 당신의 클래스가 작동하기 위해 필요한 의존성을 가지고 있다면 , 생성자가 그 것을 제공하거나 생성해야합니다.


3
DI ++ 관점을 제외하고 기본 생성자는 코드 냄새입니다.
Mathieu Guindon

3
@ Mat'sMug 기본 구현에 아무런 문제가 없습니다. ctor chaining의 부족은 여기서 냄새입니다. =;)-–
RubberDuck

아, 그렇습니다. 만약 당신이 책을 가지고 있지 않다면, 이 기사개자식 주사 방지 패턴을 아주 잘 묘사하고있는 것 같습니다 .
Mathieu Guindon

3
@ Mat'sMug : 당신은 DI를 독단적으로 해석하고 있습니다. 기본 생성자를 주입 할 필요가 없다면 필요하지 않습니다.
Robert Harvey

3
@ Mat'sMug 링크 된 책은 또한 의존성에 대한 "허용 가능한 기본값"에 대해 이야기합니다 (속성 주입을 언급하고 있음에도 불구하고). 이런 식으로하자 : 2 인식 접근 방식 은 1 인식 접근 방식보다 단순성과 유지 보수성 측면에서 더 많은 비용이 들며, 명확성을 위해 과도하게 단순화 된 질문으로 인해 비용이 정당화되지는 않지만 경우에 따라 달라질 수 있습니다. .
Carl Leth

54

단일 생성자 만 필요합니다.

public class ReaderConsumer()
{
    private IReader reader = null

    public string location

    // constructor
    public ReaderConsumer(IReader reader)
    {
        this.reader = reader;
    }

생산 코드에서 :

var rc = new ReaderConsumer(new ReaderImplementation(0));

당신의 시험에서 :

var rc = new ReaderConsumer(new MockImplementation(0));

13

의존성 주입 및 제어 역전 조사

Ewan과 RubberDuck은 모두 훌륭한 답변을 제공합니다. 그러나 DI (Dependency Injection)와 IoC (Inversion of Control)라는 또 다른 영역을 언급하고 싶습니다. 이 두 가지 접근 방식은 겪고있는 문제를 프레임 워크 / 라이브러리로 옮기므로 걱정할 필요가 없습니다.

당신의 예제는 간단하고 빠르다. 그러나 필연적으로 당신은 그것을 만들 것이고 다음과 같은 수많은 생성자 또는 초기화 루틴으로 끝날 것이다.

var foo = new Foo (new Bar (new Baz (), new Quz ()), new Foo2 ());

DI / IoC에서는 인터페이스를 구현에 일치시키기위한 규칙을 설명 할 수있는 라이브러리를 사용하고 "Give me a Foo"라고 말하면 모든 것을 연결하는 방법을 알아냅니다.

매우 친숙한 IoC 컨테이너 (많은)가 있으며, 그 중에서도 훌륭한 선택이 많으므로 살펴볼 것을 권장합니다.

시작하는 간단한 방법은 다음과 같습니다.

http://www.ninject.org/

탐색 할 목록은 다음과 같습니다.

http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx


7
Ewan과 RubberDuck의 답변 은 모두 DI를 보여줍니다 . DI / IoC는 툴이나 프레임 워크에 관한 것이 아니라, 컨트롤이 반전되고 의존성이 주입되는 방식으로 코드를 설계하고 구성하는 것입니다. Mark Seemann의 책 은 IoC 프레임 워크없이 DI / IoC를 완전히 달성 할 수있는 방법 과 IoC 프레임 워크가 좋은 아이디어가되는 이유시기 (힌트 : 종속성의 종속성에 대한 종속성이있는 경우 )를 설명하는 데있어 훌륭한 작업을 수행합니다 . .) =)
Mathieu Guindon

또한 ... Ninject가 대단하기 때문에 +1하고 다시 읽으면 답이 괜찮아 보입니다. 첫 번째 단락은 Ewan 's와 RubberDuck의 답변은 DI에 관한 것이 아니라 첫 번째 의견을 자극 한 것과 같습니다.
Mathieu Guindon

1
@ Mat'sMug 확실히 요점을 찾으십시오. 나는 OP가 DI (Iwan 's는 Poor Man 's DI)를 사용하여 다른 포스터가 실제로 사용했던 DI / IoC를 조사하도록 권장했지만 언급하지 않았습니다. 물론 프레임 워크를 사용하는 이점은 DI의 세계에서 사용자를 몰입시켜 그들이 무엇을하고 있는지 이해하도록 안내 할 많은 구체적인 예제를 제공한다는 것입니다. :-) 물론 Mark Seemann의 책을 좋아했습니다. 내가 가장 좋아하는 것 중 하나입니다.
레지날드 블루

DI 프레임 워크에 대한 팁을 주셔서 감사합니다.이 혼란을 제대로 리팩토링하는 방법 인 것 같습니다 :)
kristian mo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.