매개 변수로 생성자 모의


89

다음과 같은 수업이 있습니다.

public class A {
    public A(String test) {
        bla bla bla
    }

    public String check() {
        bla bla bla
    }
}

생성자의 논리 A(String test)check()내가 조롱하려는 것입니다. 다음과 같은 호출을 원합니다. new A($$$any string$$$).check()returns a dummy string "test".

나는 시도했다 :

 A a = mock(A.class); 
 when(a.check()).thenReturn("test");

 String test = a.check(); // to this point, everything works. test shows as "tests"

 whenNew(A.class).withArguments(Matchers.anyString()).thenReturn(rk);
 // also tried:
 //whenNew(A.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(rk);

 new A("random string").check();  // this doesn't work

그러나 작동하지 않는 것 같습니다. new A($$$any string$$$).check()의 모의 객체를 가져 오는 대신 생성자 논리를 계속 진행합니다 A.


모의 check () 메서드가 제대로 작동합니까?
Ben Glasser 2011

@BenGlasser check ()는 정상적으로 작동합니다. whenNew가 전혀 작동하지 않는 것 같습니다. 설명도 업데이트했습니다.
Shengjie

답변:


93

게시 한 코드는 최신 버전의 Mockito 및 Powermockito에서 작동합니다. A를 준비하지 않았나? 이 시도:

A.java

public class A {
     private final String test;

    public A(String test) {
        this.test = test;
    }

    public String check() {
        return "checked " + this.test;
    }
}

MockA.java

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class MockA {
    @Test
    public void test_not_mocked() throws Throwable {
        assertThat(new A("random string").check(), equalTo("checked random string"));
    }
    @Test
    public void test_mocked() throws Throwable {
         A a = mock(A.class); 
         when(a.check()).thenReturn("test");
         PowerMockito.whenNew(A.class).withArguments(Mockito.anyString()).thenReturn(a);
         assertThat(new A("random string").check(), equalTo("test"));
    }
}

두 테스트 모두 mockito 1.9.0, powermockito 1.4.12 및 junit 4.8.2로 통과해야합니다.


24
또한 생성자가 다른 클래스에서 호출 된 경우 목록에 포함하십시오PrepareForTest
Jeff E

누구나 "PowerMockito.whenNew"가 호출 될 때 왜 우리가 자신을 준비해야 하는지를 알고 있습니까?
udayanga

50

내가 아는 한, mockito로 생성자를 모의 할 수는 없으며 메서드 만 사용할 수 있습니다. 그러나 Mockito Google 코드 페이지의 위키에 따르면 해당 클래스의 새 인스턴스를 반환하는 메서드를 클래스에 생성하여 생성자 동작을 모의하는 방법이 있습니다. 그런 다음 그 방법을 조롱 할 수 있습니다. 아래는 Mockito 위키에서 직접 발췌 한 것입니다 .

패턴 1-객체 생성에 한 줄 방법 ​​사용

패턴 1 (MyClass라는 클래스 테스트)을 사용하려면 다음과 같은 호출을 대체합니다.

   Foo foo = new Foo( a, b, c );

   Foo foo = makeFoo( a, b, c );

한 줄 방법을 작성하십시오.

   Foo makeFoo( A a, B b, C c ) { 
        return new Foo( a, b, c );
   }

메서드에 논리를 포함하지 않는 것이 중요합니다. 객체를 생성하는 단 한 줄입니다. 그 이유는 메서드 자체가 단위 테스트를 거치지 않기 때문입니다.

클래스를 테스트하러 올 때 테스트하는 객체는 실제로 Mockito 스파이가되며이 메서드는 재정의되어 모의를 반환합니다. 따라서 테스트중인 것은 클래스 자체가 아니라 약간 수정 된 버전입니다.

테스트 클래스에는 다음과 같은 멤버가 포함될 수 있습니다.

  @Mock private Foo mockFoo;
  private MyClass toTest = spy(new MyClass());

마지막으로 테스트 메서드 내부에서 다음과 같은 줄로 makeFoo 호출을 조롱합니다.

  doReturn( mockFoo )
      .when( toTest )
      .makeFoo( any( A.class ), any( B.class ), any( C.class ));

생성자에 전달되는 인수를 확인하려면 any ()보다 더 구체적인 매처를 사용할 수 있습니다.

클래스의 모의 객체를 반환하고 싶다면 이것이 효과가 있다고 생각합니다. 어떤 경우 든 여기에서 모의 ​​객체 생성에 대해 자세히 읽을 수 있습니다.

