Модульное тестирование Springboot: SpyBean против MockBean

Spring Boot

Какой вопрос?

Ниже приведен тестируемый класс, который, как ожидается, будет протестирован.TestServiceизtestметод, но по какой-то причине (т.doSomething) не может быть просто выполнено. так что надеюсьtestметод действительно выполняется, но дляdoSomethingметод укладки.

package com.example.demo.service;

import com.example.demo.repositroy.TestRepository;
import org.springframework.stereotype.Component;

@Component
public class TestService {
    private final TestRepository testRepository;

    public TestService(TestRepository testRepository) {
        this.testRepository = testRepository;
    }

    public String doSomething(){
        //假装有复杂的无法执行的业务逻辑
        testRepository.doSomething();
        throw new RuntimeException();
    }

    public String test() {
        doSomething();
        //其他逻辑
        return "id";
    }
}

Как решить?

Использование мокбинов

Если только использовать@MockBeanСмоделируйте измененный объект, чтобыTestServiceизdoSomething()метод больше не выполняет конкретные детали, ноMockBeanбудет издеваться над всеми методами целевого объекта, поэтомуtestЭто не может быть реализовано, и это не может быть протестировано.

а такжеwhen...thenCallRealMethodНекоторых имитационных эффектов можно добиться, толькоtestМетод действительно выполняется.

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestServiceTest {
    @MockBean
    TestService testService;

    @Test
    public void test(){
        when(testService.test()).thenCallRealMethod();
        assertThat(testService.test(), equalTo("id"));
    }
}

Использование SpyBeans

использовать@SpyBeanдекоративныйtestServiceявляется реальным объектом, только еслиdoReturn("").when(testService).doSomething()час,doSomethingМетод заглушен, и другие методы по-прежнему вызываются.

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestServiceTest {
    @SpyBean
    TestService testService;

    @Test
    public void test(){
        doReturn("").when(testService).doSomething();
        assertThat(testService.test(), equalTo("id"));
    }
}

маленькая ловушка

и использовать@MockBeanДругой, названный в предыдущем разделеdoReturn("").when(testService).doSomething()ВремяdoSomethingМетод забит. а такжеwhen(testService.doSomething()).thenReturn("")Этот эффект не может быть достигнут. Причина: использование@SpyBeanдекоративныйtestServiceявляется реальным объектом, поэтомуtestService.doSomething()на самом деле будет называться.

В официальной документации Mockito говорится следующее:

Sometimes it's impossible or impractical to use when(Object) for stubbing spies. Therefore when using spies please consider doReturn|Answer|Throw() family of methods for stubbing. Example:

 List list = new LinkedList();
   List spy = spy(list);

   //Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
   when(spy.get(0)).thenReturn("foo");

   //You have to use doReturn() for stubbing
   doReturn("foo").when(spy).get(0);

Mockito does not delegate calls to the passed real instance, instead it actually creates a copy of it. So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction and their effect on real instance state. The corollary is that when an unstubbed method is called on the spy but not on the real instance, you won't see any effects on the real instance.

Watch out for final methods. Mockito doesn't mock final methods so the bottom line is: when you spy on real objects + you try to stub a final method = trouble. Also you won't be able to verify those method as well.

Резюме:

  • При использовании шпиона рассмотрите возможность использованияdoReturn|Answer|Throw()
  • Для переменных, измененных шпионом, Mockito воссоздает копию экземпляра и не воздействует напрямую на реальный экземпляр.
  • шпион не влияет на окончательные методы

SpyBean vs MockBean

SpyBeanа такжеMockBeanдаspring-boot-testДве аннотации, предоставляемые пакетом для экземпляров, управляемых контейнером Spy или Mock Spring. Spy и Mock как раз наоборот: по умолчанию все методы spy вызываются на самом деле, а все методы Mock по умолчанию вызывают реализацию mock. В чем разница в сценариях использования? Основываясь на приведенном выше примере, хотя оба могут быть достигнуты,SpyBeanболее подходящим, так как приведенный выше пример является проверкойTestService,такtestServiceНе должен быть полностью издевательским экземпляром. и если вTestServiceХотите издеваться над испытательной силойTestRepository,использоватьMockBeanявляется более подходящим.

Рассмотрите возможность использования заглушек для раздела класса тестового тела.SpyBean, накапливая внешние зависимости, рассмотрите возможность использованияMockBean.

Суммировать

  • SpyBeanа такжеMockBeanдаspring-boot-testДве аннотации, предоставляемые пакетом, используются для экземпляров, управляемых контейнером Spy или Mock Spring;
  • использоватьSpyBeanилиSpyКогда требуется нагромождение метода, необходимо обратить внимание на некоторые ограничения использования;
  • Рассмотрите возможность использования заглушек для раздела класса тестового тела.SpyBean, накапливая внешние зависимости, рассмотрите возможность использованияMockBean.