programing

Mockito를 사용하여 추상 클래스 테스트

newsource 2022. 7. 28. 23:56

Mockito를 사용하여 추상 클래스 테스트

추상 수업을 시험해 보고 싶어요.네, 수업에서 물려받은 모의는 수동으로 쓸 수 있어요.

모크를 수작업으로 만드는 것이 아니라 모키토를 사용하여 모킹 프레임을 사용할 수 있을까요? 어떻게요?

다음 제안에서는 "실제" 하위 클래스를 만들지 않고 추상 클래스를 테스트합니다. 모크가 하위 클래스입니다.

Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS)다음으로 호출되는 추상 메서드를 모두 조롱합니다.

예:

public abstract class My {
  public Result methodUnderTest() { ... }
  protected abstract void methodIDontCareAbout();
}

public class MyTest {
    @Test
    public void shouldFailOnNullIdentifiers() {
        My my = Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS);
        Assert.assertSomething(my.methodUnderTest());
    }
}

주의: 이 솔루션의 장점은 추상적인 방법을 실행하지 않는 한 구현할 필요가 없다는 입니다.

제 솔직한 의견으로는 스파이를 사용하는 것보다 더 순진합니다. 스파이는 예를 필요로 하기 때문입니다. 즉, 추상적인 클래스의 즉석적인 서브클래스를 만들어야 합니다.

않고 몇해 보면 될 것 .CALLS_REAL_METHODS(Morten의 답변을 참조). 단, 테스트 대상인 구체적인 메서드가 추상화 또는 구현되지 않은 인터페이스 메서드를 호출하는 경우 이는 작동하지 않습니다.Mockito는 "Java 인터페이스에서 실제 메서드를 호출할 수 없습니다."라고 불평합니다.

(예, 디자인은 형편없지만 프레임워크가 몇 가지 있습니다.태피스트리 4는 당신에게 강요하는 것 같습니다.)

어프로치를 로 하는 행동 모든 것이무시당하거나 하는 것)을 사용하고, 「모든 것이 조롱당하거나 억제당하거나 하는 것」을 사용합니다.일반적인 모의 행동(즉, 모든 것이 조롱/방해를 받고 있다)을 사용하고 있습니다.doCallRealMethod()예컨대

public abstract class MyClass {
    @SomeDependencyInjectionOrSomething
    public abstract MyDependency getDependency();

    public void myMethod() {
        MyDependency dep = getDependency();
        dep.doSomething();
    }
}

public class MyClassTest {
    @Test
    public void myMethodDoesSomethingWithDependency() {
        MyDependency theDependency = mock(MyDependency.class);

        MyClass myInstance = mock(MyClass.class);

        // can't do this with CALLS_REAL_METHODS
        when(myInstance.getDependency()).thenReturn(theDependency);

        doCallRealMethod().when(myInstance).myMethod();
        myInstance.myMethod();

        verify(theDependency, times(1)).doSomething();
    }
}

추가하도록 업데이트됨:

회피할 수 없는 방법의 경우 대신 다음과 같은 방법을 사용해야 합니다.

when(myInstance.myNonVoidMethod(someArgument)).thenCallRealMethod();

그렇지 않으면 Mockito가 "미완료 스터빙이 감지되었습니다."라고 불평할 것입니다.

Spy를 사용하면 됩니다(Mockito 1.8+의 최신 버전을 사용).

public abstract class MyAbstract {
  public String concrete() {
    return abstractMethod();
  }
  public abstract String abstractMethod();
}

public class MyAbstractImpl extends MyAbstract {
  public String abstractMethod() {
    return null;
  }
}

// your test code below

MyAbstractImpl abstractImpl = spy(new MyAbstractImpl());
doReturn("Blah").when(abstractImpl).abstractMethod();
assertTrue("Blah".equals(abstractImpl.concrete()));

모의 프레임워크는 테스트 중인 클래스의 종속성을 쉽게 모의할 수 있도록 설계되었습니다.모킹 프레임워크를 사용하여 클래스를 모킹하면 대부분의 프레임워크는 동적으로 하위 클래스를 만들고 메서드 구현을 메서드가 호출된 시기를 감지하여 가짜 값을 반환하는 코드로 대체합니다.

추상 클래스를 테스트할 때는 SUT(Subject Under Test)의 비추상 메서드를 실행하려고 하므로 조롱 프레임워크는 원하는 것이 아닙니다.

혼란의 일부는 당신이 링크한 질문에 대한 답이 당신의 추상 수업에서 나온 모조품을 손으로 만들라고 말한 것입니다.나는 그런 수업을 조롱이라고 하지 않을 것이다.모크는 의존관계를 대체하기 위해 사용되는 클래스이며 기대치로 프로그래밍되어 있으며 이러한 기대치가 충족되는지 여부를 쿼리할 수 있습니다.

대신 테스트에서 추상 클래스의 비추상 서브클래스를 정의하는 것이 좋습니다.만약 너무 많은 코드로 이어진다면, 그것은 당신의 수업을 연장하기 어렵다는 신호일 수 있습니다.

대안 솔루션은 SUT를 만드는 추상적인 방법을 사용하여 테스트 케이스 자체를 추상적으로 만드는 것입니다(즉, 테스트 케이스는 템플릿 방법 설계 패턴을 사용합니다).

사용자 지정 답변을 사용해 보십시오.

