Юнит-тестирование закачиваемых объектов JSR-330

Ранее я использовал Spring DI, и одно из преимуществ, которое я чувствую, заключается в том, что я могу тестировать свои классы bean-компонентов Spring без участия Spring (импорт для краткости опущен):

public class Foo {
    private String field;

    public void setField(String field) { this.field = field; }
    public String getField() { return field; }
} 

public class TestFoo {
    @Test
    public void test_field_is_set() {
       Foo foo = new Foo();
       foo.setField("Bar");
       assertEquals("Bar", foo.getField());
    }
}

Сейчас я экспериментирую с JSR-330, что означает не писать явно сеттеры.

До сих пор я использую Hk2, просто из-за некоторых анекдотических вещей о том, что Джерси привязан к Hk2, и затрудняет совместную работу с другими реализациями JSR-330.

public class Foo {
   @Inject 
   private String field;
}

Я наполовину ожидал, что произойдет какое-то волшебство, благодаря чему аннотация @Inject привела к появлению сеттера, но это не так:

Foo foo = new Foo();
foo.setField("Bar"); // method setField(String) is undefined for the type Foo
  • Как я могу (удобно) протестировать этот вид аннотированного класса, не вызывая фреймворк?
  • В противном случае, как я могу вызывать фреймворк переносимым способом (то есть без тесной связи моего тестового кода с Hk2, Guice и т. Д.)
  • В противном случае, каков типичный, чистый способ проверки классов, аннотированных таким образом?

4 ответа

Решение

Оказывается, что фреймворки, основанные на частном / защищенном доступе к полям, не так уж редки Hibernate, JPA, несколько реализаций JSR-330, включая сам Spring, делают это.

Spring-тестовый пакет Spring предоставляет класс ReflectionTestUtils, содержащий статические методы для доступа к этим полям.

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

import static org.springframework.test.util.ReflectionTestUtils.*;

...

@Test
public void testUsingSpringReflectionTestUtils() {
    Foo foo = new Foo();
    setField(foo, "field", "Bar");
    assertEquals("Bar", foo.getField());
}

Для этого вам понадобятся spring-test и spring-core в вашем пути к классам тестов, но это не добавляет зависимости от Spring для вашего производственного кода.

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

Проще всего сделать поля package-private (вместо private), а затем в тесте установить их напрямую. (Это работает, если тест находится в той же упаковке)

public class Foo {
   @Inject 
   String field;
}



Foo foo = new Foo();
foo.field = "bar";

Преимущество в том, что вы избегаете рефлексии, поэтому это безопасно для рефакторинга.

Упомянутый вами подход к полевым инжекциям на самом деле типичный стиль Spring; многие программисты вообще не пишут сеттеры для закрытых вставленных полей. Весна (с @Autowired или же @InjectКонтейнеры) и JSR-330 обычно вводят поля, используя прямое отражение поля, а не сеттеры.

Из-за этого, если вы не хотите использовать какую-либо инфраструктуру DI, вы можете сами добавить необходимый код отражения в свои модульные тесты, но это кажется излишним, просто чтобы избежать зависимости теста; в конце концов, смысл использования @Inject в том, что вы кодируете интерфейс, и вы не избегаете использования JVM, чтобы избежать соединения с ним.

Обычный подход к тестированию такого класса заключается в том, чтобы установить контекст теста для любого контейнера, который вы предпочитаете, и запустить модульные тесты в этом контексте. Если вы используете Spring, вы бы положили applicationContext-test.xml файл или TestConfig класс в вашем src/test/ каталог (или эквивалент), и если вы используете Guice, вы бы написали модуль для подключения макетов или тестовых наборов данных.

Дайте "иглу" попробовать: http://needle.spree.de/overview

needle - это DI-test-framework, который имитирует только поведение контейнера, делая модульные тесты очень простыми.

Другие вопросы по тегам