spock mocking stubbing
Spock을 사용한 조롱, 스터 빙 및 스파이 :
Spock 프레임 워크의 매개 변수화 된 테스트 이것에 자세히 설명되었습니다 Spock에 대한 교육 자습서 시리즈 .
모킹 및 스터 빙은 광범위한 단위 테스트의 가장 필수적인 구성 요소 중 하나입니다. 조롱과 빨기에 대한 지원은 프레임 워크를위한 케이크 위의 체리와 같습니다.
JUnit, JBehave 등과 같은 기존 프레임 워크의 경우 모의 및 스텁에 대한 지원이 기본적으로 제공되지 않으므로 개발자가 Mockito, PowerMock, EasyMock 등과 같은 타사 라이브러리를 사용해야합니다. 단위 테스트.
모의와 스텁과 그 사용 사례를 이해하려면 다음 시리즈를 살펴보십시오. Mockito 튜토리얼 .
이 튜토리얼에서 우리는 Spock 라이브러리 자체에 통합 된 내장 된 Mocking 및 Stubbing 기능에 대해 자세히 알아볼 것입니다. 이는 차례로 더 쉬운 Groovy 구문을 사용할 수있게하여 다른 3 개를 추가 / 포함 할 필요성을 줄여줍니다.rd파티 도서관.
모든 유효한 Java 코드도 유효한 Groovy 코드이므로 항상 다른 Mocking 프레임 워크를 테스트에 포함 할 수 있습니다.
학습 내용 :
테스트중인 애플리케이션
먼저 Spock 프레임 워크에서 모의 및 스텁을 사용하여 테스트 할 샘플 Java 애플리케이션을 정의 해 보겠습니다.
우리는 주어진 학생 ID에 대해 추상 된 데이터베이스에서 총 점수를 가져오고 총 점수 값에 따라 간단한 성적 할당 논리를 갖는 StudentGradeCalculator 앱을 개발할 것입니다. 학생 점수와 성적을 가져오고 업데이트하는 방법이 거의없는 데이터베이스 인터페이스를 사용합니다.
애플리케이션의 코드는이 튜토리얼의 마지막 섹션에서 사용할 수 있습니다.
스팍에서 조롱
비디오 자습서
이 섹션에서는 Spock 프레임 워크에서 Mocks를 인스턴스화하고 초기화하는 방법과 모의 상호 작용의 유효성을 검사하는 방법을 살펴 봅니다. 즉, 테스트중인 메서드의 기대에 따라 모의 호출에 대한 유효성 검사가 발생했습니다.
Mocks를 사용하면 많은 설정을 할 필요가 없지만 테스트중인 응용 프로그램에 제공된 모의 개체와 함께 발생한 상호 작용을 확인할 수 있습니다.
mock을 사용하면 다음과 같은 작업을 수행 할 수 있습니다.
Eclipse에서 Java 프로젝트를 빌드하는 방법
- 모의는 어떤 주장으로 불렸습니까?
- 총 호출 횟수는 얼마입니까?
- 모의 순서 확인.
모의 된 데이터베이스 구현 객체를 제공하고 모의와의 상호 작용을 검증하는 StudentGradeCalculator의 간단한 예를 살펴 보겠습니다. 간단한 예제를 통해 조롱 기능을 이해해 보겠습니다.
모든 상호 작용 유효성 검사는 규칙에 따라 'then'블록에서 발생해야합니다.
아래는 테스트중인 메서드의 코드입니다. ( ' 언제: ' 블록)
public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; }
#1) 정확한 인수로 상호 작용 검증 : 먼저 정확히 예상되는 인수로 상호 작용을 검증 해 보겠습니다. 여기서 우리는 모의 메서드가 정확한 인수를 사용하여 호출 될 것으로 예상합니다 (메서드 실행 흐름에 따라).
여기 ' studentDatabase ”는 상호 작용을 검증하는 데이터베이스 인터페이스의 Mock입니다.
def 'illustrate mocks for interaction verification with arguments'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade('123','C') 1*studentDatabase.getStudentGrade('123') }
위에 표시된대로 정확한 인수로 유효성을 검사하므로 모의 구현이 호출되어야합니다. 이러한 인수를 변경하면 테스트가 실패하고 오류 로그에 적절한 이유가 표시됩니다.
'에서 성적을 변경해 보겠습니다. updateStudentGrade 'C'대신 'A'로 바꾸고 테스트가 실행될 때 어떤 오류가 발생하는지 확인합니다.
Too few invocations for: 1*studentDatabase.updateStudentGrade('123','A') (0 invocations) Unmatched invocations (ordered by similarity): 1 * studentDatabase.updateStudentGrade('123', 'C') 1 * studentDatabase.getStudentScores('123')
제공된 인수로 Mock 호출을 찾을 수 없으므로 'Too few invocations'와 같은 오류가 표시됩니다.
xbox one s 용 vr 헤드셋
#두) 이제 실제 인수 값을 제공하지 않고 Mock 상호 작용을 검증하는 방법을 살펴 보겠습니다. 즉, 우리가 관심을 갖는 것은 mock이 메서드에서 호출되었지만 어떤 인수로 호출되지 않았는지 아는 것입니다.
이러한 유형의 요구 사항은 실제 프로덕션 코드에 대한 단위 테스트를 작성할 때 가장 일반적입니다. 기본적으로 테스트중인 애플리케이션의 핵심 비즈니스 논리에 의존하는 실제 인수를 식별하는 것이 항상 쉬운 것은 아니기 때문입니다.
구문은 간단합니다. 실제 값을 알 수없는 인수에 밑줄 '_'만 사용하면됩니다.
예를 들어, 문자열 값을 확인하려면 다음을 언급하면됩니다. “_ as String ”를 테스트에서 인수 대신 사용하고 모든 문자열 값에 대해 전달해야합니다 (다른 기본 형식 및 사용자 지정 데이터 유형에 대해서도 유사 함).
예를 들어 이해합시다
def 'illustrate mocks for interaction verification with generic matchers'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) 1*studentDatabase.getStudentGrade('123') }
여기서 주목해야 할 중요한 점은 알려진 인수와 알려지지 않은 인수에 대해 항상 혼합 및 일치를 수행 할 수 있다는 것입니다. 예를 들어, 아래 예제에서 우리는 하나의 모의가 실제 인수와 상호 작용하고 다른 하나는 느슨한 매처와의 상호 작용을 검증하고 있습니다.
#삼) 마지막으로, 모의 호출 순서, 즉 테스트가 실행될 때 모의가 호출 된 순서를 확인할 수있는 시나리오를 살펴 보겠습니다.
테스트중인 애플리케이션에 여러 공동 작업자 / 모의가 관련된 경우 이벤트의 흐름을 확인하는 것이 때로는 필수적이며 메서드가 미리 결정된 순서로 호출되었는지 이해하고 확인하는 것이 유용합니다.
def 'illustrate mocks for validating order'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.getStudentGrade('123') then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) }
이는 모의 시퀀스 예상 순서대로 여러 개의 'then :'블록을 사용하여 달성 할 수 있습니다. 언급 된 시퀀스가 실제 호출 순서를 충족하지 않으면 '잘못된 호출 순서'를 자세히 설명하는 오류가 발생합니다.
예를 들어 위의 순서를 변경하면 그때 다음과 같이 테스트 실행시 오류가 발생합니다.
Wrong invocation order for: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) (1 invocation) Last invocation: studentDatabase.updateStudentGrade('123', 'C')
스팍에서 스터 빙
비디오 자습서
모킹에 대한 모든 것을 살펴 보았으니 이제 모의 오브젝트에 스텁을 정의하는 방법을 살펴 보겠습니다. 스터 빙은 테스트중인 애플리케이션의 다양한 흐름 / 시나리오를 테스트하기 위해 모의 호출에 미리 정의되거나 미리 준비된 응답을 설정하는 것입니다.
호출 될 때 미리 정의 된 값을 반환하도록 모의 객체를 프로그래밍하는 것으로 생각하십시오. 동일한 StudentGradeCalculator 앱을 계속 사용하고 데이터베이스 인터페이스 호출을 스텁하여 다른 시나리오를 테스트합니다.
Stub은 실제 객체의 동작을 모방하는 Mock과 같습니다. 단순히 프로그래밍 된 Mock이라고 부를 수 있습니다.
스터 빙 구문
스터 빙 구문은 오른쪽 시프트 연산자 2 개입니다. >> '
호출에 스텁을 설정하려면 다음과 같이 정의 할 수 있습니다.
StubbedObject.StubbedMethod(//argumentList) >> “Stubbed Response”
이제 예제를 통해 다양한 스터 빙 시나리오를 이해하겠습니다.
#1) 실제 매개 변수로 스터 빙 : 인수가 미리 알려져 있거나 호출이 지정된 인수와 함께있을 때만 스텁을 설정하려는 경우이 스텁 지정 방법을 사용할 수 있습니다.
def 'illustrate stubs with exact matchers'() { given: studentDatabase.getStudentScores('123') >> (20F, 30F, 50F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' }
여기에서 스텁이 정확한 인수, 즉이 경우 StudentId가 '123'으로 설정되었음을 알 수 있습니다 (다른 값의 경우 스텁이 호출되지 않고 기본 응답이 반환 됨).
# 2) 관대 한 매 처로 스터 빙 : 인수가 알려지지 않았거나 중요하지 않은 경우 모의에 대해했던 것처럼 느슨하게 언급 할 수 있으며 구문은 동일하게 유지됩니다 (예 : 밑줄 '_').
def 'illustrate stubs with loose matchers'() { given: studentDatabase.getStudentScores(_ as String) >> (20F, 30F, 10F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' }
#삼) 예외를 발생시키기 위해 스텁을 설정하는 또 다른 간단한 예를 살펴 보겠습니다.
이러한 시나리오는 테스트중인 응용 프로그램의 오류 처리 논리를 검증하는 데 매우 유용합니다 (실제로 모든 예외를 생성하는 것은 불가능하지만 간단한 스텁을 설정하여 원하는 예외를 반환 한 다음이를 주장 할 수 있습니다.) then 블록).
jar 파일을 여는 데 사용할 사항
def 'illustrate stubs with exceptions thrown'() { given: studentDatabase.getStudentScores(_ as String) >> {throw new RuntimeException()} when: studentReportGenerator.calculateStudentGrade('123') then: thrown(RuntimeException.class) }
Spock에서 감시
스파이는 실제 물건을 기반으로합니다 즉, 추상 인터페이스 자체가 아니라 인터페이스 구현이 필요합니다. 스파이는 강력하며 테스트중인 응용 프로그램에 대해 호출 된 실제 메서드를 가져오고 메서드가 호출 된 인수를 확인할 수 있습니다.
스파이는 또한 스파이 대상 개체 인스턴스에 대한 부분 모의 정의를 허용합니다. 즉, 객체에 대한 일부 메서드의 동작을 정의하려는 경우 나머지는 실제 메서드 호출로 호출되도록 허용 할 수 있습니다.
이들은 일반적으로 구현되지 않은 인터페이스 메소드가 있고 완전히 작동하는 다른 메소드가 거의없는 상황에서 유용합니다. 따라서 개발자는 구현되지 않은 항목을 스텁하고 기능적 메서드의 실제 구현을 호출하도록 선택할 수 있습니다.
Spied 객체의 경우 스텁이 정의되지 않은 경우 기본 동작은 실제 구현을 호출하는 것입니다. 그러나 스파이를 자주 호출해서는 안되며 모의와 스텁 및 이들의 조합을 사용하여 모든 시나리오 범위를 달성 할 수 있습니다.
동일한 예제를 사용하여 Spock 프레임 워크에서 Spies를 사용하는 몇 가지 예제를 살펴 보겠습니다. StudentGradeCalculator (우리는 실제 구현을 만들었습니다. StudentDatabase 사용하는 메모리 내 구현입니다. HashMap 실제 메서드 호출 및 데이터 반환을 설명합니다. 이 코드는 튜토리얼의 마지막 섹션에서 사용할 수 있습니다.)
# 1) 스텁과 실제 메서드 호출의 조합을 사용한 스파이
def 'illustrate spies'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' 1*spiedStudentDatabase.getStudentGrade(_ as String) >> 'A' }
위의 예는 Spock 프레임 워크를 사용하여 Spy를 생성하는 구문을 보여줍니다. 스텁은 선언 시간 자체에 정의됩니다.
또한 감시 된 호출은 then 블록에 설명 된대로 확인할 수 있습니다 (특정 인수에 대해 정의 할 수있는 느슨한 인수 매처 포함).
# 2) 모든 실제 메서드 호출을 사용한 스파이
def 'illustrate spies with real method call'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' 1*spiedStudentDatabase.getStudentGrade('123') }
위의 예에서는 스텁 동작을 언급하지 않았으므로 모든 호출이 실제 구현으로 이동합니다.
결론
이 튜토리얼에서는 Spock 프레임 워크를 사용하여 Mock Stub 및 Spy에 내장 된 기술에 대해 모두 배웠습니다. Spock은 이러한 기능을 프레임 워크 자체의 일부로보다 읽기 쉬운 그루비 구문과 더 적은 상용구 코드와 결합하여 쉽게 만들 수 있습니다.
Mocks, Stub 및 Spies는 범위를 늘리고 테스트중인 애플리케이션의 핵심 비즈니스 논리를 테스트하거나 검증하기 위해 단위 테스트에서 광범위하게 사용됩니다.
애플리케이션의 소스 코드
StudentReportGenerator.java – 테스트중인 메소드 / 애플리케이션입니다.
package app.studentScores; import java.util.List; public class StudentReportGenerator { public IStudentDatabase studentDatabase; public StudentReportGenerator(IStudentDatabase studentDatabase) { this.studentDatabase = studentDatabase; } public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; } }
IStudentDatabase.java – 데이터베이스 인터페이스
package app.studentScores; import java.util.List; public interface IStudentDatabase { List getStudentScores(String studentId); void updateStudentGrade(String studentId, String grade); String getStudentGrade(String studentId); }
StudentDatabase.java – IStudentDatabase.java 인터페이스의 InMemory 구현
package app.studentScores; import java.util.*; public class StudentDatabase implements IStudentDatabase { private Map scoreMap; private Map gradeMap; public StudentDatabase() { this.scoreMap = new HashMap(); this.gradeMap = new HashMap(); scoreMap.put('123', Arrays.asList(40F, 30F, 30F)); scoreMap.put('456', Arrays.asList(10F, 10F, 30F)); gradeMap.put('123', 'C'); gradeMap.put('456', 'A'); } @Override public List getStudentScores(String studentId) { return scoreMap.get(studentId); } @Override public void updateStudentGrade(String studentId, String grade) { gradeMap.put(studentId,grade); } @Override public String getStudentGrade(String studentId) { return gradeMap.get(studentId); } }
다음 튜토리얼에서는 Spock 프레임 워크를 다른 테스트 프레임 워크 및 기술과 통합하는 방법을 살펴볼 것입니다.