Мне нужно смоделировать функцию, но она не работает. Как построить unittest без изменения кода src? - PullRequest
0 голосов
/ 10 октября 2018

Это функция, которую я хочу проверить:

@Component
public class DataSourceAttributes {

    ...
    ...

    public AWSSecretDB getAttribsBySecret() throws Exception {
        AbstractConnector abstractConnector = new AWSSecretManagerConnector("secretsmanager." + region + ".amazonaws.com", region);
        GenericManager genericManager = new GenericManager(abstractConnector);
        System.out.println("Generic Manager: " + genericManager);

        AWSSecretDB awsSecretDB;
        try {
            awsSecretDB = genericManager.getSecretModel(secretName, AWSSecretDB.class);
            System.out.println("awsSecretDB: " + awsSecretDB.getEngine()); // It must be mocked
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw e;
        }
        return awsSecretDB;
    }
}

Это мой текущий модульный тест:

public class DataSourceAttributesTest {

    @InjectMocks
    private DataSourceAttributes dataSourceAttributes;

    @Mock
    private GenericManager genericManagerMock;

    @Test
    public void AWSSecretDBGetAttribsBySecret() throws Exception {

        AWSSecretDB awsSecretDB = new AWSSecretDB();
        awsSecretDB.setEngine("Engine Test");
        awsSecretDB.setDbname("DB Test");
        awsSecretDB.setHost("Host Test");

        when(genericManagerMock.getSecretModel(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(awsSecretDB);
        dataSourceAttributes.getAttribsBySecret();

        // The assert is missing, but it's not important for this question
    }
}

Мне нужно смоделировать genericManager , чтобыcontrol getSecretModel () функция, но она не работает.

Когда я запускаю свой тест, System.out.println (расположенный в getAttribsBySecret) печатает следующее сообщение, подтверждающее, что макет не работает:

Generic Manager: co.com.bancolombia.commons.secretsmanager.manager.GenericManager@1349883

Я знаю, использую ли я следующеекод, макет прекрасно работает, но я не хочу перекодировать то, что уже работает в каталоге src:

@Component
public class DataSourceAttributes {

    private GenericManager genericManager;  // First change

    public DataSourceAttributes () { // Second cahnge
        AbstractConnector abstractConnector = new AWSSecretManagerConnector("secretsmanager." + region + ".amazonaws.com", region);
        this.genericManager = new GenericManager(abstractConnector);
    }

    public AWSSecretDB getAttribsBySecret() throws Exception {

        System.out.println("Generic Manager: " + genericManager);
        AWSSecretDB awsSecretDB;
        try {
            awsSecretDB = genericManager.getSecretModel(secretName, AWSSecretDB.class);
            System.out.println("awsSecretDB: " + awsSecretDB.getEngine());
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw e;
        }
        return awsSecretDB;
    }
}

Когда я запускаю свой тест, System.out.println (находится в getAttribsBySecret) печатает:

GENERIC MANAGER: genericManagerMock
awsSecretDB: Engine Test

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

Спасибо!

Ответы [ 2 ]

0 голосов
/ 14 марта 2019

Если вы знаете, что код работает, зачем юнит-тестировать его, в первую очередь?Или зачем выполнять действие, которое может вызвать ошибку, если вы не собираетесь менять источник, чтобы исправить ее?

Но, если серьезно, вы, похоже, застряли в «дилемме унаследованного кода»:измените код, вы бы предпочли иметь тесты на месте.Чтобы получить тесты на месте, вы должны изменить код.

Выход из этого состоит в том, чтобы начать с минимально инвазивных изменений, которые просто достаточны для создания тех тестов, которые вам нужны.Поскольку это миграционный подход, иногда даже грязные приемы приемлемы для преодоления дилеммы.Это подробно обсуждается с большим количеством практических советов Майкла Фезерса в «Эффективной работе с устаревшим кодом».

В языках с самоанализом, таких как Java, иногда самоанализ может быть решением на пути к лучшему тестируемому решению.,Это, однако, относится к категории грязных уловок и, кроме того, что служит способом перехода к лучшему решению, однако я не считаю его целесообразным.

0 голосов
/ 11 октября 2018

Чтобы использовать модульные тесты, сначала нужно сделать тестируемый код.И это может потребовать некоторых изменений кода.Нет способа смоделировать локальные переменные внутри методов, поэтому вам нужно либо передать эти переменные в качестве параметров методам, либо создать свойства внутри объектов и передать смоделированные объекты в конструктор.

@Component
public class DataSourceAttributes {
  private AbstractConnector abstractConnector;
  private GenericManager genericManager;

  @Autowired // to ask Spring to inject dependencies
  public DataSourceAttributes(AbstractConnector abstractConnector, GenericManager genericManager) {
    this.abstractConnector = abstractConnector;
    this.genericManager = genericManager;
  }

  public AWSSecretDB getAttribsBySecret() throws Exception {
    System.out.println("Generic Manager: " + genericManager);

    AWSSecretDB awsSecretDB;
    try {
        awsSecretDB = genericManager.getSecretModel(secretName, AWSSecretDB.class);
        System.out.println("awsSecretDB: " + awsSecretDB.getEngine()); // It must be mocked
    } catch (Exception e) {
        LOGGER.error(e.getMessage(), e);
        throw e;
    }
    return awsSecretDB;
 }
}

И затем в тесте вы создаете экземплярваш объект, передающий макетированные зависимости в конструктор

...