Интеграционные тесты для Google App Engine (java)

Я пытаюсь разработать некоторые эффективные интеграционные тесты для моего приложения GAE/j. Я знаком с https://developers.google.com/appengine/docs/java/tools/localunittesting - эти инструменты отлично подходят для небольших модульных тестов. Теперь я заинтересован в разработке интеграционных тестов, которые проверяют фактические веб-запросы. Например, я хотел бы проверить, что web.xml сопоставляет сервлеты и фильтры с ожидаемыми URL-адресами и проверяет, что мои JSP генерируют то, что я ожидаю.

Моя цель состояла в том, чтобы открыть локальный сервер разработки внутри JVM, от которого я мог бы запускать запросы. Однако я открыт для других стратегий интеграции; как я сказал выше, я просто хочу эффективно тестировать генерацию JSP и другие функции уровня запроса.

Мне удалось использовать DevAppServerFactory для запуска сервера разработки в той же JVM. Однако, похоже, что созданный DevAppServer использует отдельный загрузчик классов от основной JVM. Это делает тестирование намного более сложным - я не могу использовать какие-либо локальные классы Unitesting Local * TestConfig для управления поведением этого сервера. Точно так же я не могу "свернуть свои собственные" крючки для изменения поведения с помощью, например, statics, так как статика, которую я могу мутировать в тестовом жгуте, не является той же статистикой, на которую смотрит DevAppServer. Это делает сложным пропустить функции, не являющиеся центральными для текущего теста (например, для входа в систему), для ввода отказов, для приведения макетов и т.д. Это действительно ограничивает, насколько полностью и эффективно я могу проверить свой код.

Я нашел реальную нехватку документации в Интернете для тестирования интеграции с App Engine. Я уверен, что кто-то сделал это раньше... есть ли какие-либо советы или ресурсы, которые вы можете поделиться?

Ответ 1

В принципе, вам нужно сделать две вещи:

  • Добавьте два сервлета (или что-то еще), , которые должны быть включены только во время тестирования, что позволяет удаленно вызывать установку и отключение помощника.
  • Сделайте ваш сервлет-механизм подачей запросов полностью однопоточным способом. Это необходимо, потому что по какой-то причине класс Helper Google действует только в текущем потоке.

Ответ 2

Я согласен, что этот вопрос плохо документирован.
Мне удалось написать сквозной тест, который запускает сервер как черный ящик и отправляет ему HTTP-запросы.

Он работает следующим образом:

package com.project.org;

import static org.junit.Assert.assertEquals;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
import com.google.appengine.tools.development.testing.BaseDevAppServerTestConfig;
import com.google.appengine.tools.development.testing.DevAppServerTest;
import com.google.appengine.tools.development.testing.DevAppServerTestRunner;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;

@RunWith(DevAppServerTestRunner.class)
@DevAppServerTest(HelloWorldTest.TestConfig.class)
public class HelloWorldTest {

  public class TestConfig extends BaseDevAppServerTestConfig {

    @Override public File getSdkRoot() {
      // You may need to tweak this.
      return new File("../../appengine-java-sdk-1.9.15");
    }

    @Override public File getAppDir() {
      return new File("war");
    }

    @Override
    public List<URL> getClasspath() {
      // There may be an easier way to do this.
      List<URL> classPath = new ArrayList<>();
      try {
        String separator = System.getProperty("path.separator");
        String[] pathElements = System.getProperty("java.class.path").split(separator);
        for (String pathElement : pathElements) {
          classPath.add(new File(pathElement).toURI().toURL());
        }
      }
      catch (MalformedURLException e) {
        throw new RuntimeException(e);
      }
      return classPath;
    }
  }

  private final LocalServiceTestHelper testHelper;
  private final String port;

  public HelloWorldTest() {
    testHelper = new LocalServiceTestHelper();
    port = System.getProperty("appengine.devappserver.test.port");
  }

  @Before public void setUpServer() {
    testHelper.setUp();
  }

  @After public void tearDown() {
    testHelper.tearDown();
  }

  @Test public void testHelloWorld() throws Exception {
    URL url = new URL("http://localhost:" + port + "/hello");
    HTTPResponse response = URLFetchServiceFactory.getURLFetchService().fetch(url);
    assertEquals(200, response.getResponseCode());
    assertEquals("Hello world!", new String(response.getContent(), "UTF-8"));
  }
}

Теперь проблема заключается в том, что если у вас есть два теста, каждый из которых проходит отдельно, вы не можете запускать их в одном и том же двоичном файле. Исключено в строке 37 этот файл:

IllegalStateException ( "Dev Appserver уже запущен".)

Не знаю, как это исправить.