Spring Boot Unit Test @Value из файла .properties дает исключение NullPointerException - PullRequest
1 голос
/ 23 сентября 2019

Я пытаюсь прочитать значение из файла свойств для случая модульного теста в Spring Boot.У меня есть два config.properties файла, один в src/main/resources:

prop = some-value

и один в src/test/resources:

prop = some-test-value

Основной класс приложения:

package company.division.project;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.PropertySource;

@SpringBootApplication(scanBasePackages = "company.division.project")
@PropertySource(value = "classpath:config.properties")
public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        System.setProperty("DUMMY_PROPERTY", "dummy-value");

        return application.sources(Application.class);
    }

    public static void main(String[] args) throws Exception {
        // Do nothing with main
    }
}

Класс обслуживания, подлежащий проверке:

package company.division.project.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
public class Service {
    @Autowired
    Environment environment;

    public String getProperty() {
        return environment.getProperty("prop");
    }

}

Класс ServiceTest.Я пробовал два подхода к получению значения в файле src/test/resources/config.properties;один с @Autowired Environment, а другой с аннотацией @Value ... ни один не работал:

package company.division.project.service;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;

@RunWith(MockitoJUnitRunner.class)
@TestPropertySource("classpath:config.properties")
public class ServiceTest {
    @InjectMocks
    Service service;

    @Autowired
    Environment environment;

    @Value("${prop}")
    private String expectedProperty;

    @Test
    public void testGetPropertyWithValueAnnotation() {
        assertEquals(expectedProperty, service.getProperty());
    }

    @Test
    public void testGetPropertyWithEnvironment() {
        assertEquals(environment.getProperty("prop"), service.getProperty());
    }
}

Я читал где-то в StackOverflow, что для автоматической связи компонентов в тестовом классе Spring,Мне нужно создать весь контекст для теста, поэтому я попробовал это (измените аннотации и запуска теста):

package company.division.project.service;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {
    @InjectMocks
    Service service;

    @Autowired
    Environment environment;

    @Value("${prop}")
    private String expectedProperty;

    @Test
    public void testGetPropertyWithValueAnnotation() {
        assertEquals(expectedProperty, service.getProperty());
    }

    @Test
    public void testGetPropertyWithEnvironment() {
        assertEquals(environment.getProperty("prop"), service.getProperty());
    }
}

Контекст был создан, но оба подхода закончились в NullPointerException с один разеще раз.

Ответы [ 2 ]

1 голос
/ 23 сентября 2019

Проблема с вашим тестом заключается в том, что вы пытаетесь использовать MockitoJUnitRunner.class неверным способом.

Если вы издеваетесь над Сервисом, используя @InjectMocks, вам необходимо убедиться, что вам нужно вернуть значение Service.getProperty(), посмеиваясь над вызовом службы.Если вы используете SpringRunner.class, то у вас не должно быть @InjectMocks, но должно быть @Autowired для услуги.Следующие тестовые работы.

@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceTest {
    @Autowired
    Service service;

    @Autowired
    Environment environment;

    @Value("${prop}")
    private String expectedProperty;

    @Test
    public void testGetPropertyWithValueAnnotation() {
        assertEquals(expectedProperty, service.getProperty());
    }

    @Test
    public void testGetPropertyWithEnvironment() {
        assertEquals(environment.getProperty("prop"), service.getProperty());
    }
}

0 голосов
/ 26 сентября 2019

Благодаря ответу @ shazin и некоторым собственным исследованиям я смог решить эту проблему.

По сути, должна быть совместимость между классом бегуна, указанным в @RunWith, и аннотациями для макетов Mockito.Мы хотим протестировать класс Service:

Класс обслуживания :

@Component
public class Service {
    @Autowired
    Environment environment;

    public String getProperty() {
        return environment.getProperty("prop");
    }
}

Если вы используете @RunWith(MockitoJUnitRunner.class), вы можете использовать @InjectMocksи @Mock аннотации, как показано ниже.Что бы ни было @Autowired в Service, оно будет автоматически соединено с макетами:

Тестовый класс с MockitoJUnitRunner:

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
    @InjectMocks
    Service service;
        @Mock
        Environment mockEnvironment;

    @Before
    public void before() {
        Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
    }
}

Но вы можете '• Автоматически подключать что-либо в самом тестовом классе .Для этого требуется контекст Spring (контекст Spring необходим для управления bean-компонентами, которые автоматически подключаются к объектам).Вот где @RunWith(SpringRunner.class) входит в картину.Вы можете использовать его для запуска тестового примера с выделенным контекстом Spring (вы увидите журналы тестовых примеров, показывающие, что новое приложение Spring загружается для каждого тестового класса с аннотацией @RunWith(SpringRunner.class)).Вам также необходимо предоставить подробности конфигурации с аннотацией @SpringBootTest.

Предупреждение: тестовый класс с @RunWith(SpringRunner.class) не будет понимать аннотации @InjectMocks и @Mock;вам придется использовать аннотацию @MockBean.Это эффективно изменит контекст Spring, заменив bean-компоненты их макетами;что-нибудь с аннотацией @Autowired будет автоматически подключено к фиктивным бобам автоматически:

Тестовый класс с SpringRunner:

@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
public class ServiceTest {
    @Autowired
    Service service;

    @MockBean
    Environment mockEnvironment;

    @Before
    public void before() {
        Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
    }
}

Итак ...использование @RunWith(SpringRunner.class) ничего не дало, кроме изменения имен аннотаций (@InjectMocks -> @Autowired и @Mock -> @MockBean), верно?Неправильно.Использование SpringRunner дает вам возможность автоматически подключать компоненты в вашем тестовом примере .Так что, если вы хотите использовать фактический Environment (не фиктивный), вы также можете сделать это;просто автоматически подключите его из специального контекста Spring:

Тестовый класс с SpringRunner и @Autowired Environment :

@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
public class ServiceTest {
    @Autowired
    Service service;

    @Autowired
    Environment environment;

    @Test
    public void testServiceGetProperty() {
        assertEquals(environment.getProperty("prop"), service.getProperty("prop");
    }

}

И это решает проблему.

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