단위 테스트를 가능하게하기 위해 처음부터 코드를 설계해야합니까? 그것이 인터페이스가 될 구체적인 유형 이외의

현재 팀에서 단위 테스트를 허용하도록 코드 디자인을 수정하는 것이 코드 냄새인지, 또는 코드 냄새가 나지 않고 어느 정도까지 할 수 있는지에 대한 논쟁이 있습니다. 우리는 단지 다른 모든 소프트웨어 개발 회사에 존재하는 관행을 제자리에두기 시작했기 때문에 발생했습니다.

특히, 우리는 매우 얇은 웹 API 서비스를 갖게 될 것입니다. 주요 책임은 웹 요청 / 응답을 마샬링하고 비즈니스 로직을 포함하는 기본 API를 호출하는 것입니다.

한 가지 예는 인증 방법 유형을 반환하는 팩토리를 만들 계획입니다. 우리는 그것이 인터페이스가 될 구체적인 유형 이외의 다른 것을 기대하지 않기 때문에 인터페이스를 상속받을 필요가 없습니다. 그러나 웹 API 서비스를 단위 테스트하려면이 팩토리를 조롱해야합니다.

이것은 본질적으로 우리가 (생성자 또는 설정자를 통해) DI를 받아들이도록 Web API 컨트롤러 클래스를 설계한다는 것을 의미합니다. 즉, DI를 허용하고 우리가 필요로하지 않는 인터페이스를 구현하기 위해 컨트롤러의 일부를 설계하고 있음을 의미합니다. 이런 식으로 컨트롤러를 디자인 할 필요가 없도록 Ninject와 같은 타사 프레임 워크를 사용하지만 여전히 인터페이스를 만들어야합니다.

팀의 일부는 테스트를 위해 코드를 디자인하는 것을 꺼려합니다. 단위 테스트를 원한다면 약간의 타협이 필요한 것 같습니다. 그러나 그들의 우려가 어떻게 완화되는지 확실하지 않습니다.

분명히 이것은 아주 새로운 프로젝트이므로 단위 테스트를 가능하게하기 위해 코드를 수정하는 것은 아닙니다. 단위 테스트가 가능하도록 작성할 코드를 설계하는 것입니다.



답변

테스트를 위해 코드를 수정하지 않으려는 개발자는 개발자가 테스트의 역할을 이해하지 못했으며 조직에서 자신의 역할을 암시합니다.

소프트웨어 비즈니스는 비즈니스 가치를 창출하는 코드 기반을 제공하는 데 중점을 둡니다. 오랜 경험을 통해 우리는 테스트 없이는 사소한 크기의 코드 기반을 만들 수 없다는 것을 알게되었습니다. 따라서 테스트 스위트는 비즈니스에서 없어서는 안될 부분입니다.

많은 코더들은이 원칙에 따라 입술 서비스를 지불하지만 무의식적으로는 그것을 받아들이지 않습니다. 이것이 왜 그런지 이해하는 것은 쉽습니다. 우리 자신의 정신 능력이 무한하지 않다는 사실에 대한 인식은 사실 현대 코드 기반의 엄청난 복잡성에 직면했을 때 놀랍게도 제한되어 있으며, 환영받지 않고 쉽게 억압되거나 합리화됩니다. 테스트 코드가 고객에게 제공되지 않는다는 사실은 “필수”비즈니스 코드와 비교할 때이 코드가 2 등 시민이며 필수적이지 않다는 것을 쉽게 믿게합니다. 그리고 비즈니스 코드에 테스트 코드 추가한다는 아이디어는 많은 사람들에게 두려운 것 같습니다.

이 관행을 정당화하는 데 어려움은 소프트웨어 비즈니스에서 가치가 창출되는 방식에 대한 전체 그림이 종종 회사 계층 구조의 상위 계층에 의해서만 이해된다는 사실과 관련이 있지만,이 사람들은 테스트를 제거 할 수없는 이유 를 이해하는 데 필요한 코딩 워크 플로우 따라서 시험은 일반적으로 좋은 생각 일 수 있다고 확신하는 실무자에 의해 너무 자주 진정 되지만 “우리는 그런 목발을 필요로하지 않는 엘리트 프로그래머입니다.”또는 “지금 우리는 그럴 시간이 없습니다”등. 비즈니스 성공은 숫자 게임이며 기술 부채를 피하고 보장한다는 사실 품질 등은 장기적으로 만 그 가치를 보여줍니다.

