Guice, DI и модульные тесты в 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);
}

ОБНОВИТЬ:

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

Внутри моего класса 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 не внедряется.

ОТВЕЧАТЬ:

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

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

person Zarathuztra    schedule 29.08.2015    source источник
comment
Я думаю, что вы забыли создать макет репозитория @Injected, и поэтому вы получаете исключение.   -  person mr. Holiday    schedule 07.09.2015
comment
Каким будет правильный способ создать этот макет? Конструктор приложений? Если да, то куда это пойдет?   -  person Zarathuztra    schedule 07.09.2015
comment
Если вы используете конструктор или инъекцию параметров, это будет довольно просто, но из того, что вы предоставили, я предполагаю, что вы не используете ни один из них, поэтому взгляните на этот поток stackoverflow.com/questions/2448013/how-test-guice-injections   -  person mr. Holiday    schedule 07.09.2015


Ответы (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 сделают немного больше. смысл сейчас.

person Steve Chaloner    schedule 09.09.2015
comment
Это ИМЕННО то, что я искал. сегодня вечером попробую посмотреть что будет - person Zarathuztra; 09.09.2015
comment
Я пробовал это, и он все еще выдает исключение NullPointerException. Вставил мой код в мой вопрос. Я следовал примерам, которые вы мне дали, и соединил их с тем, что есть в документации Play, но я все еще не могу заставить инжектор работать правильно. - person Zarathuztra; 10.09.2015
comment
Неважно, я понял, что мне нужно привязать введенный объект репозитория к инжектору, а затем создать из него объект TagService. Вы указали мне правильный путь, большое спасибо! - person Zarathuztra; 10.09.2015