http://code.google.com/p/mockito/wiki/MockingObjectCreation


21
+1, 소스 코드를 좀 더 모키 토 친화적으로 조정해야한다는 사실이 마음에 들지 않습니다. 공유해 주셔서 감사합니다.
Shengjie

22
더 테스트 가능한 소스 코드를 가지고 있거나 코드를 작성할 때 테스트 가능성 안티 패턴을 피하는 것은 결코 나쁘지 않습니다. 더 테스트 가능한 소스를 작성하면 자동으로 유지 관리가 더 쉬워집니다. 생성자 호출을 자체 메서드에서 분리하는 것은이를 달성하는 한 가지 방법 일뿐입니다.
Dawood ibn Kareem

1
테스트 가능한 코드를 작성하는 것이 좋습니다. A에 의존하는 클래스 B에 대한 테스트를 작성할 수 있도록 클래스 A를 재 설계해야하는데, A에 의존하는 A는 C에 하드 코딩 된 종속성이 있기 때문에 기분이 좋지 않습니다. 예, 코드는 결국 더 나아질 것이지만 테스트 하나를 끝낼 수 있도록 몇 개의 클래스를 다시 디자인하게 될까요?
Mark Wood

내 경험의 @MarkWood 투박한 테스트 경험은 일반적으로 일부 디자인 결함의 신호입니다. 생성자를 테스트하는 경우 IRL 코드는 아마도 팩토리 또는 일부 종속성 주입에 대해 비명을 지르고있을 것입니다. 이 두 가지 경우에 대한 일반적인 디자인 패턴을 따르면 코드를 테스트하고 일반적으로 작업하기가 훨씬 쉬워집니다. 거기에 논리가 많아서 생성자를 테스트하는 경우 다형성 계층이 필요하거나 해당 논리를 초기화 메서드로 이동할 수 있습니다.
Ben Glasser

12

Powermock을 사용하지 않고 .... Ben Glasser 답변을 기반으로 한 아래 예제를 참조하십시오. 시간을 절약하는 데 시간이 좀 걸렸기 때문에 ...

원래 수업 :

public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(new BClazz(cClazzObj, 10));
    } 
}

수정 된 클래스 :

@Slf4j
public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(getBObject(cClazzObj, 10));
    }

    protected BClazz getBObject(CClazz cClazzObj, int i) {
        return new BClazz(cClazzObj, 10);
    }
 }

테스트 클래스

public class AClazzTest {

    @InjectMocks
    @Spy
    private AClazz aClazzObj;

    @Mock
    private CClazz cClazzObj;

    @Mock
    private BClazz bClassObj;

    @Before
    public void setUp() throws Exception {
        Mockito.doReturn(bClassObj)
               .when(aClazzObj)
               .getBObject(Mockito.eq(cClazzObj), Mockito.anyInt());
    }

    @Test
    public void testConfigStrategy() {
        aClazzObj.updateObject(cClazzObj);

        Mockito.verify(cClazzObj, Mockito.times(1)).setBundler(bClassObj);
    }
}

6

mockito를 사용하면 withSettings ()를 사용할 수 있습니다. 예를 들어 CounterService에 2 개의 종속성이 필요한 경우이를 mock으로 전달할 수 있습니다.

UserService userService = Mockito.mock(UserService.class); SearchService searchService = Mockito.mock(SearchService.class); CounterService counterService = Mockito.mock(CounterService.class, withSettings().useConstructor(userService, searchService));


제 생각에는 가장 쉽고 가장 좋은 대답입니다. 감사합니다.
Eldon

4

Mockito에는 최종, 정적 및 비공개 메서드를 테스트하는 데 제한이 있습니다.

jMockit 테스트 라이브러리를 사용하면 아래와 같이 매우 쉽고 간단하게 몇 가지 작업을 수행 할 수 있습니다.

java.io.File 클래스의 모의 생성자 :

new MockUp<File>(){
    @Mock
    public void $init(String pathname){
        System.out.println(pathname);
        // or do whatever you want
    }
};
  • 공개 생성자 이름은 $ init로 바꿔야합니다.
  • 던져진 인수와 예외는 동일하게 유지됩니다.
  • 반환 유형은 void로 정의되어야합니다.

정적 메서드 모의 :

  • 메서드 모의 서명에서 정적 제거
  • 메소드 서명은 그렇지 않으면 동일하게 유지됩니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.