짧은 이야기 : 코드를 테스트 할 수있게 만드는 것은 개발 프로세스의 필수 부분으로 다른 분야와 다르지 않습니다 (많은 마이크로 칩은 테스트 목적으로 상당한 비율의 요소로 설계되었습니다 ). 그러나 그 이유를 간과하기 쉽습니다. 그. 함정에 빠지지 마십시오.


답변

생각만큼 간단하지 않습니다. 그것을 분해하자.

  • 단위 테스트 작성은 확실히 좋은 일입니다.

그러나!

  • 코드를 변경하면 버그가 발생할 수 있습니다. 따라서 좋은 비즈니스 이유없이 코드를 변경하는 것은 좋은 생각이 아닙니다.

  • 귀하의 ‘매우 얇은’webapi는 단위 테스트에서 가장 큰 경우는 아닙니다.

  • 코드와 테스트를 동시에 변경하는 것은 나쁜 일입니다.

다음과 같은 접근법을 제안합니다.

  1. 통합 테스트를 작성하십시오 . 코드를 변경하지 않아도됩니다. 기본 테스트 사례를 제공하고 추가 코드 변경으로 인해 버그가 발생하지 않는지 확인할 수 있습니다.

  2. 새 코드 를 테스트 할 수 있고 단위 및 통합 테스트가 있는지 확인하십시오 .

  3. 빌드 및 배포 후에 CI 체인이 테스트를 실행하는지 확인하십시오.

그러한 것들이 설정되면 테스트 가능성을 위해 레거시 프로젝트를 리팩토링하는 것에 대해서만 생각하십시오.

모든 사람이 프로세스에서 교훈을 배우고 테스트가 가장 필요한 위치, 테스트 구성 방법 및 비즈니스에 미치는 가치에 대해 잘 알고 있기를 바랍니다.

편집 :이 답변을 작성한 이후 OP는 기존 코드를 수정하지 않고 새로운 코드에 대해 이야기하고 있음을 보여주기 위해 질문을 명확하게했습니다. “순수하게 테스트하는 것이 좋습니까?” 논쟁은 몇 년 전에 해결되었습니다.

단위 테스트에 어떤 코드 변경이 필요할지 상상하기 어렵지만 어떤 경우에도 원하는 일반적인 모범 사례는 아닙니다. 실제 이의 제기를 검토하는 것이 현명 할 것입니다. 아마도 이의 제기가되는 단위 테스트 스타일 일 것입니다.


답변

본질적으로 테스트 가능하도록 코드를 설계하는 것은 코드 냄새가 아닙니다. 오히려 좋은 디자인의 징조입니다. 이를 기반으로 잘 알려진 널리 사용되는 디자인 패턴 (예 : Model-View-Presenter)이 몇 가지있어 쉬운 테스트를 쉽게 제공 할 수 있습니다.

따라서보다 쉽게 ​​테스트하기 위해 구체적인 클래스에 대한 인터페이스를 작성해야하는 경우 좋은 방법입니다. 이미 구체적인 클래스를 가지고 있다면 대부분의 IDE는 인터페이스를 추출하여 필요한 노력을 최소화 할 수 있습니다. 둘을 동기화하는 것이 조금 더 많은 작업이지만 인터페이스는 크게 바뀌지 않아야하며 테스트의 이점이 추가 노력보다 중요 할 수 있습니다.

반면에 @MatthieuM으로. 주석에서 언급했듯이 테스트를 위해 프로덕션에서 절대 사용해서는 안되는 특정 진입 점을 코드에 추가하는 경우 문제가 될 수 있습니다.


답변

