[java] Mockito : 메소드 내에서 생성 된 객체에서 메소드를 확인하는 방법은 무엇입니까?

나는 Mockito를 처음 사용합니다.

아래 클래스가 주어지면 Mockito를 사용하여 someMethod호출 한 후 정확히 한 번 호출 되었는지 어떻게 확인할 수 foo있습니까?

public class Foo
{
    public void foo(){
        Bar bar = new Bar();
        bar.someMethod();
    }
}

다음과 같은 확인 전화를하고 싶습니다.

verify(bar, times(1)).someMethod();

bar의 조롱 된 인스턴스는 어디에 있습니까 Bar?



답변

의존성 주입

Bar 인스턴스 또는 Bar 인스턴스를 만드는 데 사용되는 팩토리 (또는 다른 483 가지 방법 중 하나)를 주입하면 테스트를 수행하는 데 필요한 액세스 권한이 있습니다.

공장 예 :

다음과 같이 작성된 Foo 클래스가 주어집니다.

public class Foo {
  private BarFactory barFactory;

  public Foo(BarFactory factory) {
    this.barFactory = factory;
  }

  public void foo() {
    Bar bar = this.barFactory.createBar();
    bar.someMethod();
  }
}

테스트 방법에서 다음과 같이 BarFactory를 주입 할 수 있습니다.

@Test
public void testDoFoo() {
  Bar bar = mock(Bar.class);
  BarFactory myFactory = new BarFactory() {
    public Bar createBar() { return bar;}
  };

  Foo foo = new Foo(myFactory);
  foo.foo();

  verify(bar, times(1)).someMethod();
}

보너스 : 이것은 TDD가 코드 디자인을 주도 할 수있는 방법의 예입니다.


답변

고전적인 반응은 “그렇지 않다”입니다. Foo내부 API가 아닌 의 공개 API를 테스트합니다 .

영향을받는 Foo개체 (또는 환경의 다른 개체)의 동작이 foo()있습니까? 그렇다면 테스트하십시오. 그렇지 않은 경우 방법은 무엇을합니까?


답변

DI 또는 공장을 사용하지 않으려는 경우. 약간 까다로운 방식으로 수업을 리팩토링 할 수 있습니다.

public class Foo {
    private Bar bar;

    public void foo(Bar bar){
        this.bar = (bar != null) ? bar : new Bar();
        bar.someMethod();
        this.bar = null;  // for simulating local scope
    }
}

그리고 당신의 시험 수업 :

@RunWith(MockitoJUnitRunner.class)
public class FooTest {
    @Mock Bar barMock;
    Foo foo;

    @Test
    public void testFoo() {
       foo = new Foo();
       foo.foo(barMock);
       verify(barMock, times(1)).someMethod();
    }
}

그런 다음 foo 메소드를 호출하는 클래스는 다음과 같이합니다.

public class thirdClass {

   public void someOtherMethod() {
      Foo myFoo = new Foo();
      myFoo.foo(null);
   }
}

이 방법으로 메소드를 호출 할 때 알 수 있듯이 foo 메소드를 호출하는 다른 클래스에서 Bar 클래스를 가져올 필요가 없습니다.

물론 단점은 호출자가 막대 객체를 설정할 수 있다는 것입니다.

도움이 되길 바랍니다.


답변

예제 코드를 사용하는 솔루션 PowerMockito.whenNew

  • mockito-all 1.10.8
  • 파워 코어 1.6.1
  • powermock- 모듈 -junit4 1.6.1
  • powermock-api-mockito 1.6.1
  • junit 4.12

FooTest.java

package foo;

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

//Both @PrepareForTest and @RunWith are needed for `whenNew` to work 
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Foo.class })
public class FooTest {

    // Class Under Test
    Foo cut;

    @Mock
    Bar barMock;

    @Before
    public void setUp() throws Exception {
        cut = new Foo();

    }

    @After
    public void tearDown() {
        cut = null;

    }

    @Test
    public void testFoo() throws Exception {

        // Setup
        PowerMockito.whenNew(Bar.class).withNoArguments()
                .thenReturn(this.barMock);

        // Test
        cut.foo();

        // Validations
        Mockito.verify(this.barMock, Mockito.times(1)).someMethod();

    }

}

JUnit 출력
JUnit 출력


답변

나는 Mockito 생각 @InjectMocks 가 갈 길이 합니다.

당신의 의도에 따라 다음을 사용할 수 있습니다.

  1. 생성자 주입
  2. 속성 세터 주입
  3. 필드 주입

문서에 대한 추가 정보

아래는 필드 주입의 예입니다.

클래스:

public class Foo
{
    private Bar bar = new Bar();

    public void foo()
    {
        bar.someMethod();
    }
}

public class Bar
{
    public void someMethod()
    {
         //something
    }
}

테스트:

@RunWith(MockitoJUnitRunner.class)
public class FooTest
{
    @Mock
    Bar bar;

    @InjectMocks
    Foo foo;

    @Test
    public void FooTest()
    {
        doNothing().when( bar ).someMethod();
        foo.foo();
        verify(bar, times(1)).someMethod();
    }
}


답변

예, 정말로 원하거나해야 할 경우 PowerMock을 사용할 수 있습니다. 이것은 최후의 수단으로 간주되어야합니다. PowerMock을 사용하면 호출에서 생성자로 모의를 반환 할 수 있습니다. 그런 다음 모의에서 확인하십시오. 즉, csturtz는 “올바른”답변입니다.

새로운 객체의 모의 구성에 대한 링크는 다음과 같습니다.


답변

또 다른 간단한 방법은 bar.someMethod ()에 로그 문을 추가 한 다음 테스트가 실행될 때 해당 메시지를 볼 수 있는지 확인하십시오. 여기의 예제를 참조하십시오 : 로거의 메시지에 JUnit assert를 수행하는 방법

Bar.someMethod ()가 인 경우 특히 유용합니다 private.