2015-04-09 4 views
12

У меня есть веб-сервер Spring, который по запросу делает внешний вызов стороннему веб-API (например, retreive Facebook oauth token). После получения данных из этого вызова вычисляет ответ:Mock внешний сервер во время тестирования интеграции с Spring

@RestController 
public class HelloController { 
    @RequestMapping("/hello_to_facebook") 
    public String hello_to_facebook() { 
     // Ask facebook about something 
     HttpGet httpget = new HttpGet(buildURI("https", "graph.facebook.com", "/oauth/access_token")); 
     String response = httpClient.execute(httpget).getEntity().toString(); 
     // .. Do something with a response 
     return response; 
    } 
} 

Я пишу интеграционный тест, который проверяет, что удар URL на моем сервере приводит к некоторому ожидаемому результату. Однако я хочу издеваться над внешним сервером локально, так что мне даже не нужен интернет-доступ, чтобы проверить все это. Каков наилучший способ сделать это?

Я новичок весной, это то, что у меня есть до сих пор.

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = Application.class) 
@WebAppConfiguration 
@IntegrationTest({}) 
public class TestHelloControllerIT {   
    @Test 
    public void getHelloToFacebook() throws Exception { 
     String url = new URL("http://localhost:8080/hello_to_facebook").toString(); 
     //Somehow setup facebook server mock ... 
     //FaceBookServerMock facebookMock = ... 

     RestTemplate template = new TestRestTemplate(); 
     ResponseEntity<String> response = template.getForEntity(url, String.class); 
     assertThat(response.getBody(), equalTo("...")); 

     //Assert that facebook mock got called 
     //facebookMock.verify(); 
    } 
} 

Фактические реальная создана более сложный - я делаю Facebook OAuth логин и все, что логика не в контроллере, но в различных объектах Spring Security. Однако я подозреваю, что код тестирования должен быть таким же, поскольку я просто нажимаю URL-адреса и ожидаю ответа, не так ли?

ответ

4

Поиграв немного с различными сценариями, вот один из способов, как можно достичь того, что было предложено с минимальным вмешательством в основной код

  1. Refactor контроллера использовать параметр для третьего стороны адреса сервера:

    @RestController 
    public class HelloController { 
        @Value("${api_host}") 
        private String apiHost; 
    
        @RequestMapping("/hello_to_facebook") 
        public String hello_to_facebook() { 
         // Ask facebook about something 
         HttpGet httpget = new HttpGet(buildURI("http", this.apiHost, "/oauth/access_token")); 
         String response = httpClient.execute(httpget).getEntity().toString(); 
         // .. Do something with a response 
         return response + "_PROCESSED"; 
        } 
    } 
    

'api_host' равно 'graph.facebook.com' в application.properties в SRC/основные/ресурсов

  1. Создайте новый контроллер в папке src/test/java, который высмеивает сервер третьей стороны.

  2. Переопределить «api_host» для тестирования на «localhost».

Вот код для шагов 2 и 3 в одном файле для краткости:

@RestController 
class FacebookMockController { 
    @RequestMapping("/oauth/access_token") 
    public String oauthToken() { 
     return "TEST_TOKEN"; 
    } 
} 

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = Application.class) 
@WebAppConfiguration 
@IntegrationTest({"api_host=localhost",}) 
public class TestHelloControllerIT {   
    @Test 
    public void getHelloToFacebook() throws Exception { 
     String url = new URL("http://localhost:8080/hello_to_facebook").toString(); 
     RestTemplate template = new TestRestTemplate(); 
     ResponseEntity<String> response = template.getForEntity(url, String.class); 
     assertThat(response.getBody(), equalTo("TEST_TOKEN_PROCESSED")); 

     // Assert that facebook mock got called: 
     // for example add flag to mock, get the mock bean, check the flag 
    } 
} 

Есть ли лучше способ сделать это? Все отзывы приветствуются!

P.S.Вот некоторые осложнения, с которыми я столкнулся положить этот ответ в более реалистичном приложение:

  1. Eclipse, смешивает тест и основную конфигурацию в пути к классам, чтобы вы могли задрать основную конфигурацию тестовых классов и параметров: https://issuetracker.springsource.com/browse/STS-3882 Использование Gradle bootRun, чтобы избежать его

  2. Вы должны открыть доступ к вашим издеваемым ссылкам в конфиге безопасности, если у вас установлена ​​защита от пружины. Для добавления к конфигурации безопасности, а не возиться с основной конфигурацией конфигурацией:

    @Configuration 
    @Order(1) 
    class TestWebSecurityConfig extends WebSecurityConfig { 
        @Override 
        protected void configure(HttpSecurity http) throws Exception { 
         http 
          .authorizeRequests() 
           .antMatchers("/oauth/access_token").permitAll(); 
         super.configure(http); 
        } 
    } 
    
  3. Это не просто ударить по протоколу HTTPS ссылки в интеграционных тестах. В конечном итоге я использую TestRestTemplate с настраиваемым запросом и настроенным SSLConnectionSocketFactory.

0

У вас может быть другой файл конфигурации весны, который предоставляет ту же конечную точку, что и класс HelloController. Затем вы можете просто вернуть законченный ответ json.

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

Если вы тестируете, что данные, возвращаемые с facebook, каким-то образом мутируются, и вы хотите убедиться, что работа над ним выполнена правильно, вы можете сделать эту работу отдельным методом, который взял ответ facebook как параметр, а затем выполнил мутацию. Затем вы можете проверить на основе различных json-входов, что он работает правильно.

Вы можете протестировать, не введя в него веб-сервис.

+0

Я пришел с этим кодом, чтобы понять, как написать более сложный интеграционный тест. Фактическая система, которую я хочу протестировать, а затем рефакторинг, состоит из нескольких модулей безопасности Spring, которые работают, чтобы обеспечить oauth2 с помощью facebook. Все это делается с защищенной архитектурой REST без состояния, которая имеет свои собственные причуды. В конечном счете, я хочу протестировать эту сложную систему - есть несколько шагов перед вызовом facebook и несколько после. Этот вопрос с контроллером - это самый простой явный первый шаг, о котором я могу сейчас думать, и опубликовать всю систему, чтобы полностью ответить на него невозможно. – otognan

+0

Можно ли сказать, что вы хотите частично проверить поведение своего приложения, когда весенняя безопасность имеет ошибки аутентификации? –

+0

У меня уже есть рабочий прототип, но его уродливый. У меня нет давления на этот проект, поэтому я хочу написать несколько тестов, которые обеспечат хорошую работу системы, в то время как я буду делать большой рефакторинг. Так что да, частично я хочу протестировать проверку подлинности и другие режимы отказа, но в основном я хочу быть уверенным, что я сохраняю работоспособность во время рефакторинга. – otognan

Смежные вопросы