단위 테스트를 만들려면 테스트 할 코드에 적어도 특정 속성이 있어야한다는 것을 이해하는 것은 매우 간단합니다. 예를 들어, 코드가 개별적 으로 테스트 할 수있는 개별 단위 로 구성되지 않은 경우 “단위 테스트”라는 단어는 의미가 없습니다. 코드에 이러한 속성이 없으면 먼저 변경해야합니다.

이론적으로는 모든 SOLID 원리를 적용하여 테스트 가능한 코드 단위를 먼저 작성한 다음 나중에 원래 코드를 수정하지 않고 테스트를 시도 할 수 있다고 말했다. 불행히도 실제로 단위 테스트가 가능한 코드를 작성하는 것이 항상 간단하지는 않으므로 테스트를 만들 때 감지해야 할 변경 사항이있을 가능성이 큽니다. 이것은 단위 테스트라는 개념을 염두에두고 작성된 코드에서도 마찬가지이며, “단위 테스트 성”이 처음에 안건이 아닌 곳에 작성된 코드의 경우에는 더욱 그렇습니다.

단위 테스트를 먼저 작성하여 문제를 해결하려고하는 잘 알려진 접근 방식이 있습니다.이를 TDD (Test Driven Development)라고하며 처음부터 코드 단위를 더 테스트 할 수 있도록 도와줍니다.

물론, 나중에 코드를 테스트 가능하게 만들기 위해 코드를 변경하는 것을 꺼리는 것은 코드를 수동으로 먼저 테스트하거나 문제를 잘 처리하는 상황에서 자주 발생하므로 변경하면 실제로 새로운 버그가 발생할 수 있습니다. 이를 완화하는 가장 좋은 방법은 회귀 테스트 슈트를 먼저 작성하는 것입니다 (코드베이스에 대한 최소한의 변경만으로도 구현할 수 있음)뿐만 아니라 코드 검토 또는 새로운 수동 테스트 세션과 같은 기타 수반되는 조치도 있습니다. 일부 내부를 재 설계해도 중요한 것이 깨지지 않도록 충분한 확신을 주어야합니다.


답변

나는 당신이 만드는 (확실하지 않은) 주장과 관련하여 문제를 겪습니다.

웹 API 서비스를 단위 테스트하려면이 팩토리를 조롱해야합니다.

반드시 그런 것은 아닙니다. 테스트를 작성 하는 방법 에는 여러 가지 가 있으며 모의를 포함하지 않는 단위 테스트를 작성 하는 방법 이 있습니다 . 더 중요한 것은 기능 테스트 또는 통합 테스트와 같은 다른 종류 의 테스트가 있습니다. 많은 경우 OOP 프로그래밍 언어가 아닌 “인터페이스”에서 “테스트 이음새”를 찾을 수 있습니다 interface.

보다 자연스러운 대체 테스트 솔기를 찾는 데 도움이되는 몇 가지 질문 :

  • 다른 API를 통해 씬 웹 API를 작성하고 싶 습니까?
  • 웹 API와 기본 API 간의 코드 중복을 줄일 수 있습니까? 하나는 다른 것의 관점에서 생성 될 수 있습니까?
  • 전체 웹 API와 기본 API를 단일 “블랙 박스”단위로 취급하고 전체 동작 방식에 대한 의미있는 주장을 할 수 있습니까?
  • 향후 웹 API를 새로운 구현으로 교체해야한다면 어떻게해야할까요?
  • 향후 웹 API가 새로운 구현으로 대체 된 경우 웹 API의 클라이언트가 알 수 있습니까? 그렇다면 어떻게?

입증되지 않은 또 다른 주장은 DI에 관한 것입니다.

DI를 생성하도록 Web API 컨트롤러 클래스를 설계하거나 (생성자 또는 설정자를 통해), 이는 DI를 허용하고 필요하지 않은 인터페이스를 구현하기 위해 컨트롤러의 일부를 설계하거나 타사를 사용함을 의미합니다. Ninject와 같은 프레임 워크는 컨트롤러를 이런 식으로 디자인하지 않아도되지만 여전히 인터페이스를 만들어야합니다.

