Простой модульный тест с Mocks продолжает проваливаться; фиктивный объект не передается в класс? - PullRequest
0 голосов
/ 15 марта 2020

У меня есть простой модульный тест, который не проходит. Надеюсь, я смогу объяснить это простыми словами, поскольку я смотрел на это часами, и я вижу, в чем проблема, но я не слишком знаком с основной теорией, стоящей за Mocks, поэтому я немного запутался и не могу ее исправить. Я очень быстро подведу итог этой проблемы, а затем вставлю приведенный ниже код.

В основном, в моем методе тестирования, называемом getAllValidModelsTest () , он использует для l oop для итерации через значения перечисления типа объекта DeviceModel . Их всего 5: [EX3400_24P, EX4300_32F, EX4300_48MP, SRX_345, FAUX].

Таким образом, внутри для l oop перед оператором Assert (Junit) выполняется вызов метода stati c для getDevice (deviceId) , и он должен оттуда вернуть объект Device . В первой строке для for l oop в getAllValidModelsTest () объект mocketMock возвращает объект модель , который итерируется в массиве DeviceModels [], который был возвращен из вызова .values ​​() класса enum DeviceModel.

Так что моя проблема в том, что когда он переходит на 2-й итерации в my для l oop (считая от 1), Assert не выполняется, потому что Очевидно, что 0-й элемент в массиве DeviceModel [] - это EX4300_32F, но в аннотации @Before setUp он высмеивается для возврата EX3400_24P. Но странная вещь в том, что для l oop в методе getAllValidModelsTest () он снова переопределяется / насмехается , чтобы вернуться к текущей модели, которая итерируется когда .getModel вызывается для объекта elementMock, поэтому он должен возвращать значение SAME ...

