Как исправить сбой теста при отсутствии ModelAndView?

Этот класс находится в верхней части иерархии моих тестов:

@TestPropertySource("/test.properties")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public abstract class ApplicationAbstractTest {
}

И еще несколько тестовых классов:

@WebAppConfiguration
@ActiveProfiles("mysql")
abstract public class AbstractControllerTest extends ApplicationAbstractTest {

    protected MockMvc mockMvc;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @PostConstruct
    private void postConstruct() {
        mockMvc = MockMvcBuilders
                .webAppContextSetup(webApplicationContext)
                .apply(springSecurity())
                .build();
    }
}

JsonUserServiceTest:

@ActiveProfiles("json")
public class JsonUserServiceTest extends ApplicationAbstractTest {
    @Before
    public void setUp() throws Exception {
        ...
    }
}

КонтактКонтроллерТест:

public class ContactControllerTest extends AbstractControllerTest {
    @Test
    public void testGet() throws Exception {
        mockMvc.perform(get("/update-" + ID + "-contact")
                .with(userAuth(USER)))
//                .andExpect(status().isOk())
                .andDo(print())
                .andExpect(view().name("details"))
                .andExpect(forwardedUrl("/WEB-INF/jsp/details.jsp"));
    }
}

Итак, когда я запускаю ContactControllerTest, он выполняется успешно, и метод print показывает мне:

Handler:
             Type = com.telecom.web.ContactController
           Method = public java.lang.String com.myApp.web.ContactController.details(java.lang.Integer,org.springframework.ui.ModelMap)

Но когда я запускаю все тесты, то JsonUserServiceTest запускается первым, ContactControllerTest терпит неудачу. И print показывает:

Handler:
             Type = null
...
java.lang.AssertionError: No ModelAndView found

Что не так в конфигурации? Или как устранить неполадки?

UPD: заодно тестил так, всегда работает нормально:

public class UserControllerTest extends AbstractControllerTest {
    @Test
    public void testRegister() throws Exception {
        mockMvc.perform(get("/register"))
                .andDo(print())
                .andExpect(view().name("profile"))
                .andExpect(forwardedUrl("/WEB-INF/jsp/profile.jsp"));
    }
}

UPD: тестирую метод контроллера:

@GetMapping("/update-{id}-contact")
public String details(@PathVariable Integer id, ModelMap model) {
    Integer userId = AuthorizedUser.id();
    LOG.info("get contact {} for User {}", id, userId);
    Contact contact = service.get(id, userId);
    model.addAttribute("contact", contact);
    return "details";
}

У меня тоже есть такая фасоль:

@Bean
public InternalResourceViewResolver viewResolver() {
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setViewClass(JstlView.class);
    viewResolver.setPrefix("/WEB-INF/jsp/");
    viewResolver.setSuffix(".jsp");

    return viewResolver;
}

UPD: Пробовал настроить mockMvc в отдельном классе:

@Configuration
public class TestConfig {

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Bean
    public MockMvc mockMvc() {
        return MockMvcBuilders
                .webAppContextSetup(webApplicationContext)
                .apply(springSecurity())
                .build();
    }
}

И добавил сюда:

@WebAppConfiguration
@ContextConfiguration(classes = {TestConfig.class})
@ActiveProfiles("mysql")
abstract public class AbstractControllerTest extends ApplicationAbstractTest {

но я получил:

java.lang.IllegalStateException: springSecurityFilterChain не может быть нулевым. Убедитесь, что Bean с именем springSecurityFilterChain, реализующий фильтр, присутствует или внедрите фильтр, который будет использоваться.


person Pavlo Plynko    schedule 26.03.2017    source источник
comment
Что произойдет, если вы удалите @ActiveProfiles(json) из JsonUserServiceTest и запустите костюм. Работает ли ContactControllerTest?   -  person johncena    schedule 27.03.2017
comment
Да, ContactControllerTest в этом случае работает (но JsonUserServiceTest, как и ожидалось, не работает).   -  person Pavlo Plynko    schedule 27.03.2017
comment
@ActiveProfiles принимает массив. Что произойдет, если вы дадите ему @ActiveProfiles({ "json", "mysql" })?   -  person Roddy of the Frozen Peas    schedule 27.03.2017
comment
Это дает NoUniqueBeanDefinitionException, так как у меня есть две реализации репозитория, помеченные разными профилями: @Profile("json") и @Profile("mysql")   -  person Pavlo Plynko    schedule 27.03.2017


Ответы (2)


Сообщение WARN не приводит к сбою тестов. Он просто говорит, что фабрика Entity manager зарегистрирована дважды. Это будет проблемой только в том случае, если вы кластеризуете свое приложение, используя ту же фабрику Entity Manager. Для запуска тестового примера это не повод для беспокойства.

Основная причина сбоя тестового примера находится в этих двух строках

 .andExpect(view().name("details"))
 .andExpect(forwardedUrl("/WEB-INF/jsp/details.jsp"));

Пожалуйста, проверьте, есть ли в проекте представление с именем "details" и пересылаемый URL-адрес "/WEB-INF/jsp/details.jsp"

Обновить

Не могли бы вы попробовать это

@Configuration
public class TestConfig {


@Autowired
private Filter springSecurityFilterChain;

@Autowired
private WebApplicationContext webApplicationContext;

@Bean
public MockMvc mockMvc() {
    return MockMvcBuilders
            .webAppContextSetup(webApplicationContext)
            .apply(springSecurityFilterChain)
            .build();
}
}
person Abdul Rahman    schedule 29.03.2017
comment
Но как это проходит, когда я запускаю его автономно (или с другими тестами с профилем mysql)? Я обновил вопрос методом, который я тестирую. - person Pavlo Plynko; 29.03.2017

Создайте файл конфигурации, который будет инициализировать фиктивные объекты для ваших тестовых случаев. И поставить на все тестовые классы.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestConfig.class})

Он инициализирует все ваши имитирующие объекты только один раз, после чего кэшируется и повторно используется для всех тестовых случаев.

Или, если вы не хотите использовать фиктивную конфигурацию, вы можете напрямую передать фактическую конфигурацию приложения в ContextConfiguration, как показано ниже.

Для конфигурации приложения на основе аннотаций (здесь AppConfig и AppConfig2 — ваш класс конфигурации)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class, AppConfig2.class})

Для конфигурации приложения на основе xml (здесь appConfig.xml и appConfig2.xml — ваши файлы конфигурации)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:pathTo/appConfig.xml","classpath:pathTo/appConfig2.xml"})

Ссылка: Пример интеграции JUnit + Spring

person Anil Agrawal    schedule 29.03.2017
comment
Может подскажете как правильно сделать? Я обновил вопрос с примером моей попытки. - person Pavlo Plynko; 29.03.2017
comment
Наконец я настроил TestConfig.class и поставил @ContextConfiguration(classes = {TestConfig.class}), и получаю ту же ошибку: No ModelAndView found - person Pavlo Plynko; 29.03.2017
comment
Не могли бы вы уточнить, как инициализировалось ваше приложение? Означает, что вы используете конфигурацию на основе xml или конфигурацию на основе аннотаций. Допустим, вы используете конфигурацию на основе аннотаций и инициализировали свое веб-приложение в WebConfig.java, после чего вы можете напрямую передать WebConfig.class в качестве аргумента в ContextConfiguration. Если вы используете конфигурацию на основе xml, вы можете напрямую передать имя xml в качестве аргумента в ContextConfiguration. - person Anil Agrawal; 29.03.2017