예를 들어 다음과 같습니다.

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class CustomAnswer implements Answer<Object> {

    public Object answer(InvocationOnMock invocation) throws Throwable {

        Answer<Object> answer = null;

        if (isAbstract(invocation.getMethod().getModifiers())) {

            answer = Mockito.RETURNS_DEFAULTS;

        } else {

            answer = Mockito.CALLS_REAL_METHODS;
        }

        return answer.answer(invocation);
    }
}

추상적인 방법에 대한 모크를 반환하고 구체적인 방법에 대한 실제 방법을 호출합니다.

컨스트럭터도 디폴트 컨스트럭터도 디폴트 컨스트럭터도 디폴트 컨스트럭터, 디폴트 컨스트럭터, 디폴트 컨스트럭터, 디폴트 컨스트럭터, 디폴트 컨스트럭터, 디폴트 컨스트럭터, 디폴트 컨스트럭터, 디폴트 컨스트럭터 없다는 사실이다.YourAbstractClass()불리다super()도, 에서는, 를 들면, Mockito을 디폴트로 은 없는 것 같습니다.List 속성( 「」)ArrayList ★★★★★★★★★★★★★★★★★」LinkedList를 참조해 주세요.

추상 클래스(기본적으로 클래스 소스 코드가 생성됨)는 목록 요소에 대한 종속성 세터 주입을 제공하지 않으며 목록 요소를 초기화하는 생성자(수동으로 추가하려고 시도)도 제공하지 않습니다.

기본 초기화를 사용하는 것은 클래스 속성뿐입니다.

private List<MyGenType> dep1 = new ArrayList<MyGenType>();
private List<MyGenType> dep2 = new ArrayList<MyGenType>();

따라서 실제 객체 구현(유닛 테스트 클래스의 내부 클래스 정의, 추상 메서드 우선)과 실제 객체 스파이(적절한 필드 초기화를 수행하는) 없이 추상 클래스를 조롱할 수 있는 방법은 없습니다.

PowerMock만이 더 많은 도움을 줄 수 있다는 것은 유감입니다.

는 모키토에 을 조롱할 수 .@Mock★★★★

public abstract class My {

    public abstract boolean myAbstractMethod();

    public void myNonAbstractMethod() {
        // ...
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyTest {

    @Mock(answer = Answers.CALLS_REAL_METHODS)
    private My my;

    @Test
    private void shouldPass() {
        BDDMockito.given(my.myAbstractMethod()).willReturn(true);
        my.myNonAbstractMethod();
        // ...
    }
}

단점은 생성자 매개 변수가 필요한 경우 사용할 수 없다는 것입니다.

테스트 클래스가 테스트 대상 클래스와 동일한 패키지(다른 소스 루트 아래)에 있다고 가정하면 다음과 같이 간단히 모크를 만들 수 있습니다.

YourClass yourObject = mock(YourClass.class);

테스트할 메서드를 다른 메서드와 마찬가지로 호출합니다.

Super method라고 불리는 구체적인 방법에 대한 기대와 함께 호출되는 각 방법에 대한 기대치를 제시해야 합니다.Mockito에서는 어떻게 할지 모르겠지만, Easy Mock에서는 가능하다고 생각합니다.

이것이 하는 모든 것은 구체적인 예를 만드는 것이다.YouClass각 추상적인 방법의 빈 구현을 제공하는 수고를 덜 수 있습니다.

한편, 저는 종종 테스트에서 추상 클래스를 구현하는 것이 유용하다고 생각합니다.이러한 클래스는 추상 클래스가 제공하는 기능에 따라 다르지만 퍼블릭인터페이스를 통해 테스트하는 구현 예로서 기능합니다.

테스트에서 익명 클래스로 추상 클래스를 확장할 수 있습니다.예: (Junit 4 사용):

private AbstractClassName classToTest;

@Before
public void preTestSetup()
{
    classToTest = new AbstractClassName() { };
}

// Test the AbstractClassName methods.
class Dependency{
  public void method(){};
}

public abstract class My {

  private Dependency dependency;
  public abstract boolean myAbstractMethod();

  public void myNonAbstractMethod() {
    // ...
    dependency.method();
  }
}

@RunWith(MockitoJUnitRunner.class)
public class MyTest {

  @InjectMocks
  private My my = Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS);
  // we can mock dependencies also here
  @Mock
  private Dependency dependency;

  @Test
  private void shouldPass() {
    // can be mock the dependency object here.
    // It will be useful to test non abstract method
    my.myNonAbstractMethod();
  }
}

어나니머스 클래스를 인스턴스화하고 모크를 주입한 후 해당 클래스를 테스트할 수 있습니다.

@RunWith(MockitoJUnitRunner.class)
public class ClassUnderTest_Test {

    private ClassUnderTest classUnderTest;

    @Mock
    MyDependencyService myDependencyService;

    @Before
    public void setUp() throws Exception {
        this.classUnderTest = getInstance();
    }

    private ClassUnderTest getInstance() {
        return new ClassUnderTest() {

            private ClassUnderTest init(
                    MyDependencyService myDependencyService
            ) {
                this.myDependencyService = myDependencyService;
                return this;
            }

            @Override
            protected void myMethodToTest() {
                return super.myMethodToTest();
            }
        }.init(myDependencyService);
    }
}

가시성이 다음과 같아야 한다는 점에 유의하십시오.protected재산에 대하여myDependencyService추상적인 종류의ClassUnderTest.

PowerMock의Whitebox.invokeMethod(..)이 경우 편리할 수 있습니다.

언급URL : https://stackoverflow.com/questions/1087339/using-mockito-to-test-abstract-classes