CDI создает и вводит компоненты типа «родитель-потомок» либо неоднозначно, либо дочерние типы не найдены - PullRequest
0 голосов
/ 18 апреля 2019

У меня есть такой интерфейс:

public interface DateTimeService {
    ZonedDateTime now();

    void fixTo(ZonedDateTime date);

    void forget();
}

, и у меня есть две его реализации.Один для производства, где fixTo и forget генерирует исключения, а другой для тестирования, где мы контролируем время.Затем у меня есть конфигурация CDI, которая в зависимости от флага создает правильный тип.

@ApplicationScoped
public class Configuration {
    @Produces
    @ApplicationScoped
    public DateTimeService dateTimeService(Configuration config) {
        if (config.isFakeDateTimeServiceEnabled()) {
            return new FakeDateTimeService();
        } else {
            return new DefaultDateTimeService();
        }
    }
}

Однако я хотел удалить fixTo и forget из DateTimeService, поскольку они есть только там, поэтому мы можемконтрольное время в тестах.Я сделал новый интерфейс, например:

public interface FakeDateTimeService extends DateTimeService {
    // fixto and forget is moved from DateTimeService to here
}

У меня есть несколько точек ввода.Некоторые в рабочем коде, некоторые в тестовом кодеВ рабочем коде я хотел бы иметь возможность доступа только к DateTimeSerice, в тестовом коде я хочу иметь возможность получить указатель на расширенный сервис.

В коде продукта:

    @Inject
    private DateTimeService dateTimeService;

В тестовом коде:

    @Inject
    private FakeDateTimeService dateTimeService;

Если я оставлю конфигурацию без изменений, то тестовый код никогда не найдет мою расширенную службу (поскольку CDI, похоже, игнорирует тип времени выполнения экземпляра, созданного методом продюсера).

Если я обновлю конфигурацию для создания экземпляров обоих (в этом случае я даже могу внедрить подлинную службу в поддельную), тогда производство не сможет соединиться, так как в fake также реализован интерфейс DateTimeService, и это вызываетнеоднозначность.В этот момент я, вероятно, мог бы просто использовать квалификатор, но я не хочу менять свой производственный код из-за этого и не должен иметь поддельную службу времени и даты в производственном контексте.

Я пытался наложить вето на создание бина, ноЯ не смог этого сделать.

Я думал, что сработает / должен работать, это создать экземпляр правильного типа и программно добавить его в контекст компонента, но я нашел примеры для CDI 2.0, в то время как на данный момент соответствующая часть моего кодазастрял на CDI 1.2.

Возможно, в этот момент вы можете сказать, что я не эксперт CDI.Так что я открыт для предложений о хороших материалах для чтения CDI, а также конкретных предложений по этой проблеме.

В противном случае, я собираюсь сдаться и просто жить с DateTimeService, которые имеют fixTo иforget методов.

1 Ответ

0 голосов
/ 05 мая 2019

В зависимости от того, как вы это делаете, вы можете использовать следующие подходы, при этом некоторые из них уже упоминались в комментариях.

С альтернативами

Для тестирования вы предоставляете собственный файл beans.xml, который определяет альтернативу для тестирования, которая заменяет производственную. Вам нужно будет синхронизировать их, в результате чего test beans.xml содержит изменения, необходимые для тестирования. В зависимости от реализации CDI файл beans.xml может быть пропущен, и @Alternative будет достаточно.

https://docs.oracle.com/javaee/6/tutorial/doc/gjsdf.html

interface DateTimeService  { ... }

// Active during production, beans.xml does not define alternative
class DefaultDateTimeService implements DateTimeService  { ... }

// Active during test, beans.xml defines alternative
@Alternative
class FakeDateTimeService implements DateTimeService { ... }

// Injection point stays the same.
@Inject
DateTimeService dateTimeService;

// Used in test environment
<beans ... >
    <alternatives>
        <class>FakeDateTimeService</class>
    </alternatives>
</beans>

С Аркиллианом

С Aarquillian вы можете самостоятельно собрать развертывание, исключить реализацию DefaultDateTimeService и заменить ее реализацией FakeDateTimeService. При таком подходе вам не нужно ничего взламывать, потому что во время теста контейнеру CDI будет видна только реализация FakeDateTimeService.

http://arquillian.org/guides/getting_started/

с расширением CDI

Для тестирования вы можете предоставить расширение CDI и реализацию FakeDateTimeService в тестовом коде, при этом расширение CDI накладывает вето на реализацию DefaultDateTimeService.

package test.extension

public class VetoExtension implements Extension {

    public <T> void disableBeans(@Observes ProcessAnnotatedType<T> pat) {
        // type matching
        if (DefaultDateTimeService.class.isAssignableFrom(pat.getAnnotatedType().getJavaClass())) {
            pat.veto();
        }
    }
}

// Define SPI provider file, assuming test/resources is on CDI classpath
src/test/resources/META-INF/services/javax.enterprise.inject.spi.Extension
// Single line you put in there
test.extension.VetoExtension 

Deltaspike @ Exclude

Deltaspike - это библиотека расширений CDI, которая предоставляет несколько полезных утилит для разработки и тестирования CDI, также предоставляется CDITestRunner. Аннотация @Exclude используется расширением, которое делает то же самое, что и в примере выше, но уже реализовано для вас.

https://deltaspike.apache.org/

https://deltaspike.apache.org/documentation/test-control.html

https://deltaspike.apache.org/documentation/projectstage.html

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

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

С помощью Deltaspike вы можете исключить производственный код для тестирования и заменить его тестовыми реализациями.

Если никакие дополнительные библиотеки или фреймворки не могут быть использованы, я бы предпочел альтернативный подход, потому что он наименее сложный в использовании.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...