유닛 테스트를 위해 개인 방법을 공개하는 것... 좋은 생각?
진행자 주의:여기에는 이미 39개의 답변이 게시되어 있습니다(일부는 삭제되어 있습니다).답변을 게시하기 전에 토론에 의미 있는 내용을 추가할 수 있는지 검토하십시오.당신은 다른 사람이 이미 말한 것을 그냥 반복하고 있는 것 이상입니다.
나는 가끔 단지 그것을 위한 몇 가지 단위 테스트를 작성하기 위해 학급에서 사적인 방법을 만들어야 한다는 것을 알게 된다.
보통 이는 메서드가 클래스 내의 다른 메서드 간에 공유되는 로직을 포함하고 있어 그 로직을 스스로 테스트하는 것이 더 깔끔하기 때문이거나 스레드 문제를 걱정하지 않고 동기 스레드에서 사용되는 로직을 테스트하고 싶기 때문일 수 있습니다.
다른 사람들이 내가 싫어해서 이러는 걸까?저는 개인적으로 보너스가 교실 밖에서는 실제로 어떤 서비스도 제공하지 않는 방법을 공개하는 것의 문제점보다 더 크다고 생각합니다.
갱신하다
모두 답변 감사합니다. 사람들의 관심을 끌었던 것 같습니다.클래스가 사용될 수 있는 유일한 방법이기 때문에 퍼블릭 API를 통해 테스트를 진행해야 한다는 것이 일반적인 의견이며, 저는 이에 동의합니다.위에서 언급한 몇 가지 사례는 드문 경우로, 저는 이 방법을 통해 얻을 수 있는 이점이 충분히 있다고 생각했습니다.
하지만, 모든 사람이 그런 일이 일어나서는 안 된다고 지적하고 있는 것을 알 수긍할 수 있다.그리고 좀 더 생각해 보면 테스트에 대응하기 위해 코드를 변경하는 것은 좋지 않은 생각이라고 생각합니다.테스트는 어떤 면에서 지원 도구이며, 시스템을 '지원 도구'로 변경하는 것은 명백한 잘못된 관행이라고 생각합니다.
이 답변은 원래 "유닛 테스트만으로 개인 인스턴스 변수를 getter를 통해 노출할 수 있는 타당한 이유가 있습니까?"라는 질문에서 게시되었습니다. 여기에 통합되었기 때문에 여기에 제시된 사용 사례에 따라 약간 다를 수 있습니다.
일반적으로 저는 테스트를 쉽게 하기 위해 "제작" 코드를 리팩터링하는 것에 찬성합니다.하지만, 저는 그것이 여기서 좋은 결정이 아니라고 생각합니다.양호한 유닛 테스트(일반적으로)는 클래스의 구현 세부사항에는 신경 쓰지 않고 눈에 보이는 동작에만 신경을 써야 합니다., 가 「」를 호출한 후, 할 수 .first() ★★★★★★★★★★★★★★★★★」last()
예를 들어, 다음의 의사 코드를 생각해 보겠습니다.
public class NavigationTest {
private Navigation nav;
@Before
public void setUp() {
// Set up nav so the order is page1->page2->page3 and
// we've moved back to page2
nav = ...;
}
@Test
public void testFirst() {
nav.first();
assertEquals("page1", nav.getPage());
nav.next();
assertEquals("page2", nav.getPage());
nav.next();
assertEquals("page3", nav.getPage());
}
@Test
public void testLast() {
nav.last();
assertEquals("page3", nav.getPage());
nav.previous();
assertEquals("page2", nav.getPage());
nav.previous();
assertEquals("page1", nav.getPage());
}
}
개인적으로는 퍼블릭 API를 사용하여 유닛 테스트를 하는 것이 좋으며 테스트하기 쉽도록 프라이빗 메서드를 공개하는 것은 결코 아닙니다.
개인 메서드를 단독으로 테스트하려면 Java에서 Easymock / Powermock을 사용하여 테스트합니다.
당신은 그것에 대해 실용적이어야 하며, 당신은 또한 사물을 테스트하기 어려운 이유를 알아야 한다.
'테스트를 들어보십시오' - 테스트가 어렵다면, 이것이 설계에 대해 말해주는 것이 있습니까?이 메서드에 대한 테스트는 public api를 통한 테스트로 쉽게 커버될 수 있는 부분을 다시 확인해 주시겠습니까?
Michael Features는 "Legacy Code를 사용하여 효과적으로 작업"에서 다음과 같이 말합니다.
「많은 사람들이, 이 문제를 회피하는 방법을 찾기 위해서, 많은 시간을 소비하고 있습니다.진짜 답은 프라이빗 메서드를 테스트하고 싶은 충동이 있는 경우, 그 메서드를 비공개로 해서는 안 된다는 것입니다.메서드를 공개하는 것이 귀찮은 것은, 다른 책임의 일부이기 때문일 가능성이 높습니다.[기존 코드로 효과적으로 작업(2005년)]깃털]
다른 사람들이 말한 것처럼 유닛이 프라이빗 방식을 테스트하고 있는 것은 다소 의심됩니다.유닛은 프라이빗 구현의 상세 내용이 아닌 퍼블릭인터페이스를 테스트합니다.
즉, C#에서 프라이빗한 유닛을 테스트할 때 사용하는 기술은 접근성 보호를 프라이빗에서 내부로 다운그레이드한 후 InteralsVisibleTo를 사용하여 유닛 테스트 어셈블리를 친구 어셈블리로 마크하는 것입니다.그러면 유닛 테스트 어셈블리는 내부 장치를 공용으로 취급할 수 있지만, 실수로 공용 영역에 추가할 걱정은 없습니다.
많은 답변이 퍼블릭인터페이스 테스트만을 제안하고 있지만 IMHO는 비현실적입니다.만약 어떤 방법이 5단계에 걸친 작업을 수행한다면, 이 5단계를 모두 테스트하는 것이 아니라 개별적으로 테스트하는 것이 좋습니다.이를 위해서는 5가지 방법을 모두 테스트해야 합니다.이 방법(테스트 제외)은 다음과 같습니다.private.
은, 각독자적인 「」메서드를 「프라이빗」메서드로 입니다.public인터페이스에는 포함하지 않습니다.이렇게 하면 테스트도 가능하지만 인터페이스를 부풀리지는 않습니다.
네, 이로 인해 파일링과 클래스 블리딩이 발생합니다.
이렇게 '나', '나'가 .public ★★★★★★★★★★★★★★★★★」private지정자가 중복됩니다.
그래, 이건 골칫거리야
안타깝게도 이것은 코드를 테스트하기 위해 우리가 희생하는 많은 희생 중 하나입니다.아마도 미래의 언어(또는 미래의 C#/Java 버전)에는 클래스 및 모듈 테스트 기능을 더욱 편리하게 하는 기능이 있을 것입니다.그러나 그 사이에 이러한 후프를 건너뛰어야 합니다.
각 단계가 그 자체의 클래스가 되어야 한다고 주장하는 사람들도 있지만, 나는 동의하지 않는다. 만약 그들이 모두 주를 공유한다면, 다섯 가지 방법이 가능한 다섯 개의 클래스를 만들 이유가 없다.설상가상으로, 이것은 파일링과 클래스 블러드(class-blood)가 된다.또한 모듈의 퍼블릭 API에 감염됩니다.이러한 클래스는 모두 필수입니다.public테스트 코드를 다른 모듈에서 테스트하는 경우(또는 테스트 코드를 같은 모듈에 포함시키는 경우, 즉 테스트 코드를 제품과 함께 발송합니다.
유닛 테스트는 퍼블릭 계약을 테스트해야 합니다.이것은 코드의 다른 부분에서 클래스를 사용할 수 있는 유일한 방법입니다.프라이빗 메서드는 구현 세부사항이므로 테스트하지 마십시오. 퍼블릭 API가 올바르게 동작하는 한 구현은 문제가 되지 않으며 테스트 케이스 변경 없이 변경될 수 있습니다.
IMO, 당신은 당신의 클래스가 안에서 어떻게 실행되었는지에 대해 깊이 생각하지 말고 당신의 시험을 작성해야 합니다.나중에 다른 내부 모델을 사용하여 리팩터링할 수도 있지만 이전 구현과 동일한 보증을 할 수도 있습니다.
이 점을 염두에 두고, 현재 어떤 사내에서 실시되고 있는지에 관계없이 계약이 아직 유지되고 있는지 테스트하는 데 주력할 것을 권장합니다.퍼블릭 API의 속성 기반 테스트.
패키지를 비공개로 하는 건 어때요?그러면 테스트 코드(및 패키지의 다른 클래스도)는 볼 수 있지만 사용자에게는 여전히 숨겨져 있습니다.
하지만 실제로는 개인 방법을 테스트해서는 안 됩니다.이러한 내용은 구현 세부 사항이며 계약의 일부가 아닙니다.그들이 하는 모든 일은 공공의 방법을 부르는 것으로 다루어져야 한다(만약 그들이 공공의 방법에 의해 행사되지 않는 코드를 가지고 있다면, 그것은 사라져야 한다).개인 코드가 너무 복잡하면 클래스가 너무 많은 작업을 수행하고 리팩터링이 필요할 수 있습니다.
방법을 공개하는 것은 큰 약속입니다.그렇게 하면 사람들이 사용할 수 있게 되고, 더 이상 바꿀 수 없게 됩니다.
업데이트: 이 질문에 대한 보다 광범위하고 완전한 답변을 다른 여러 곳에서 추가했습니다. 이것은 제 블로그에서 찾을 수 있습니다.
테스트하기 위해 무언가를 공개할 필요가 있는 경우, 이는 일반적으로 테스트 대상 시스템이 단일 책임 원칙을 준수하지 않음을 암시합니다.따라서 도입해야 할 클래스가 누락되어 있습니다.코드를 새 클래스로 추출한 후 공개합니다.이제 쉽게 테스트할 수 있고 SRP를 따르고 있습니다.다른 클래스는 작문을 통해 이 새로운 클래스를 호출하기만 하면 됩니다.
테스트 어셈블리에 코드를 표시하기 위한 방법 등 언어 사용 방법을 공개하거나 사용하는 것은 항상 마지막 수단이어야 합니다.
예를 들어 다음과 같습니다.
public class SystemUnderTest
{
public void DoStuff()
{
// Blah
// Call Validate()
}
private void Validate()
{
// Several lines of complex code...
}
}
검증자 개체를 도입하여 이를 리팩터링합니다.
public class SystemUnderTest
{
public void DoStuff()
{
// Blah
validator.Invoke(..)
}
}
이제 검증자가 올바르게 호출되었는지 테스트하기만 하면 됩니다.검증의 실제 프로세스(기존의 프라이빗 로직)는 완전히 격리된 상태로 테스트할 수 있습니다.이 검증에 합격하기 위해 복잡한 테스트를 설정할 필요는 없습니다.
훌륭한 답변입니다.Test-Driven Development(TDD; 테스트 주도 개발)에서는 리팩터링 단계에서 프라이빗 메서드가 생성되므로(리팩터링 패턴의 예시는 Extract Method 참조), 필요한 테스트 커버리지가 이미 갖추어져 있을 것입니다.올바르게 행해진다면(물론, 정확성에 대해서는 의견이 엇갈릴 수 있습니다), 테스트하기 위해서만 비공개 방법을 공개할 필요는 없습니다.
C# 를 사용하고 있는 경우는, 메서드를 내부로 할 수 있습니다.그래야 공공 API를 오염시키지 않습니다.
그런 다음 dll에 속성을 추가합니다.
[어셈블리:InternalsVisibleTo("MyTestAssembly")]]
이제 MyTestAssembly 프로젝트에 모든 메서드가 표시됩니다.완벽하진 않겠지만 테스트하기 위해 사적인 방법을 공개하는 것보다는 낫습니다.
스택 관리 알고리즘을 유틸리티 클래스로 분할하면 어떨까요?유틸리티 클래스는 스택을 관리하고 퍼블릭액세서를 제공할 수 있습니다.유닛 테스트는 구현 세부 사항에 초점을 맞출 수 있습니다.알고리즘적으로 까다로운 클래스에 대한 심층 테스트는 가장자리 케이스를 주름잡고 커버리지를 확보하는 데 매우 유용합니다.
그러면 현재 클래스는 구현 세부 정보를 노출하지 않고 유틸리티 클래스에 완전히 위임할 수 있습니다.이 테스트는 다른 사용자가 권장한 페이지 번호 지정 요건과 관련이 있습니다.
Java에서는 패키지를 비공개로 하는 옵션도 있습니다(즉, 가시성 수식자는 생략).유닛 테스트가 테스트 대상 클래스와 동일한 패키지에 포함되어 있는 경우 이러한 메서드를 확인할 수 있으며 메서드를 완전히 공개하는 것보다 안전합니다.
개인 메서드는 보통 "도움말" 메서드로 사용됩니다.따라서 기본 값만 반환하고 개체의 특정 인스턴스에서는 작동하지 않습니다.
테스트하려면 몇 가지 옵션이 있습니다.
- 반영 사용
- 메서드 패키지 액세스 권한 부여
또는 새로운 클래스에 적합한 경우 퍼블릭 메서드로 도우미 메서드를 사용하여 새 클래스를 만들 수도 있습니다.
여기에 아주 좋은 기사가 있어요.
필요한 경우 리플렉션을 사용하여 개인 변수에 액세스합니다.
그러나 실제로는 클래스의 내부 상태는 신경 쓰지 않습니다.예상할 수 있는 상황에서 퍼블릭 메서드가 기대하는 것을 반환하는 것을 테스트하고 싶을 뿐입니다.
당신의 업데이트에서 당신은 퍼블릭 API를 사용하여 테스트하는 것이 좋다고 말했습니다.사실 여기에는 두 개의 학교가 있다.
블랙박스 테스트
블랙박스 학교는 이 수업을 아무도 그 안에서 시행을 볼 수 없는 블랙박스로 간주해야 한다고 말한다.이를 테스트하는 유일한 방법은 공개 API를 사용하는 것입니다.클래스의 유저가 사용하는 것과 같습니다.
화이트 박스 테스트
화이트박스 스쿨은 수업의 실행에 관한 지식을 활용하고, 그것이 제대로 작동하는지 확인하기 위해 반을 테스트하는 것이 당연하다고 생각한다.
나는 그 논의에서 정말 어느 편도 들 수 없다.수업(또는 도서관 등)을 테스트할 수 있는 두 가지 뚜렷한 방법이 있다는 것을 아는 것이 흥미로울 것 같아서요.
테스트에 더방법을 는 안 것 .first()메서드입니다.- 을 번 할 수 .그러면 다음 값을 여러 번 호출할 수 있습니다.next(),previous() ★★★★★★★★★★★★★★★★★」last()결과가 예상과 일치하는지 확인할 수 있습니다.수업에 더 많은 방법을 추가하지 않으면(단순히 테스트 목적으로), 테스트의 '블랙박스' 원칙을 고수하게 될 것입니다.
절대 테스트가 코드를 지시하게 해서는 안 됩니다.저는 TDD나 다른 DD에 대해 말하는 것이 아니라, 정확히 당신이 원하는 것을 말하는 것입니다.당신의 앱은 이러한 방법을 공개해야 합니까?문제가 있는 경우는, 테스트해 주세요.그렇지 않으면 테스트용으로만 공개하지 마십시오.변수와 다른 변수도 마찬가지입니다.사용하시는 어플리케이션의 요구에 따라 코드가 결정되도록 하고, 그 요구가 충족되고 있는지 테스트하도록 합니다.(이것 역시 테스트의 첫 번째가 아니라 테스트의 목표를 달성하기 위해 클래스 구조를 변경하는 것을 의미합니다.)
대신 "더 높게 테스트"해야 합니다.프라이빗 메서드를 호출하는 메서드를 테스트합니다.단, 테스트는 어플리케이션의 요구를 테스트하는 것이지 "실장 결정"을 테스트하는 것은 아닙니다.
예를 들어 (여기에서는 bod 의사 코드);
public int books(int a) {
return add(a, 2);
}
private int add(int a, int b) {
return a+b;
}
대신 "책"을 테스트할 수 있는 "추가"를 테스트할 이유가 없습니다.
절대 테스트로 코드 설계를 결정하지 마십시오.결과를 어떻게 얻느냐가 아니라 예상한 결과를 얻느냐를 테스트합니다.
나는 그것이 나쁜 생각이라고 말하고 싶다. 왜냐하면 나는 당신이 앞으로 어떤 이익과 잠재적인 문제를 얻을 수 있을지 확신이 없기 때문이다.콜 계약을 변경하는 경우, 프라이빗 메서드를 테스트하기 위해서만 클래스를 사용하는 방법을 테스트하는 것이 아니라 의도하지 않은 인위적인 시나리오를 작성합니다.
또, 이 방법을 공개한다고 하는 것으로써, 6개월 이내에(메서드를 공개하는 유일한 이유가 테스트의 목적이라는 것을 잊은 후에) 전혀 다른 사람이 이 방법을 사용하지 않게 되어, 의도하지 않은 결과나 유지보수의 악몽이 생길 가능성이 있습니다.
먼저 이 방법을 다른 클래스로 추출하여 공개해야 하는지 확인합니다.그렇지 않은 경우 패키지로 보호하고 Java에서 @VisibleForTesting으로 주석을 추가합니다.
개별적으로 테스트하는 개인 메서드는 클래스에 다른 "개념"이 포함되어 있음을 나타냅니다.해당 "개념"을 자체 클래스로 추출하여 별도의 "단위"로 테스트합니다.
이 비디오에서 이 주제에 대한 정말 흥미로운 관점을 찾아보세요.
실제로 이 작업을 수행해야 하는 상황이 있습니다(예를 들어 복잡한 알고리즘을 구현하는 경우).그냥 패키지-프라이빗으로 하면 충분할 거야.그러나 대부분의 경우 논리학을 다른 클래스에서 제외해야 하는 너무 복잡한 클래스가 있을 수 있습니다.
나는 일부 구성원의 가시성을 높이는 문제보다 그것을 테스트하는 것의 보너스가 더 낫다는 것에 동의하는 경향이 있다.약간의 개선사항은 보호 및 가상화를 한 후 테스트 클래스에서 덮어쓰고 노출하는 것입니다.
또는 기능을 개별적으로 테스트하는 경우 설계에서 누락된 개체를 제안하지 않습니까?아마 다른 시험 가능한 수업에 넣을 수 있을 거예요그러면 기존 클래스가 이 새 클래스의 인스턴스에 위임됩니다.
일반적으로 테스트 클래스는 테스트 대상 클래스와 동일한 프로젝트/어셈블리에 보관합니다.
이 방법만 있으면 돼internal가시성을 통해 기능/기능을 테스트할 수 있습니다.
이로 인해 구축 프로세스가 다소 복잡해지고 테스트 클래스가 필터링됩니다.는 모든 수업의 이 를 달성합니다.TestedClassTest정규식을 사용하여 클래스를 필터링합니다.
이는 물론 C# /에만 적용됩니다.질문의 NET 부분
저는 자주 예요.validate,verify,check오브젝트의 내부 상태를 테스트하기 위해 호출할 수 있도록 클래스, 등.
이 메서드는 ifdef 블록(대부분 C++로 작성)으로 랩되어 릴리즈용으로 컴파일되지 않는 경우가 있습니다.그러나 프로그램 오브젝트 트리를 걸어 사물을 확인하는 검증 방법을 제공하는 것이 릴리스에서 유용한 경우가 많습니다.
Guava에는 @VisibleForTesting 주석이 있습니다.마킹 메서드에는 확장 범위(패키지 또는 퍼블릭)가 있습니다.같은 일에 @Private 주석을 사용합니다.
공개 API를 테스트해야 하지만, 때로는 공개되지 않는 것을 입수하는 것이 편리하고 현명할 수 있다.
언제:
- 토토에서는 여러 개의 클래스로 나누면 클래스가 상당히 읽기 어려워집니다.
- 좀 더 테스트하기 쉽게 하려고
- 내장에 대한 시험 접근을 제공하면
종교가 공학보다 앞서고 있는 것 같아요
저는 보통 그런 방법들을 남겨두고protected장치 테스트를 동일한 패키지(단, 다른 프로젝트 또는 소스 폴더) 내에 배치합니다. 클래스 로더가 모든 보호된 메서드를 동일한 네임스페이스에 배치하기 때문에 액세스 할 수 있습니다.
아니, 고양이 가죽을 벗기는 더 좋은 방법이 있거든
일부 유닛 테스트 하네스는 클래스 정의의 매크로에 의존합니다.이 매크로가 자동으로 확장되어 테스트 모드로 내장되었을 때 훅이 생성됩니다.C스타일이지만 효과가 있어요.
보다 쉬운 OO 관용구는 테스트하고자 하는 모든 것을 "비밀"이 아닌 "보호"로 만드는 것입니다.테스트 하니스는 테스트 대상 클래스에서 상속된 후 모든 보호된 멤버에 액세스할 수 있습니다.
아니면 "친구" 옵션을 선택하든지.개인적으로 이것은 C++의 기능입니다.캡슐화 규칙을 어기고 있기 때문에 가장 마음에 듭니다만, C++가 몇 가지 기능을 실장하기 위해서 필요한 것이기도 합니다.그렇기 때문에, Hey ho.
어쨌든 유닛 테스트의 경우, 그 멤버에게 가치를 주입할 필요가 있습니다.화이트 박스 문자는 완벽하게 유효합니다.그러면 캡슐화가 깨집니다.
에는 라는 .넷으로 하다PrivateObject클래스의 개인 메서드에 액세스할 수 있도록 특별히 설계되어 있습니다.
자세한 내용은 MSDN 또는 스택오버플로우를 참조해 주세요.
(지금까지 아무도 언급하지 않은 것 같습니다.)
이것만으로는 불충분한 상황도 있습니다만, 이 경우 성찰이 필요합니다.
그래도 개인 방법을 테스트하지 말라는 일반적인 권고를 고수하고 싶지만, 언제나 그렇듯이 예외는 있습니다.
아주 좋은 질문입니다.
@BlueRaja - Danny Plughoft의 훌륭한 답변인 IHMO는 최고 중 하나입니다.
많은 답변이 퍼블릭인터페이스 테스트만을 제안하고 있지만 IMHO는 비현실적입니다.만약 어떤 방법이 5단계에 걸친 작업을 수행한다면, 이 5단계를 모두 테스트하는 것이 아니라 개별적으로 테스트하는 것이 좋습니다.이를 위해서는 5가지 방법을 모두 테스트해야 합니다.이 방법(테스트 제외)은 비공개일 수 있습니다.
무엇보다 '단위를 테스트하기 위해 개인 방식을 공개해야 하는가'라는 질문은 객관적으로 정답이 여러 파라미터에 따라 달라지는 문제라는 점을 강조하고 싶다.
그래서 나는 어떤 경우에는 하지 않아도 되고 어떤 경우에는 해야 한다고 생각한다.
프라이빗 메서드를 공개하거나 프라이빗 메서드를 다른 클래스(신규 또는 기존)에서 퍼블릭 메서드로 추출할 것인가?
그것은 좀처럼 최선의 방법이 아니다.
유닛 테스트는 하나의 API 메서드/함수의 동작을 테스트해야 합니다.
를 public 것을 public동일한 성분에 속하는 메서드는 단위 테스트하지 않습니다.여러 개를 테스트합니다. public여러 가지 방법을 동시에 사용합니다.
그 결과, 테스트, 고정 장치, 테스트 어설션, 테스트 유지보수 및 보다 일반적인 애플리케이션 설계를 복제할 수 있습니다.
테스트 값이 감소함에 따라 테스트 값을 작성하거나 유지하는 개발자에게 흥미를 잃게 되는 경우가 많습니다.
중복을 " " "를 " "를 사용합니다.private 법 methodpublicmethod, 많은 경우 새로운 클래스 또는 기존 클래스의 method로서 method를 추출하는 것이 더 나은 해결책이다.
설계상의 결함을 발생시키지 않습니다.
그러면 코드가 더 의미 있고 클래스가 덜 부풀게 됩니다.
게다가 가끔은privatemethod는 특정 구조에서 동작이 더 잘 맞는 반면 클래스의 루틴 또는 규칙입니다.
마지막으로 코드를 테스트하기 쉬워지고 테스트 중복을 방지합니다.
의 중복을 는 유닛 .public자체 테스트 클래스 및 클라이언트 클래스의 테스트 클래스에서는 종속성을 조롱해야 합니다.
사적인 방법을 조롱한다고?
리플렉션이나 툴로 PowerMock을 사용하면 가능하지만 디자인상의 문제를 회피하는 방법이 될 수 있다고 생각합니다.
A private멤버는 다른 클래스에 노출되지 않도록 설계되어 있습니다.
시험 수업은 다른 수업과 같다.그래서 우리는 그것에 같은 규칙을 적용해야 합니다.
테스트 대상 객체의 공개 방법을 조롱하는 건가요?
.private로로 합니다.public방법을 테스트합니다.
방식을 퍼블릭 방식을 할 수도 .public도구를 Mockito(스파이 개념)로 사용하는 방법이지만 조롱과 유사합니다.private방법, 우리는 테스트 대상 물체를 조롱하는 것을 피해야 한다.
Mockito.spy()에는 그 있습니다.
실제 객체의 스파이를 만듭니다.스파이는 >> stubbled 이외의 실제 메서드를 호출합니다.
실제 스파이는 레거시 코드를 다룰 때처럼 신중하게 그리고 가끔 사용해야 합니다.
험상, 용용을 한다.spy()일반적으로 테스트 품질과 가독성이 저하됩니다.
게다가 테스트 대상 물체는 모의물체이면서 실제물체이기 때문에 오류가 발생하기 쉽다.
잘못된 합격 테스트를 작성하는 가장 좋은 방법이 될 수 있습니다.
.private은 그대로 있어야 .private또는 리팩터링됩니다.
1) 절대 1) 절대 안 돼요 1) 안 돼요.private 법 methodpublic이 메서드가 한 번 호출된 경우.
은 것은다이 it it이다.private단일 메서드에 대한 메서드입니다.따라서 테스트 로직은 한 번 호출되므로 복제할 수 없습니다.
2) '아니다'가 '아니다'가 '아니다'가 '아니다'로 되어 있는지 요.private.public method if method if 명령어private메서드가 여러 번 호출되었습니다.
떻게게결?결 정??
private이치노
-> 메서드는 그대로 비공개로 유지합니다.private방법 단위 의 각 에 대해 몇 테스트를 .publicprivate★★★★★★ 。
-> 반복 처리로 인해 클라이언트에 제공되는 API의 일부가 될 수 있는 경우(보안 문제 없음, 내부 처리 없음 등), 새로운 클래스의 메서드로 메서드를 추출합니다.
-> 그렇지 않으면 반복 처리로 인해 클라이언트에 제공되는 API(보안 문제, 내부 처리 등)의 일부가 되지 않을 경우 까지 메서드의 가시성을 확대하지 마십시오.
그대로 두거나 할 수 .private패키지 클래스는 API의 일부가 되지 않으며 클라이언트가 액세스할 수 없습니다.
코드 예시
Java JUnit, Assertion J(J) Mockito.
하지만 전체적인 접근법은 C#에도 유효하다고 생각합니다.
1) : :private는 을 생성하지
, 여기 ㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇ.Computation일부 계산을 수행하기 위한 메서드를 제공하는 클래스입니다.
공개 은 " " " 를 합니다.mapToInts()★★★★★★ 。
public class Computation {
public int add(String a, String b) {
int[] ints = mapToInts(a, b);
return ints[0] + ints[1];
}
public int minus(String a, String b) {
int[] ints = mapToInts(a, b);
return ints[0] - ints[1];
}
public int multiply(String a, String b) {
int[] ints = mapToInts(a, b);
return ints[0] * ints[1];
}
private int[] mapToInts(String a, String b) {
return new int[] { Integer.parseInt(a), Integer.parseInt(b) };
}
}
테스트 코드는 다음과 같습니다.
public class ComputationTest {
private Computation computation = new Computation();
@Test
public void add() throws Exception {
Assert.assertEquals(7, computation.add("3", "4"));
}
@Test
public void minus() throws Exception {
Assert.assertEquals(2, computation.minus("5", "3"));
}
@Test
public void multiply() throws Exception {
Assert.assertEquals(100, computation.multiply("20", "5"));
}
}
「 」 「 」 「 」 「 」의 할 수 .private 법 methodmapToInts()테스트 로직이 중복되지 않습니다.
이것은 중간 작업이며 테스트에서 우리가 주장할 필요가 있는 특정한 결과를 낳지 않습니다.
2) : :private 않은 .
, 여기 ㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇㅇ.MessageService메시지 작성 메서드를 제공하는 클래스입니다.
★★★★★public는 ""를 합니다.createHeader(): 법: :
public class MessageService {
public Message createMessage(String message, Credentials credentials) {
Header header = createHeader(credentials, message, false);
return new Message(header, message);
}
public Message createEncryptedMessage(String message, Credentials credentials) {
Header header = createHeader(credentials, message, true);
// specific processing to encrypt
// ......
return new Message(header, message);
}
public Message createAnonymousMessage(String message) {
Header header = createHeader(Credentials.anonymous(), message, false);
return new Message(header, message);
}
private Header createHeader(Credentials credentials, String message, boolean isEncrypted) {
return new Header(credentials, message.length(), LocalDate.now(), isEncrypted);
}
}
테스트 코드는 다음과 같습니다.
import java.time.LocalDate;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import junit.framework.Assert;
public class MessageServiceTest {
private MessageService messageService = new MessageService();
@Test
public void createMessage() throws Exception {
final String inputMessage = "simple message";
final Credentials inputCredentials = new Credentials("user", "pass");
Message actualMessage = messageService.createMessage(inputMessage, inputCredentials);
// assertion
Assert.assertEquals(inputMessage, actualMessage.getMessage());
Assertions.assertThat(actualMessage.getHeader())
.extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
.containsExactly(inputCredentials, 9, LocalDate.now(), false);
}
@Test
public void createEncryptedMessage() throws Exception {
final String inputMessage = "encryted message";
final Credentials inputCredentials = new Credentials("user", "pass");
Message actualMessage = messageService.createEncryptedMessage(inputMessage, inputCredentials);
// assertion
Assert.assertEquals("Aç4B36ddflm1Dkok49d1d9gaz", actualMessage.getMessage());
Assertions.assertThat(actualMessage.getHeader())
.extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
.containsExactly(inputCredentials, 9, LocalDate.now(), true);
}
@Test
public void createAnonymousMessage() throws Exception {
final String inputMessage = "anonymous message";
Message actualMessage = messageService.createAnonymousMessage(inputMessage);
// assertion
Assert.assertEquals(inputMessage, actualMessage.getMessage());
Assertions.assertThat(actualMessage.getHeader())
.extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
.containsExactly(Credentials.anonymous(), 9, LocalDate.now(), false);
}
}
「 」 「 」 「 」 「 」의 할 수 .private 법 methodcreateHeader()는 테스트 로직에서 몇 가지 중복을 생성합니다.
createHeader()테스트에서 주장할 필요가 있는 구체적인 결과를 만들어냅니다.
단일 어설션이 필요하지만 헤더 콘텐츠의 3배를 어설션합니다.
두한 것은 을 알 수 .private논리가 수 .private★★★★★★ 。
게다가, 매번 우리는 새로운 것을 추가합니다.public in the method in the method in 。MessageService를 호출합니다.createHeader() 이 할 것 같습니다.
에 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아,createHeader()을 사용하다이러한 테스트도 모두 변경해야 할 수 있습니다.
확실히 좋은 디자인은 아닙니다.
리팩터링 단계
를 들어, 우리가 , 하다, 하다, 하다, , 라고 가정해 봅시다.createHeader()API를 사용합니다.
, 그럼 먼저 을 해보도록 하겠습니다.MessageService 로 이행합니다.createHeader()로로 합니다.public:
public Header createHeader(Credentials credentials, String message, boolean isEncrypted) {
return new Header(credentials, message.length(), LocalDate.now(), isEncrypted);
}
이제 이 방법을 테스트해 볼 수단은 다음과 같습니다.
@Test
public void createHeader_with_encrypted_message() throws Exception {
...
boolean isEncrypted = true;
// action
Header actualHeader = messageService.createHeader(credentials, message, isEncrypted);
// assertion
Assertions.assertThat(actualHeader)
.extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
.containsExactly(Credentials.anonymous(), 9, LocalDate.now(), true);
}
@Test
public void createHeader_with_not_encrypted_message() throws Exception {
...
boolean isEncrypted = false;
// action
messageService.createHeader(credentials, message, isEncrypted);
// assertion
Assertions.assertThat(actualHeader)
.extracting(Header::getCredentials, Header::getLength, Header::getDate, Header::isEncryptedMessage)
.containsExactly(Credentials.anonymous(), 9, LocalDate.now(), false);
}
우리가 에 쓴 되나요?public" " 를 사용하는 createHeader()
차이가 별로 없어요.
실, 리, 리, 리, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as these,public메서드는 반환된 헤더 값에 대해 아직 테스트해야 합니다.
이러한 주장을 삭제하면 이에 대한 퇴행은 검출되지 않을 수 있습니다.
할 수 , 이 처리는 분리할 수 .createHeader()메서드는 테스트된 컴포넌트에 속합니다.
그래서 내가 대답의 첫머리에서 대부분의 경우, 우리는 그 추출을 선호해야 한다고 설명한 것이다.private액세스 수식자를 변경하는 다른 클래스의 메서드public.
그래서 소개하겠습니다.HeaderService:
public class HeaderService {
public Header createHeader(Credentials credentials, String message, boolean isEncrypted) {
return new Header(credentials, message.length(), LocalDate.now(), isEncrypted);
}
}
이행을 실시합니다.createHeader()에서 테스트하다HeaderServiceTest.
지금이다MessageService에 의해 정의됩니다.HeaderService의존관계:
public class MessageService {
private HeaderService headerService;
public MessageService(HeaderService headerService) {
this.headerService = headerService;
}
public Message createMessage(String message, Credentials credentials) {
Header header = headerService.createHeader(credentials, message, false);
return new Message(header, message);
}
public Message createEncryptedMessage(String message, Credentials credentials) {
Header header = headerService.createHeader(credentials, message, true);
// specific processing to encrypt
// ......
return new Message(header, message);
}
public Message createAnonymousMessage(String message) {
Header header = headerService.createHeader(Credentials.anonymous(), message, false);
return new Message(header, message);
}
}
그리고...MessageService각 헤더 값은 이미 테스트되었기 때문에 더 이상 어설션할 필요가 없습니다.
우리는 단지 이 모든 것을 확실하게 하고 싶다.Message.getHeader()무엇을 반환합니까?HeaderService.createHeader()가 돌아왔습니다.
예를 들어, 의 새로운 버전을 다음에 나타냅니다.createMessage()테스트:
@Test
public void createMessage() throws Exception {
final String inputMessage = "simple message";
final Credentials inputCredentials = new Credentials("user", "pass");
final Header fakeHeaderForMock = createFakeHeader();
Mockito.when(headerService.createHeader(inputCredentials, inputMessage, false))
.thenReturn(fakeHeaderForMock);
// action
Message actualMessage = messageService.createMessage(inputMessage, inputCredentials);
// assertion
Assert.assertEquals(inputMessage, actualMessage.getMessage());
Assert.assertSame(fakeHeaderForMock, actualMessage.getHeader());
}
주의:assertSame()내용을 비교하지 않고 헤더의 객체 참조를 비교하기 위해 사용합니다.
지금이다,HeaderService.createHeader()동작과 다른 값을 반환할 수 있습니다.그것은, 에서는 문제가 되지 않습니다.MessageService의 시점을 테스트합니다.
유닛 테스트의 포인트는 해당 유닛에 대한 퍼블릭 API의 동작을 확인하는 것입니다.테스트만을 위해서 프라이빗 방식을 공개할 필요는 없습니다.그렇다면 인터페이스를 재검토해야 합니다.프라이빗 메서드는 퍼블릭인터페이스에 대한 '도움말' 메서드로 간주되므로 프라이빗 메서드를 호출할 때 퍼블릭인터페이스를 통해 테스트됩니다.
이 작업을 '필요'하게 된 유일한 이유는 당신의 수업이 시험을 위해 적절하게 설계되지 않았기 때문입니다.
언급URL : https://stackoverflow.com/questions/31646092/is-unit-testing-alone-ever-a-good-reason-to-expose-private-instance-variables-vi
'programing' 카테고리의 다른 글
| 콤마가 있는 문자열을 배열로 변환 (0) | 2022.10.08 |
|---|---|
| Java: 와의 차이점은 무엇입니까? (0) | 2022.10.08 |
| LINQ에서 엔티티로 'System' 메서드를 인식하지 않습니다.String ToString() 메서드입니다.이 메서드는 스토어 식으로 변환할 수 없습니다. (0) | 2022.10.07 |
| PHPStorm의 PHP 파일에 대한 잘못된 구문 강조 표시 (0) | 2022.10.07 |
| 'key' 및 lamda 식을 사용하는 python max 함수 (0) | 2022.10.07 |