Вот как класс SwitchDeviceFactoryTest. java построенный (класс с модульным тестом):

    @PowerMockIgnore({"javax.net.ssl.*"})
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({DataGatewayFactory.class, SwitchConfig.class, RouterConfig.class})
    public class SwitchDeviceFactoryTest {

        String deviceId = "testdevice";
        String ip = "1.1.1.1";
        DataGateway dbMock = Mockito.mock(DataGateway.class);
        SwitchConfig swConfigMock = PowerMockito.mock(SwitchConfig.class);
        RouterConfig routerConfigMock = PowerMockito.mock(RouterConfig.class);
        TransportDeviceSecretsInfo secrets = new TransportDeviceSecretsInfo();
        TransportDeviceSecretsData secretsData = new TransportDeviceSecretsData("root","rootPw", "sshUser", "sshPass", "snmpAuthPass", "snmpPrivPass");
        IElement elementMock = Mockito.mock(IElement.class);

        ITransportDeviceSecretsCrud transportDeviceSecretsCrud = mock(ITransportDeviceSecretsCrud.class);
        ISwitchConfigCrud switchConfigCrud = mock(ISwitchConfigCrud.class);
        IRouterConfigCrud routerConfigCrud = mock(IRouterConfigCrud.class);
        IElementCrud elementCrud = mock(IElementCrud.class);

Это мой метод setUp, который выполняется перед тестом. Единственные переменные, которые должны иметь значение, - это элемент elementMock, в частности, объект, который насмехается, чтобы возвратить объект EX3400_24P:

@Before
public void setup() throws Exception {
    secrets.setSecretsData(secretsData);
    PowerMockito.mockStatic(DataGatewayFactory.class);
    Mockito.when(DataGatewayFactory.getInstance()).thenReturn(dbMock);
    Mockito.when(dbMock.getTransportDeviceSecretsCrud()).thenReturn(transportDeviceSecretsCrud);
    Mockito.when(transportDeviceSecretsCrud.getServerSecretsInfo(anyString())).thenReturn(Optional.of(secrets));
    Mockito.when(transportDeviceSecretsCrud.getReportedSecretsInfo(anyString())).thenReturn(Optional.of(secrets));

    when(dbMock.getElementCrud()).thenReturn(elementCrud);
    doReturn(Optional.of(elementMock)).when(elementCrud).getById(anyString());
    Mockito.when(elementMock.getModel()).thenReturn(DeviceModel.EX3400_24P.getModel());

    Mockito.when(elementMock.getType()).thenReturn(ElementType.SWITCH);
    Mockito.when(dbMock.getSwitchConfigCrud()).thenReturn(switchConfigCrud);
    Mockito.when(switchConfigCrud.get(anyString())).thenReturn(Optional.of(swConfigMock));
    Mockito.when(swConfigMock.getIp()).thenReturn(ip);
    Mockito.when(dbMock.getRouterConfigCrud()).thenReturn(routerConfigCrud);
    Mockito.when(routerConfigCrud.get(anyString())).thenReturn(Optional.of(routerConfigMock));
    Mockito.when(routerConfigMock.getIp()).thenReturn(ip);

И метод теста:

@Test
public void getAllValidModelsTest() throws Exception {
    for (DeviceModel model: DeviceModel.values()) {
        when(elementMock.getModel()).thenReturn(model.getModel());
        if (model == DeviceModel.SRX_345)
            when(elementMock.getType()).thenReturn(ElementType.ROUTER);
        else
            when(elementMock.getType()).thenReturn(ElementType.SWITCH);
        Device device = DeviceFactory.getDevice(deviceId);
        assertEquals(model, device.getModel());
    }
}

То, что не имеет смысла, я рефакторинг кода, и только изменил 2 строки (elementCrud и elementMock .doReturn и .when вызовы), и он отлично работает на ветви разработки.

Когда я отлаживаю, я Можно видеть, что на 2-й итерации для l oop .getModel возвращает объект EX3400_24P внутри метода stati c getDevice, когда он должен возвращать model.getModel (), который будет вторым объектом, который повторяется в массив перечислений .values ​​() для DeviceModels ... поэтому он должен быть EX4300_32F.

В ветви разработки это работает отлично ... Это как если бы объект-имитатор Mockito забывал, что он должен делать, когда он переходит в класс DeviceFactory внутри метода getDevice после его вызова в моем методе getAllValidModelsTest () (т. е. De недостаток устройства = DeviceFactory.getDevice (deviceId);)

Вот метод .getDevice из класса DeviceFactory:

public static Device getDevice(String serialNumber) throws Exception {
    IElement element = dataGateway.getElementCrud().getById(serialNumber).get();
    DeviceModel model = DeviceModel.valueOfLabel(element.getModel()); // right here is where it returns the wrong model... it returns EX3400_24P on the 2nd iteration
    log.info("Found device {} in database", serialNumber);

    if (serialNumber.startsWith(FakeDevicePrefix.ATGTEST.toString()) || serialNumber.startsWith(FakeDevicePrefix.FAKE.toString())) {
        log.info("Detected FAKE/ATG serial number. Using FAUX device.");
        model = DeviceModel.FAUX;
    }

    switch (element.getType()) {
        case SWITCH:
            SwitchConfig config = dataGateway.getSwitchConfigCrud().get(serialNumber).get();
            return getDevice(serialNumber, config.getIp(), model);
        case ROUTER:
            RouterConfig rconfig = dataGateway.getRouterConfigCrud().get(serialNumber).get();
            return getDevice(serialNumber, rconfig.getIp(), DeviceModel.SRX_345);
        case PTP:
        default:
            log.warn("Unsupported device type {}", element.getType().toString());
            throw new Exception("Unsupported device type " + element.getType().toString());
    }
}

Я действительно закомментировал / удалил кусок кода, который высмеивает он возвращает EX3400_24P в методе setUp () с аннотацией @Before, но на этом этапе тесты завершаются неудачно с ИСКЛЮЧЕНИЕМ NULL POINTER. предыдущий класс (SwitchDeviceFactoryTest. java), прежде чем он перейдет в класс DeviceFactory. java? Как он помнит, что если я не передаю его как переменную в метод getDevice ()?

Нужно ли использовать PowerMock или что-то еще, потому что это метод stati c? Как это что-то меняет?

Пожалуйста, помогите!

...