mocking private static
예제를 통해 Mockito에서 Mocking Private, Static 및 Void 메서드를 알아보십시오.
이 실습 시리즈에서 Mockito에 대한 튜토리얼 , 우리는 다양한 유형의 Mockito Matchers 지난 튜토리얼에서.
일반적으로 비정상적인 조롱의 범주에 속합니다.
개인 및 정적 메서드 / 클래스를 모의해야하는 경우 리팩토링 된 코드가 잘못되어 실제로 테스트 가능한 코드가 아니며 단위 테스트에 익숙하지 않은 일부 레거시 코드 일 가능성이 높습니다.
그럼에도 불구하고 PowerMockito와 같은 소수의 단위 테스트 프레임 워크 (Mockito가 직접적으로는 아님)에 의해 개인 및 정적 메서드 모킹에 대한 지원이 여전히 존재합니다.
'void'메서드를 조롱하는 것은 데이터베이스 행 업데이트와 같이 본질적으로 아무것도 반환하지 않는 메서드가있을 수 있으므로 일반적입니다 (입력을 받아들이고 출력을 반환하지 않는 Rest API 끝점의 PUT 작업으로 간주).
Mockito는 모의 void 메서드를 완벽하게 지원합니다.
웹 사이트에 코드를 삽입하는 방법
학습 내용 :
Powermock – 간략한 소개
Mockito의 경우 개인 및 정적 메서드를 모의하기위한 직접적인 지원이 없습니다. 개인 메서드를 테스트하려면 다음을 수행해야합니다. 코드 리팩터링 보호 된 (또는 패키지)에 대한 액세스를 변경하려면 정적 / 최종 방법을 피해야합니다.
Mockito는 이러한 종류의 코드 구조를 사용하는 것이 코드 냄새와 잘못 설계된 코드이기 때문에 의도적으로 이러한 종류의 모의에 대한 지원을 제공하지 않습니다.
그러나 개인 및 정적 메서드에 대한 모의를 지원하는 프레임 워크가 있습니다.
Powermock EasyMock 및 Mockito와 같은 다른 프레임 워크의 기능을 확장하고 정적 및 개인 메서드를 모의하는 기능을 제공합니다.
# 1) 방법 : Powermock은 개인 및 정적 메서드 모의, 최종 클래스, 생성자 등을 지원하기 위해 사용자 지정 바이트 코드 조작의 도움으로이를 수행합니다.
# 2) 지원되는 패키지 : Powermock은 2 개의 확장 API를 제공합니다. 하나는 Mockito 용이고 다른 하나는 easyMock 용입니다. 이 기사에서는 power mock 용 Mockito 확장을 사용하여 예제를 작성합니다.
# 3) 구문 :Powermockito는 정적 및 개인 메서드를 모의하는 몇 가지 추가 메서드를 제외하고 Mockito와 거의 유사한 구문을 가지고 있습니다.
# 4) Powermockito 설정
Gradle 기반 프로젝트에 Mockito 라이브러리를 포함하기 위해 포함 할 라이브러리는 다음과 같습니다.
testCompile group: 'org.powermock', name: 'powermock-api-mockito2', version: '1.7.4' testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '1.7.4'
Maven에서도 유사한 종속성을 사용할 수 있습니다.
Powermock-api-mockito2 – 라이브러리는 Powermockito 용 Mockito 확장을 포함해야합니다.
Powermock 모듈 -junit4 – 모듈은 PowerMockRunner (PowerMockito로 테스트를 실행하는 데 사용되는 사용자 지정 러너)를 포함해야합니다.
여기서 주목해야 할 중요한 점은 PowerMock이 Junit5 테스트 러너를 지원하지 않는다는 것입니다. 따라서 테스트는 Junit4에 대해 작성되어야하며 테스트는 PowerMockRunner로 실행되어야합니다.
PowerMockRunner를 사용하려면 테스트 클래스에 주석을 추가해야합니다. @RunWith (PowerMockRunner.class)
이제 private, static 및 void 메서드를 자세히 모의하여 논의하겠습니다!
개인 메서드 조롱
테스트중인 메서드에서 내부적으로 호출되는 개인 메서드 모의는 특정 시간에 피할 수 없습니다. powermockito를 사용하면 이것이 가능하며 검증은 'verifyPrivate'라는 새로운 방법을 사용하여 수행됩니다.
해 보자 an예 여기서 테스트중인 메서드는 개인 메서드 (부울을 반환)를 호출합니다. 테스트에 따라 참 / 거짓을 반환하도록이 메서드를 스텁하려면이 클래스에 스텁을 설정해야합니다.
이 예제에서 테스트중인 클래스는 인터페이스 호출과 개인 메서드 호출에 대한 조롱을 사용하여 스파이 인스턴스로 생성됩니다.
Mock Private Method의 중요 사항 :
#1) 테스트 메서드 또는 테스트 클래스는 @로 주석을 달아야합니다. PrepareForTest (ClassUnderTest). 이 주석은 테스트를 위해 특정 클래스를 준비하도록 powerMockito에 지시합니다.
이것들은 대부분 조작 된 바이트 코드 . 일반적으로 최종 클래스의 경우 테스트 중에 모의해야하는 개인 및 / 또는 정적 메서드를 포함하는 클래스입니다.
예:
@PrepareForTest(PriceCalculator.class)
#두) 개인 메서드에 스텁을 설정하려면
통사론 - when (mock 또는 spy 인스턴스,“privateMethodName”). thenReturn (// return value)
예:
when (priceCalculatorSpy, 'isCustomerAnonymous').thenReturn(false);
#삼) stubbed private 메서드를 확인합니다.
통사론 – verifyPrivate (mockedInstance) .invoke ( 'privateMethodName')
예:
verifyPrivate (priceCalculator).invoke('isCustomerAnonymous');
전체 테스트 샘플 : priceCalculator에는 itemService, userService 등과 같은 일부 모의 종속성이있는 이전 기사의 동일한 예제가 계속됩니다.
같은 클래스 내에서 private 메서드를 호출하고 고객이 익명인지 여부를 반환하는 computePriceWithPrivateMethod라는 새 메서드를 만들었습니다.
@Test @PrepareForTest(PriceCalculator.class) public void calculatePriceForAnonymous_witStubbedPrivateMethod_returnsCorrectPrice() throws Exception { // Arrange ItemSku item1 = new ItemSku(); item1.setApplicableDiscount(5.00); item1.setPrice(100.00); double expectedPrice = 90.00; // Setting up stubbed responses using mocks when(priceCalculatorSpy, 'isCustomerAnonymous').thenReturn(false); when(mockedItemService.getItemDetails(123)).thenReturn(item1); // Act double actualDiscountedPrice = priceCalculatorSpy.calculatePriceWithPrivateMethod(123); // Assert verifyPrivate(priceCalculator).invoke('isCustomerAnonymous'); assertEquals(expectedPrice, actualDiscountedPrice); }
정적 메서드 모의
정적 메서드는 개인 메서드에서 본 것과 유사한 방식으로 조롱 할 수 있습니다.
테스트중인 메서드가 동일한 클래스 (또는 다른 클래스)의 정적 메서드를 사용하는 경우 테스트 전에 (또는 테스트 클래스에) prepareForTest 주석에 해당 클래스를 포함해야합니다.
모의 정적 메서드에 대한 중요 사항 :
#1) 테스트 메서드 또는 테스트 클래스는 @로 주석을 달아야합니다. PrepareForTest (ClassUnderTest). 개인 메서드 / 클래스를 조롱하는 것과 유사하게 정적 클래스에도 필요합니다.
#두) 정적 메서드에 필요한 추가 단계는 다음과 같습니다. mockStatic (// 정적 클래스 이름)
예:
mockStatic(DiscountCategoryFinder.class)
#삼) 정적 메서드에 스텁을 설정하는 것은 다른 인터페이스 / 클래스 모의 인스턴스에있는 메서드를 스텁하는 것만 큼 좋습니다.
예를 들면 : DiscountCategoryFinder 클래스의 getDiscountCategory () (PREMIUM 및 GENERAL 값이있는 열거 형 DiscountCategory를 반환) 정적 메서드를 스텁하려면 다음과 같이 간단히 스텁합니다.
when (DiscountCategoryFinder. getDiscountCategory ()).thenReturn(DiscountCategory. PREMIUM );
# 4) final / static 메서드에서 mock 설정을 확인하려면 verifyStatic () 메서드를 사용할 수 있습니다.
예:
verifyStatic (DiscountCategoryFinder.class, times (1));
모킹 무효 방법
먼저 스터 빙 void 메서드와 관련된 사용 사례의 종류를 이해해 보겠습니다.
#1) 예를 들어 메서드 호출-프로세스 중에 이메일 알림을 보냅니다.
예를 들어 :인터넷 뱅킹 계정의 비밀번호를 변경했다고 가정하면 변경이 성공하면 이메일로 알림을 받게됩니다.
/ changePassword는 고객에게 이메일 알림을 보내는 void 메서드 호출을 포함하는 Bank API에 대한 POST 호출로 생각할 수 있습니다.
#두) void 메서드 호출의 또 다른 일반적인 예는 일부 입력을 받고 아무것도 반환하지 않는 DB에 대한 업데이트 된 요청입니다.
스터 빙 무효 메서드 (즉, 아무것도 반환하지 않거나 예외를 throw하는 메서드)는 다음을 사용하여 처리 할 수 있습니다. doNothing (), doThrow () 및 doAnswer (), doCallRealMethod () 함수 . 테스트 기대치에 따라 위의 방법을 사용하여 스텁을 설정해야합니다.
또한 모든 void 메서드 호출은 기본적으로 doNothing ()으로 조롱됩니다. 따라서 명시 적 모의 설정이 수행되지 않은 경우에도 빈 메서드 호출시 기본 동작은 여전히 doNothing ()입니다.
이 모든 기능의 예를 살펴 보겠습니다.
모든 예에서 클래스가 있다고 가정 해 보겠습니다. StudentScoreUpdates 방법이있는 calculateSumAndStore (). 이 메서드는 점수 합계 (입력)를 계산하고 빈 방법 updateScores () databaseImplementation 인스턴스에서.
public class StudentScoreUpdates { public IDatabase databaseImpl; public StudentScoreUpdates(IDatabase databaseImpl) { this.databaseImpl = databaseImpl; } public void calculateSumAndStore(String studentId, int[] scores) { int total = 0; for(int score : scores) { total = total + score; } // write total to DB databaseImpl.updateScores(studentId, total); } }
아래 예제를 사용하여 모의 메서드 호출에 대한 단위 테스트를 작성합니다.
# 1) doNothing () – doNothing ()은 Mockito에서 void 메서드 호출에 대한 기본 동작입니다. 즉, void 메서드에 대한 호출을 확인하더라도 (void를 doNothing ()에 명시 적으로 설정하지 않아도 확인이 성공합니다)
public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabase); int[] scores = {60,70,90}; Mockito.doNothing().when(mockDatabase).updateScores(anyString(), anyInt()); // Act studentScores.calculateSumAndStore('student1', scores); // Assert Mockito.verify(mockDatabase, Mockito.times(1)).updateScores(anyString(), anyInt()); }
doNothing ()과 함께 다른 사용법
가중 그래프 인접 목록 C ++
에) void 메서드가 여러 번 호출되고 다른 호출에 대해 다른 응답을 설정하려는 경우-첫 번째 호출에는 doNothing ()을 사용하고 다음 호출에서는 예외를 throw합니다.
예를 들어 :다음과 같이 mock을 설정하십시오.
Mockito. doNothing ().doThrow(new RuntimeException()).when(mockDatabase).updateScores( anyString (), anyInt ());
비) void 메서드가 호출 된 인수를 캡처하려면 Mockito의 ArgumentCaptor 기능을 사용해야합니다. 이것은 메소드가 호출 된 인수의 추가 검증을 제공합니다.
ArgumentCaptor를 사용한 예 :
public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabase); int[] scores = {60,70,90}; Mockito.doNothing().when(mockDatabase).updateScores(anyString(), anyInt()); ArgumentCaptor studentIdArgument = ArgumentCaptor.forClass(String.class); // Act studentScores.calculateSumAndStore('Student1', scores); // Assert Mockito.verify(mockDatabase, Mockito.times(1)).updateScores(studentIdArgument.capture(), anyInt()); assertEquals('Student1', studentIdArgument.getValue()); }
# 2) doThrow ()- 이는 테스트중인 메서드에서 void 메서드가 호출 될 때 단순히 예외를 throw하려는 경우에 유용합니다.
예를 들면 :
Mockito.doThrow(newRuntimeException()).when(mockDatabase).updateScores ( anyString (), anyInt ());
# 3) doAnswer ()- doAnswer ()는 사용자 정의 로직을 수행하는 인터페이스를 제공합니다.
예 : 전달 된 인수를 통해 일부 값을 수정하고, 특히 void 메서드에 대해 일반 스텁이 반환 할 수없는 사용자 지정 값 / 데이터를 반환합니다.
데모 목적으로 – 나는 updateScores () void 메서드를 스텁 처리하여 ' 대답() ”를 입력하고 메서드가 호출되어야 할 때 전달되어야하는 인수 중 하나의 값을 인쇄합니다.
코드 예 :
@Test public void calculateSumAndStore_withValidInput_shouldCalculateAndUpdateResultInDb() { // Arrange studentScores = new StudentScoreUpdates(mockDatabaseImpl); int[] scores = {60,70,90}; Mockito.doCallRealMethod().when(mockDatabaseImpl).updateScores(anyString(), anyInt()); doAnswer(invocation -> { Object[] args = invocation.getArguments(); Object mock = invocation.getMock(); System.out.println(args[0]); return mock; }).when(mockDatabaseImpl).updateScores(anyString(), anyInt()); // Act studentScores.calculateSumAndStore('Student1', scores); // Assert Mockito.verify(mockDatabaseImpl, Mockito.times(1)).updateScores(anyString(), anyInt()); }
# 4) doCallRealMethod ()- 부분 모의는 스텁과 유사합니다 (일부 메소드에 대해서는 실제 메소드를 호출하고 나머지는 스텁 할 수 있음).
void 메서드의 경우 mockito는 mock을 설정하려고 할 때 사용할 수있는 doCallRealMethod ()라는 특수 함수를 제공합니다. 이것이 할 일은 실제 인수로 실제 void 메서드를 호출하는 것입니다.
예를 들면 :
Mockito. doCallRealMethod ().when(mockDatabaseImpl).updateScores( anyString (), anyInt ());
팁 & 트릭
# 1) 동일한 테스트 메서드 / 클래스에 여러 정적 클래스 포함– PowerMockito 사용 여러 Static of Final 클래스를 모의해야하는 경우 @의 클래스 이름 PrepareForTest 주석은 쉼표로 구분 된 값으로 배열로 언급 될 수 있습니다 (기본적으로 클래스 이름의 배열을 허용 함).
예:
@PrepareForTest({PriceCalculator.class, DiscountCategoryFinder.class})
위의 예에서 볼 수 있듯이 PriceCalculator와 DiscountCategoryFinder가 모두 모의 처리가 필요한 최종 클래스라고 가정합니다. 이 두 가지 모두 PrepareForTest 주석의 클래스 배열로 언급 될 수 있으며 테스트 메소드에서 스텁 될 수 있습니다.
# 2) PrepareForTest 속성 포지셔닝 – 이 속성의 위치는 Test 클래스에 포함 된 테스트의 종류와 관련하여 중요합니다.
모든 테스트가 동일한 최종 클래스를 사용해야하는 경우 테스트 클래스 수준에서이 속성을 언급하는 것이 합리적입니다. 즉, 준비된 클래스를 모든 테스트 메서드에서 사용할 수 있다는 의미입니다. 이와 반대로 테스트 방법에 주석이 언급되면 해당 특정 테스트에서만 사용할 수 있습니다.
결론
이 튜토리얼에서 우리는 모의 static, final 및 void 메서드에 대한 다양한 접근 방식을 논의했습니다.
많은 정적 또는 최종 방법을 사용하면 테스트 가능성을 방해하지만 일반적으로 사용되지 않는 레거시 코드의 경우에도 코드 / 애플리케이션에 대한 신뢰도를 높이기 위해 단위 테스트를 만드는 데 도움이되는 테스트 / 모킹에 대한 지원이 있습니다. 테스트 가능성을 위해 설계되어야합니다.
정적 및 최종 메서드의 경우 Mockito는 기본적으로 지원하지 않지만 PowerMockito (Mockito에서 많은 것을 상속)와 같은 라이브러리는 이러한 지원을 제공하고 이러한 기능을 지원하기 위해 실제로 바이트 코드 조작을 수행해야합니다.
Mockito out of the box는 stubbing void 메서드를 지원하고 doNothing, doAnswer, doThrow, doCallRealMethod 등과 같은 다양한 메서드를 제공하며 테스트 요구 사항에 따라 사용할 수 있습니다.
가장 자주 묻는 Mockito 인터뷰 질문은 다음 튜토리얼에서 요약됩니다.
추천 도서
- Mockito 자습서 : 단위 테스트에서 모의하기위한 Mockito 프레임 워크
- Mockito 인터뷰 상위 12 개 질문 (모킹 프레임 워크 인터뷰)
- C ++에서 정적
- 메소드 및 라이프 사이클이있는 Java 스레드
- 코드 예제를 사용하여 Mockito에서 모의와 스파이 만들기
- Mockito가 제공하는 다양한 유형의 매처
- 결함 방지 방법 및 기법
- 대량 테스트 실행을 위해 SoapUI에서 메서드를 사용하는 방법-SoapUI Tutorial # 10