Guice, DI и Unit Tests in Play 2.4

Итак, я пытался понять это самостоятельно, используя документацию, но я никуда не денусь.

У меня есть простая настройка привязок DI в классе сервиса, который создает объект репозитория. Просто. Однако, когда я запускаю это в тестовом режиме, @Inject ничего не делает, и объект репозитория никогда не создается.

@Inject
TagRepository tagRepository;

Итак, в строке, где он используется, в тестовом режиме мы, конечно, получаем NullPointerException

tagRepository.tagExistsByName(tag);

Это впрыскивается в мой тест следующим образом:

[error] Test services.TagsServiceTest.testAddNewTag failed: java.lang.NullPointerException: null, took 0.097 sec
[error]     at services.TagService.tagExists(TagService.java:27)
[error]     at services.TagService.addNewTag(TagService.java:18)
[error]     at services.TagsServiceTest.testAddNewTag(TagsServiceTest.java:29)

Мой вопрос: как мне настроить приложение для использования инжекторов Guice в тестовом режиме? У меня не было этой проблемы с моими контроллерами, потому что на них действительно делали запросы, настраивая полное приложение.

Одна вещь, которую я должен упомянуть, это то, что я использую провайдера для предоставления моего приложения для тестов. Должен ли я использовать построитель приложений Guice? Если да, то куда это? Игровые документы не очень полезны в этом отношении. Вот провайдер

@Override
protected FakeApplication provideFakeApplication() {
    return new FakeApplication(new java.io.File("."), Helpers.class.getClassLoader(), ImmutableMap.of("play.http.router", "router.Routes"), new ArrayList<String>(), null);
}

UPDATE:

Вот обновление, основанное на приведенном ниже предложении

Внутри моего класса BaseTest

    @Override
    protected Application provideApplication() {
        return new GuiceApplicationBuilder().in(Mode.TEST).build();
    }

И затем в классе тестирования обслуживания

    @Before
    public void beforeTest() {
        Injector injector = new GuiceInjectorBuilder().bindings(bind(TagService.class).toInstance(new TagService())).injector();
        tagService = injector.instanceOf(TagService.class);
    }

Однако, я все еще получаю исключения с нулевым указателем, потому что TagRepository не вводится.

Ответ:

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

@Before
public void beforeTest() {
    Injector injector = new GuiceInjectorBuilder().bindings(bind(TagRepository.class).toInstance(new TagRepository())).injector();
    tagService = injector.instanceOf(TagService.class);
}

Ответ 1

Если вы расширяете WithApplication, вы можете переопределить protected Application provideApplication(), чтобы вернуть Application с помощью GuiceApplicationBuilder.

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

public abstract class AbstractFakeApplicationTest extends WithApplication
{
    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractFakeApplicationTest.class);

    @Override
    protected Application provideApplication()
    {
        return new GuiceApplicationBuilder().in(Mode.TEST)
                                            .build();
    }

    @Override
    public void startPlay()
    {
        super.startPlay();
        // mock or otherwise provide a context
        Http.Context.current.set(new Http.Context(1L,
                                                  Mockito.mock(RequestHeader.class),
                                                  Mockito.mock(Http.Request.class),
                                                  Collections.<String, String>emptyMap(),
                                                  Collections.<String, String>emptyMap(),
                                                  Collections.<String, Object>emptyMap()));
    }

    public Http.Context context()
    {
        return Http.Context.current.get();
    }
}

Детские классы затем просто расширяют этот класс и проверяют как обычно - все DI должно происходить так же, как при обычном запуске приложения.

Здесь вы можете увидеть различные примеры

Это дает основной план того, что вам нужно сделать. Надеемся, что документы в https://playframework.com/documentation/2.4.x/JavaTestingWithGuice станут более понятными.