#java #spring #unit-testing #spring-mvc #annotations
#java #весна #модульное тестирование #spring-mvc #аннотации
Вопрос:
Предположим, я хочу написать benchmark
для класса, который может быть autowired
, таким образом, мне нужно загрузить application context
.
Мой тест имеет аннотацию @org.openjdk.jmh.annotations.State(Scope.Benchmark)
и основной метод
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(MyBenchmark.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
И, конечно, у меня есть несколько таких тестов:
@Benchmark
public void countAllObjects() {
Assert.assertEquals(OBJECT_COUNT, myAutowiredService.count());
}
Теперь вопрос в том, как мне внедрить myAutowiredService
?
Возможное решение
Загрузите контекст вручную в @Setup
методе.
ApplicationContext context = new ClassPathXmlApplicationContext("META-INF/application-context.xml");
context.getAutowireCapableBeanFactory().autowireBean(this);
Но мне не нравится это решение. Я бы предпочел, чтобы в моем тесте была только аннотация
@ContextConfiguration(locations = { "classpath:META-INF/application-context.xml" })
а затем я просто вводю свой компонент, например
@Autowired
private MyAutowiredService myAutowiredService;
но это не работает. Я полагаю, причина в том, что у меня нет аннотации, что мой тест должен выполняться с Spring:
@RunWith(SpringJUnit4ClassRunner.class)
Однако нет смысла делать это, потому что у меня также нет никаких @Test
аннотированных методов, поэтому я получу No runnable methods
исключение.
Могу ли я добиться загрузки контекста через аннотации в этом случае?
Комментарии:
1. вы когда-нибудь находили решение?
Ответ №1:
Я нахожу код из spring-cloud-sleuth, он работает для меня
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
public class DemoApplicationTests {
volatile DemoApplication app;
volatile ConfigurableApplicationContext context;
private VideoService videoService;
@Test
public void contextLoads() throws RunnerException {
Options opt = new OptionsBuilder()
.include(DemoApplicationTests.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
@Setup
public void setup() {
this.context = new SpringApplication(DemoApplication.class).run();
Object o = this.context.getBean(VideoService.class);
videoService = (VideoService)o;
}
@TearDown
public void tearDown(){
this.context.close();
}
@Benchmark
public String benchmark(){
return videoService.find("z");
}
Ответ №2:
@State(Scope.Benchmark)
public static class SpringState {
AnnotationConfigApplicationContext context;
@Setup(Level.Trial)
public void setup() {
context = new AnnotationConfigApplicationContext();
context.register(CLASSNAME.class);
context.register(ANOTHER_CLASSNAME_TO_BE_LOADED.class);
context.refresh();
}
@TearDown(Level.Trial)
public void tearDown() {
context.close();
}
}
Ответ №3:
Я бы выбрал getAutowireCapableBeanFactory().autowire()
решение, которое вы уже набросали.
Должен быть некоторый шаблонный код, который загружает контекст приложения и запускает автозапуск. Если вы предпочитаете указывать конфигурацию своего приложения с аннотациями, метод установки может выглядеть примерно так:
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(MyBenchmarkWithConfig.class);
context.refresh();
Комментарии:
1. На данный момент у меня все еще есть конфигурация на основе xml, но я предполагаю, что однажды я перейду на версию на основе аннотаций. Итак, основываясь на вашем ответе, кажется, что это невозможно сделать с помощью аннотаций, это правильно?
2. Я не тестировал это, но подход AnnotationConfigWebApplicationContext должен работать, если вы аннотируете свой класс MyBenchmark с помощью @ContextConfiguration, который указывает на XML-файл.