게터와 세터에 대한 테스트를 작성해야합니까, 아니면 과잉입니까?
게터와 세터에 대한 테스트를 작성해야합니까, 아니면 과잉입니까?
답변:
나는 아니오라고 말할 것입니다.
@ 윌은 100 % 코드 범위를 목표로 삼아야한다고 말했지만 내 생각에는 위험한 혼란입니다. 적용 범위가 100 % 인 단위 테스트를 작성할 수 있지만 전혀 테스트 할 수는 없습니다.
단위 테스트는 표현적이고 의미있는 방식으로 코드의 동작을 테스트하기 위해 제공되며 게터 / 세터는 끝의 수단 일뿐입니다. 테스트에서 getter / setter를 사용하여 "실제"기능을 테스트한다는 목표를 달성하면 충분합니다.
반면에, getter와 setter가 단지 get and set 이상의 기능을 수행하는 경우 (즉, 적절하게 복잡한 방법) 테스트를 받아야합니다. 그러나 단위 테스트 케이스를 작성하지 않는 단지 시간 낭비 게터 또는 세터를 테스트합니다.
Roy Osherove는 그의 유명한 저서 'The Art Of Unit Testing'에서 다음과 같이 말합니다.
속성 (Java의 getter / setter)은 일반적으로 로직을 포함하지 않으며 테스트 할 필요가없는 코드의 좋은 예입니다. 그러나 조심하십시오 : 일단 속성 내부에 검사를 추가하면 논리가 테스트되고 있는지 확인해야합니다.
참고 :이 답변은 잠재적으로 나쁜 조언이기는하지만 계속 upvotes를 얻습니다. 이유를 이해하려면 아래의 여동생을 살펴보십시오 .
논란의 여지가 있지만, 나는이 질문에 '아니오'라고 대답하는 사람은 TDD의 기본 개념이 누락되었다고 주장합니다.
나를 위해, 대답은 울려 퍼지는이다 예는 는 TDD를 수행합니다. 그렇지 않다면 그럴듯한 대답은 없습니다.
TDD는 종종 주요 이점이 있다고 인용됩니다.
프로그래머로서, 속성을 중요성과 게터로 생각하고 일종의 오버 헤드로 세터를 생각하는 것은 대단히 유혹적입니다.
그러나 속성은 구현 세부 사항이며 세터와 게터는 실제로 프로그램을 작동시키는 계약 인터페이스입니다.
객체가 다음과 같이 철자를 작성하는 것이 훨씬 더 중요합니다.
클라이언트가 상태를 변경하도록 허용
과
클라이언트가 상태를 쿼리하도록 허용
그런 다음이 상태가 실제로 저장되는 방법 (속성이 가장 일반적이지만 유일한 방법은 아닙니다).
다음과 같은 테스트
(The Painter class) should store the provided colour
TDD 의 문서 부분에 중요합니다 .
테스트를 작성할 때 최종 구현이 사소한 (속성) 방어 이점 이 없다는 사실을 알 수 없습니다.
시스템 개발 세계의 주요 문제 중 하나는 왕복 공학 의 부족입니다. 1- 시스템의 개발 프로세스가 분리 된 하위 프로세스로 분할됩니다 (문서, 코드).
1 브로디, 마이클 L. "존 마일로 풀 로스 : 개념적인 모델링의 재봉 씨." 개념 모델링 : 기초 및 응용. Springer Berlin Heidelberg, 2009. 1-9.
시스템 및 해당 코드의 스펙이 항상 일관되도록 TDD 의 문서 부분입니다.
TDD 내에서 먼저 합격 합격 테스트를 작성한 다음 통과 할 수있는 코드 만 작성하십시오.
높은 수준의 BDD 내에서 먼저 시나리오를 작성한 다음 통과시킵니다.
왜 세터와 게터를 제외시켜야합니까?
이론적으로 TDD 내에서 한 사람이 테스트를 작성하고 다른 사람이 테스트를 통과시키는 코드를 구현할 수 있습니다.
그러므로 스스로에게 물어보십시오 :
클래스 테스트를 작성하는 사람이 getter 및 setter를 언급해야합니다.
getter와 setter는 클래스에 대한 공용 인터페이스이므로 대답은 명백히 yes 이거나 객체의 상태를 설정하거나 쿼리 할 방법이 없습니다. 그러나이 작업을 수행하는 방법이 각 방법을 개별적으로 테스트하는 것은 아닙니다 . 자세한 내용은 다른 답변 을 참조하십시오.
분명히 코드를 먼저 작성하면 답이 명확하지 않을 수 있습니다.
TL; 박사 : 네 당신이 해야 하고, 함께 OpenPojo 사소한입니다.
게터와 세터에서 약간의 유효성 검사를 수행해야하므로 테스트해야합니다. 예를 들어, setMom(Person p)
자신보다 어린 사람을 어머니로 설정해서는 안됩니다.
지금도 그 일을하고 있지 않더라도 미래에 일어날 확률은 회귀 분석에 도움이 될 것입니다. 당신이 어머니를 설정하게하려는 경우 null
, 나중에 누군가가 그것을 바꿀 수 있는지 테스트해야합니다. 그러면 당신의 가정이 강화 될 것입니다.
일반적인 버그는 void setFoo( Object foo ){ foo = foo; }
어디에 있어야 하는가void setFoo( Object foo ){ this.foo = foo; }
. (첫 번째 경우 foo
에 작성되는 것은 오브젝트 의 필드가 아닌 매개 변수입니다 ).foo
배열이나 컬렉션을 반환하는 경우 getter가 반환하기 전에 setter로 전달 된 데이터의 방어 복사본 을 수행 할 것인지 테스트해야합니다 .
그렇지 않으면 가장 기본적인 세터 / 게터가 있다면 단위 테스트를 수행하면 객체 당 최대 10 분 정도가 추가되므로 손실은 무엇입니까? 동작을 추가하면 이미 골격 테스트를 받았으며이 회귀 테스트를 무료로받을 수 있습니다. Java를 사용하는 경우 OpenPojo 가 있으므로 변명의 여지가 없습니다 . 사용 가능한 기존 규칙 세트가 있고 전체 프로젝트를 스캔하여 규칙 내에서 일관되게 적용되도록 할 수 있습니다.
그들의 예에서 :
final PojoValidator pojoValidator = new PojoValidator();
//create rules
pojoValidator.addRule( new NoPublicFieldsRule () );
pojoValidator.addRule( new NoPrimitivesRule () );
pojoValidator.addRule( new GetterMustExistRule () );
pojoValidator.addRule( new SetterMustExistRule () );
//create testers
pojoValidator.addTester( new DefaultValuesNullTester () );
pojoValidator.addTester( new SetterTester () );
pojoValidator.addTester( new GetterTester () );
//test all the classes
for( PojoClass pojoClass : PojoClassFactory.getPojoClasses( "net.initech.app", new FilterPackageInfo() ) )
pojoValidator.runValidation( pojoClass );
정교하게 해주세요 :
에서 레거시 코드를 효과적으로 작업을 1 :
단위 테스트라는 용어는 소프트웨어 개발에서 오랜 역사를 가지고 있습니다. 단위 테스트의 대부분의 개념에 공통되는 것은 소프트웨어의 개별 구성 요소를 분리 한 테스트라는 개념입니다. 구성 요소 란 무엇입니까? 정의는 다양하지만 단위 테스트에서는 일반적으로 시스템의 가장 원자적인 동작 단위에 관심이 있습니다. 절차 코드에서 단위는 종종 기능입니다. 객체 지향 코드에서 단위는 클래스입니다.
getter와 setter를 찾는 OOP를 사용하면 단위는 클래스 이며 반드시 개별 메소드는 아닙니다. 입니다.
모든 요구 사항 및 테스트는 Hoare 로직 의 형식을 따릅니다 .
{P} C {Q}
어디:
{P}
전제 조건입니다 ( 주어진 )C
트리거 조건입니다 ( when ){Q}
사후 조건이다 ( then )그런 다음 최대가됩니다.
구현이 아닌 테스트 동작
즉 C
, 사후 조건을 달성하는 방법을 테스트하지 말고 {Q}
그 결과인지 확인해야합니다 C
.
OOP에 관해서 C
는 클래스입니다. 따라서 내부 효과는 테스트하지 말고 외부 효과 만 테스트해야합니다.
게터와 세터는 약간의 로직을 포함 할 수 있지만, 너무 오래이 논리는 외부 효과가 없습니다 - 그들을 만드는 콩 접근 (2) 자를 ) 테스트는 객체 내부를 살펴 봐야합니다.
따라서 빈 getter와 setter를 분리해서 테스트해서는 안됩니다. 이것은 나쁘다 :
Describe 'LineItem class'
Describe 'setVAT()'
it 'should store the VAT rate'
lineItem = new LineItem()
lineItem.setVAT( 0.5 )
expect( lineItem.vat ).toBe( 0.5 )
경우 만 setVAT
예외를 슬로우 해주기 때문에, 해당 테스트는 적절한 것 이 외부 효과.
이러한 효과가 나중에 영향을 주더라도 외부에 영향을 미치지 않으면 객체의 내부 상태를 변경하는 지점은 거의 없습니다.
따라서 세터와 게터에 대한 테스트는 내부 메소드가 아닌 이러한 메소드의 외부 효과에 대해 고려해야합니다.
예를 들면 다음과 같습니다.
Describe 'LineItem class'
Describe 'getGross()'
it 'should return the net time the VAT'
lineItem = new LineItem()
lineItem.setNet( 100 )
lineItem.setVAT( 0.5 )
expect( lineItem.getGross() ).toBe( 150 )
당신은 자신에게 생각할 수 있습니다 :
잠깐만, 우리는
getGross()
여기서 테스트 하지 않습니다setVAT()
.
그러나 setVAT()
오작동 인 경우 해당 테스트는 모두 동일하게 실패합니다.
1 Feathers, M., 2004. 레거시 코드로 효과적으로 작업합니다. 프렌 티스 홀 프로페셔널.
2 Martin, RC, 2009. 깨끗한 코드 : 민첩한 소프트웨어 장인의 핸드북. 피어슨 교육.
속성에 대한 정당한 이유가 있지만 속성을 통해 멤버 상태를 노출하는 것은 잘못된 디자인이라는 일반적인 객체 지향 디자인의 믿음이 있습니다. 공개 폐쇄 원칙에 대한 Robert Martin의 기사 는 속성이 커플 링을 권장하므로 클래스를 수정하지 못하도록 제한하는 기능을 제한함으로써이를 확장합니다. 속성을 수정하면 클래스의 모든 소비자도 변경해야합니다. 그는 멤버 변수를 노출하는 것이 반드시 나쁜 디자인 일 필요는 없으며 스타일이 좋지 않을 수도 있다고 규정합니다. 그러나 속성이 읽기 전용 인 경우 남용 및 부작용이 적습니다.
단위 테스트에 제공 할 수있는 가장 좋은 방법은 가능한 한 많은 속성을 보호하거나 내부적으로 만드는 것입니다. 이렇게하면 게터와 세터에 대한 바보 같은 테스트를 작성하지 말고 커플 링을 방지 할 수 있습니다.
입력 필드에 바인딩 된 ViewModel 속성과 같이 읽기 / 쓰기 속성을 사용해야하는 분명한 이유가 있습니다.
보다 실질적으로, 단위 테스트는 공개 방법을 통해 기능성을 주도해야합니다. 테스트중인 코드가 해당 속성을 사용하는 경우 코드 적용 범위가 무료입니다. 이러한 속성이 코드 커버리지로 강조 표시되지 않는 경우 다음과 같은 가능성이 매우 높습니다.
getter 및 setter에 대한 테스트를 작성하면 적용 범위가 잘못되어 실제로 기능적 동작에 의해 특성이 사용되는지 여부를 판별 할 수 없습니다.
유머러스하면서도 현명한 선택 : Testivus의 길
"지금 할 수있는 테스트를 작성하십시오"
숙련 된 테스터이고 소규모 프로젝트 인 경우 게터 / 세터 테스트가 과도 할 수 있습니다. 그러나 단위 테스트 방법을 배우기 시작하거나 이러한 게터 / 세터에 로직 ( setMom()
예 : @ArtB의 예) 이 포함되어 있으면 테스트를 작성하는 것이 좋습니다.
이것은 실제로 우리 팀과 I 사이의 최근 주제였습니다. 우리는 80 %의 코드 범위를 촬영합니다. 우리 팀은 게터와 세터가 자동으로 구현되고 컴파일러가 기본 코드를 생성한다고 주장합니다. 이 경우 생성 된 코드가 방해가되지 않기 때문에 컴파일러에서 생성 한 코드를 테스트하는 것은 실제로 의미가 없습니다. 우리는 비동기 메소드에 대해서도이 논의를했으며,이 경우 컴파일러는 뒤에서 많은 코드를 생성합니다. 이것은 다른 경우이며 우리가 테스트하는 것입니다. 긴 답변을 짧게 작성하고 팀에 가져와 테스트 할 가치가 있는지 스스로 결정하십시오.
또한 코드 커버리지 보고서를 사용하는 경우 [ExcludeFromCodeCoverage] 특성을 추가하면됩니다. 우리의 솔루션은 getter 및 setter를 사용하는 속성이 있거나 속성 자체에 모델이있는 모델에 이것을 사용하는 것입니다. 이렇게하면 코드 범위 백분율을 계산하는 데 사용한다고 가정하고 코드 범위 보고서가 실행될 때 총 코드 범위 %에 영향을 미치지 않습니다. 행복한 테스트!
내 생각에 코드 범위는 당신이 다루어야 할 기능을 놓쳤는 지 확인하는 좋은 방법입니다.
당신이 그것을 꽤 채색하여 수동으로 적용 범위를 검사 할 때, 평범한 게터와 세터는 테스트 할 필요가 없다고 주장 할 수 있습니다 (항상 그래도).
프로젝트에서 코드 적용률 만 확인하면 80 %와 같은 테스트 적용률은 의미가 없습니다. 모든 논리적 부분을 테스트하고 중요한 부분을 잊을 수 있습니다. 이 경우 100 %만이 모든 중요한 코드 및 모든 비논리적 코드를 테스트했음을 의미합니다. 99.9 %가 되 자마자 무언가를 잊었다는 것을 알게됩니다.
그건 그렇고 : 코드 범위는 클래스를 완전히 (단위) 테스트했는지 확인하는 최종 점검입니다. 그러나 100 % 코드 적용 범위가 반드시 클래스의 모든 기능을 실제로 테스트했음을 의미하지는 않습니다. 따라서 단위 테스트는 항상 클래스의 논리에 따라 구현되어야합니다. 결국 당신은 당신이 아무것도 잊었는지 확인하기 위해 적용 범위를 실행합니다. 당신이 그것을 올바르게했을 때, 당신은 처음으로 100 %를 쳤다.
한 가지 더 : 최근 네덜란드의 대형 은행에서 일하면서 소나가 100 % 코드 범위를 나타내는 것을 알았습니다. 그러나 나는 무언가가 빠져 있다는 것을 알았습니다. 파일 당 코드 적용 비율을 검사하면 파일이 낮은 비율로 표시됩니다. 전체 코드 기본 백분율은 하나의 파일이 백분율을 99.9 %로 표시하지 않은 크기입니다. 그래서 당신은 이것을 찾고 싶을 것입니다 ...
JUnit 코드 자체에서 얻은 적용 범위에 대해 약간의 분석을 수행했습니다 .
발견되지 않은 코드의 한 범주는 "너무 단순 테스트하기" 입니다. 여기에는 간단한 Getter 및 Setter가 포함되며 JUnit 개발자는 테스트 하지 않습니다 .
반면에 JUnit에는 테스트가 적용되지 않는 3 줄보다 긴 (더 이상 사용되지 않는) 메소드가 없습니다.
예 : getter / setter 메소드의 오류는 자동으로 몰래 들어가서 추악한 버그를 일으킬 수 있습니다.
나는 이것과 다른 테스트를 더 쉽게하기 위해 lib를 작성했다. JUnit 테스트에서 작성해야 할 유일한 것은 다음과 같습니다.
assertTrue(executor.execute(Example.class, Arrays.asList( new DefensiveCopyingCheck(),
new EmptyCollectionCheck(), new GetterIsSetterCheck(),
new HashcodeAndEqualsCheck(), new PublicVariableCheck())));
그렇습니다. 특히 가져올 항목이 추상 클래스에서 서브 클래스 된 클래스의 객체 인 경우 특히 그렇습니다. IDE는 특정 속성이 초기화되지 않았다는 경고 메시지를 표시하거나 표시하지 않을 수 있습니다.
그리고 분명히 관련이없는 테스트가 a NullPointerException
와 충돌 하고 gettable 속성이 실제로 처음에 도착하지 않는다는 것을 알아내는 데 시간이 걸립니다.
그럼에도 불구하고 여전히 생산 문제를 발견하는 것만 큼 나쁘지는 않습니다.
모든 추상 클래스에 생성자가 있는지 확인하는 것이 좋습니다. 그렇지 않은 경우 게터 테스트를 통해 문제를 경고 할 수 있습니다.
프리미티브의 getter 및 setter와 관련하여 질문은 다음과 같습니다. 프로그램을 테스트 중입니까, 아니면 JVM 또는 CLR을 테스트 중입니까? 일반적으로 JVM은 테스트 할 필요가 없습니다.