의존성 주입이 반드시 새로운 것을 만드는 것은 아닙니다 interface. 예를 들어 인증 토큰의 원인 : 프로그래밍 방식 으로 실제 인증 토큰을 만들 수 있습니까? 그런 다음 테스트에서 이러한 토큰을 만들어 주입 할 수 있습니다. 토큰을 검증하는 프로세스는 어떤 종류의 암호화 비밀에 의존합니까? 나는 당신이 비밀을 하드 코딩하지 않았기를 바랍니다. 어떻게 든 스토리지에서 읽을 수 있기를 기대하며,이 경우 테스트 케이스에서 다른 (잘 알려진) 비밀을 사용할 수 있습니다.

이것은 결코 새로운 것을 만들지 말아야한다는 것은 아닙니다 interface. 그러나 시험을 작성하는 한 가지 방법이나 행동을 위조하는 한 가지 방법만으로는 고쳐지지 마십시오. 상자 밖에서 생각하면 일반적으로 최소한의 코드 왜곡이 필요한 솔루션을 찾을 수 있지만 원하는 효과를 얻을 수 있습니다.


답변

이것이 새로운 프로젝트이므로 운이 좋았습니다. Test Driven Design은 좋은 코드를 작성하는 데 매우 효과적이라는 것을 알았습니다 (그래서 우리는 처음에 그것을 수행합니다).

파악함으로써 정면 실제 입력 데이터와 주어진 코드를 호출 한 후 의도 한대로 당신이, 당신이 초기 과정에서 API 설계를 할 수있다 확인할 수 있습니다 실제 출력 데이터를 가져오고을 얻기의 좋은 기회를 가지고하는 방법 수용하기 위해 다시 작성해야하는 기존 코드에 방해받지 않기 때문에 유용한 디자인. 또한 동료가 이해하기가 더 쉬워 프로세스 초기에 다시 좋은 토론을 할 수 있습니다.

위 문장에서 “유용하다”는 결과 메소드가 호출하기 쉬울뿐만 아니라 통합 테스트에서 조작하기 쉽고 깔끔한 인터페이스를 만들고 모형을 작성하는 경향이 있음을 의미합니다.

생각해 봐. 특히 동료 검토의 경우. 내 경험상 시간과 노력의 투자는 매우 빨리 반환 될 것입니다.


답변

코드를 수정해야하는 경우 코드 냄새가납니다.

개인적인 경험으로 볼 때 내 코드가 테스트를 작성하기 어려운 경우 나쁜 코드입니다. 설계된대로 실행되거나 작동하지 않기 때문에 나쁜 코드가 아닙니다. 왜 작동하는지 빨리 이해할 수 없기 때문에 나쁩니다. 버그가 발생하면 문제를 해결하는 데 오랜 시간이 걸릴 것입니다. 코드는 재사용이 어렵거나 불가능합니다.

좋은 (깨끗한) 코드는 작업을 한눈에 알기 쉽게 (또는 최소한보기 좋게) 더 작은 섹션으로 나눕니다. 이러한 작은 섹션을 테스트하는 것은 쉽습니다. 하위 섹션에 대해 확신이있는 경우 비슷한 코드베이스 청크 만 테스트하는 테스트를 작성할 수도 있습니다 (재 테스트는 이미 테스트 했으므로 여기에서도 유용합니다).

코드를 쉽게 테스트하고, 리팩토링하고, 처음부터 재사용하기 쉽도록 유지하십시오. 변경해야 할 때마다 스스로를 죽이지 않을 것입니다.

더 깔끔한 코드로 버려 질 프로토 타입이었던 프로젝트를 완전히 재건하면서 이것을 입력하고 있습니다. 화면에서 몇 시간 동안 화면을 응시하는 대신 가능한 빨리 시작하고 나쁜 코드를 리팩터링하는 것이 부분적으로 작동하는 무언가를 깨뜨리는 것을 두려워하는 것을 두려워하는 것이 훨씬 좋습니다.