2014-11-13 4 views
20

Я использую DynamoDB local для модульного тестирования. Это неплохо, но имеет некоторые недостатки. В частности:Простой локальный тест DynamoDB

  • Вы должны каким-то образом запустить сервер, прежде чем тесты запустить
  • Сервер не запускается и останавливается перед каждым испытанием так тесты становятся взаимозависимыми, если не добавить код, чтобы удалить все таблицы и т.д. после каждого испытания
  • Все разработчики должны установить ее

то, что я хочу сделать что-то вроде поставить местную банку DynamoDB и другие банки, на которых она зависит, в моем каталоге тестов/ресурсов (I Я пишу Java). Затем перед каждым тестом я запустил его, работая с -inMemory, и после теста я бы остановил его. Таким образом, любой, кто вытаскивает git repo, получает копию всего, что им нужно, чтобы запускать тесты, и каждый тест не зависит от других.

Я нашел способ сделать эту работу, но это уродливо, поэтому я ищу альтернативы. Решение, которое я имею, это поместить .zip-файл локального материала DynamoDB в test/resources, а затем в метод @Before, я извлечу его в какой-нибудь временный каталог и запустим новый Java-процесс для его выполнения. Это работает, но это некрасиво и имеет некоторые недостатки:

  • Каждый нуждается в Java исполняемый файл на их $ PATH
  • Я должен распаковать молнию на локальном диске. Использование локального диска часто бывает скучным для тестирования, особенно при непрерывных сборках и т. Д.
  • Мне нужно запустить процесс и дождаться его запуска для каждого модульного теста, а затем убить этот процесс после каждого теста. Помимо медленного, потенциал для процессов, связанных с переходом, кажется уродливым.

Кажется, что должен быть более простой способ. В конце концов, DynamoDB Local - это просто код Java. Не могу ли я каким-то образом спросить JVM о том, чтобы развить себя и заглянуть в ресурсы для создания classpath? Или, еще лучше, не могу ли я просто вызвать метод DynamoDb Local main из какого-то другого потока, так что все это происходит в одном процессе? Есть идеи?

PS: Я знаю об альтернаторе, но, похоже, имеет другие недостатки, поэтому я склонен придерживаться поддерживаемого решения Amazon, если я могу заставить его работать.

+0

Как S ay, что вы хотите написать модульные тесты - не интеграционные тесты - почему бы не использовать макет? Что-то вроде DynamoDB-mock. Это позволяет [быть инкапсулированным как библиотека] (http://ddbmock.readthedocs.org/en/latest/pages/getting_started.html#using-ddbmock-for-tests). – cheffe

+0

@ cheffe, спасибо за эту мысль. Это похоже на то, что я хочу, но это Python, а не Java, поэтому мне все равно придется выставлять внешний исполняемый файл из моих тестов так же, как я делаю с DynamoDB Local (и убедитесь, что все пользователи имеют правильную версию Python установленный, имел это на их $ PATH и т. д.). Я ищу что-то очень похожее, но на Java. Обратите внимание, что создание моего собственного макета будет огромной задачей, поскольку API Dynamo довольно богат. –

ответ

1

Для модульных испытаний на работе я использую Mockito, а затем просто издеваюсь над AmazonDynamoDBClient. затем выкрикивайте возвращаемые данные, используя когда. например:

when(mockAmazonDynamoDBClient.getItem(isA(GetItemRequest.class))).thenAnswer(new Answer<GetItemResult>() { 
     @Override 
     public GetItemResult answer(InvocationOnMock invocation) throws Throwable { 
      GetItemResult result = new GetItemResult(); 
      result.setItem(testResultItem); 
      return result; 
     } 
    }); 

не уверен, что это то, что вы ищете, но это то, как мы это делаем.

+1

Спасибо за мысль. Mocks в порядке, но может быть трудно получить протокол в точности. Таким образом, вы заканчиваете тестирование, если код работает, полагая, что Dynamo (или что-то другое) ведет себя так, как вы думаете, он ведет себя (как вы его издеваетесь), но вы не тестируете, действительно ли код работает с Dynamo. Если вы ошибаетесь в том, как работает «Динамо», ваш код и тесты делают те же предположения, что и все, но у вас есть ошибки. –

+2

Похоже, что вы делаете тесты интеграции, для чего вам не нужно много тестов. вы просто хотите убедиться, что вы можете выполнять основные операции. В основном проверьте правильность подключения. В прошлом то, что я буду делать, это развернуть локальный экземпляр в тесте. то у вас будут жестко закодированные значения, которые вы сохраняете, читаете и удаляете из своей локальной базы данных. Кроме того, что это за тесты? Я всегда рекомендую иметь единичные тесты (только одна вещь - все остальное, что я макет) и интеграционные тесты (все реально) –

+0

Теоретически издевательство - это правильный путь для модульного теста, но местный DDB может убедиться, что код является правильным в более перспективном ключе. – Cherish

12

Вы можете использовать DynamoDB Local в качестве тестовой зависимости Maven в тестовом коде, как показано в этом announcement. Вы можете запустить через HTTP:

import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; 
import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; 

final String[] localArgs = { "-inMemory" }; 
DynamoDBProxyServer server = ServerRunner.createServerFromCommandLineArgs(localArgs); 
server.start(); 
AmazonDynamoDB dynamodb = new AmazonDynamoDBClient(); 
dynamodb.setEndpoint("http://localhost:8000"); 
dynamodb.listTables(); 
server.stop(); 

Вы также можете работать в интегрированном режиме:

import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded; 

AmazonDynamoDB dynamodb = DynamoDBEmbedded.create(); 
dynamodb.listTables(); 
+1

Для первого варианта с использованием ServerRunner он запускается нормально, но как только я пытаюсь создать таблицу, я получаю 'AmazonServiceException. Обработка запроса завершилась неудачно из-за неизвестной ошибки, исключения или сбоя. (Сервис: AmazonDynamoDBv2; Код состояния: 500; Код ошибки: InternalFailure; Идентификатор запроса: ea0eff34-65e4-49d5-8ae9-3bfbfec9136e) ' – leonardoborges

+5

Для встроенной версии я получаю' NullPointerException' из SQLLite в методе 'initializeMetadataTables'. :( – leonardoborges

+0

Убедитесь, что вы предоставили полный путь к библиотекам JNI sqlite4java как часть системного свойства -Dsqlite4java.library.path =/the/path/to/sqlite/for/java/jni/libraries. –

35

Для того, чтобы использовать DynamoDBLocal вам необходимо выполнить следующие шаги.

  1. Получить Прямая DynamoDBLocal Завис
  2. Получить Native SQLite4Java зависимостей
  3. Набор sqlite4java.library.path показать собственные библиотеки

1. Получить прямой DynamoDBLocal Dependency

Это один легкий один , Вам нужен этот репозиторий, как описано в AWS Forums.

<!--Dependency:--> 
<dependencies> 
    <dependency> 
     <groupId>com.amazonaws</groupId> 
     <artifactId>DynamoDBLocal</artifactId> 
     <version>1.11.0.1</version> 
     <scope></scope> 
    </dependency> 
</dependencies> 
<!--Custom repository:--> 
<repositories> 
    <repository> 
     <id>dynamodb-local</id> 
     <name>DynamoDB Local Release Repository</name> 
     <url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url> 
    </repository> 
</repositories> 

2. Получить Native SQLite4Java зависимостей

Если вы не добавите эту зависимость, ваши тесты будут выпадать с 500 внутренней ошибки.

Во-первых, добавить эти зависимости:

<dependency> 
    <groupId>com.almworks.sqlite4java</groupId> 
    <artifactId>sqlite4java</artifactId> 
    <version>1.0.392</version> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>com.almworks.sqlite4java</groupId> 
    <artifactId>sqlite4java-win32-x86</artifactId> 
    <version>1.0.392</version> 
    <type>dll</type> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>com.almworks.sqlite4java</groupId> 
    <artifactId>sqlite4java-win32-x64</artifactId> 
    <version>1.0.392</version> 
    <type>dll</type> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>com.almworks.sqlite4java</groupId> 
    <artifactId>libsqlite4java-osx</artifactId> 
    <version>1.0.392</version> 
    <type>dylib</type> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>com.almworks.sqlite4java</groupId> 
    <artifactId>libsqlite4java-linux-i386</artifactId> 
    <version>1.0.392</version> 
    <type>so</type> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>com.almworks.sqlite4java</groupId> 
    <artifactId>libsqlite4java-linux-amd64</artifactId> 
    <version>1.0.392</version> 
    <type>so</type> 
    <scope>test</scope> 
</dependency> 

Затем добавьте этот плагин, чтобы получить собственные зависимости в определенной папке:

<build> 
    <plugins> 
     <plugin> 
      <groupId>org.apache.maven.plugins</groupId> 
      <artifactId>maven-dependency-plugin</artifactId> 
      <version>2.10</version> 
      <executions> 
       <execution> 
        <id>copy</id> 
        <phase>test-compile</phase> 
        <goals> 
         <goal>copy-dependencies</goal> 
        </goals> 
        <configuration> 
         <includeScope>test</includeScope> 
         <includeTypes>so,dll,dylib</includeTypes> 
         <outputDirectory>${project.basedir}/native-libs</outputDirectory> 
        </configuration> 
       </execution> 
      </executions> 
     </plugin> 
    </plugins> 
</build> 

3. Установить sqlite4java.library.path показать собственные библиотеки

В качестве последнего шага вам необходимо установить системное свойство sqlite4java.library.path в каталог native-libs. Это нормально сделать это непосредственно перед созданием локального сервера.

System.setProperty("sqlite4java.library.path", "native-libs"); 

После этих шагов вы можете использовать DynamoDBLocal, как хотите. Вот правило Junit, которое создает для этого локальный сервер.

import com.amazonaws.auth.BasicAWSCredentials; 
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; 
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; 
import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; 
import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; 
import org.junit.rules.ExternalResource; 

import java.io.IOException; 
import java.net.ServerSocket; 

/** 
* Creates a local DynamoDB instance for testing. 
*/ 
public class LocalDynamoDBCreationRule extends ExternalResource { 

    private DynamoDBProxyServer server; 
    private AmazonDynamoDB amazonDynamoDB; 

    public LocalDynamoDBCreationRule() { 
     // This one should be copied during test-compile time. If project's basedir does not contains a folder 
     // named 'native-libs' please try '$ mvn clean install' from command line first 
     System.setProperty("sqlite4java.library.path", "native-libs"); 
    } 

    @Override 
    protected void before() throws Throwable { 

     try { 
      final String port = getAvailablePort(); 
      this.server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", port}); 
      server.start(); 
      amazonDynamoDB = new AmazonDynamoDBClient(new BasicAWSCredentials("access", "secret")); 
      amazonDynamoDB.setEndpoint("http://localhost:" + port); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    protected void after() { 

     if (server == null) { 
      return; 
     } 

     try { 
      server.stop(); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public AmazonDynamoDB getAmazonDynamoDB() { 
     return amazonDynamoDB; 
    } 

    private String getAvailablePort() { 
     try (final ServerSocket serverSocket = new ServerSocket(0)) { 
      return String.valueOf(serverSocket.getLocalPort()); 
     } catch (IOException e) { 
      throw new RuntimeException("Available port was not found", e); 
     } 
    } 
} 

Вы можете использовать это правило, как этот

@RunWith(JUnit4.class) 
public class UserDAOImplTest { 

    @ClassRule 
    public static final LocalDynamoDBCreationRule dynamoDB = new LocalDynamoDBCreationRule(); 
} 
+2

Я обнаружил, что зависимость DynamoDBLocal автоматически добавлена ​​в sqlite4java, а дополнительные зависимости не нужно указывать вручную. –

+0

@JefferyGrajkowski Я тоже это пробовал, но я не могу заставить его работать без родных библиотек. Какова ваша локальная версия DDB? Возможно, они обновили зависимости. – bhdrkn

+1

Я использую 'com.amazonaws: DynamoDBLocal: 1. +'. Я решил, что лучше всего остановиться на последнем, потому что сама служба также собирается обновить, нравится мне это или нет. Это работает до 1.11.0 прямо сейчас. –

0

Есть несколько Node.js оберток для DynamoDB Local. Это позволяет легко выполнять модульные тесты, сочетающиеся с бегунами задач, такими как глоток или ворчание. Попробуйте dynamodb-localhost, dynamodb-local

6

Это Подтвердив ответ bhdrkn для пользователей Gradle (его основан на Maven). Это все те же три шага:

  1. Get Direct DynamoDBLocal Dependency
  2. Get Native SQLite4Java dependencies
  3. Set sqlite4java.library.path to show native libraries

1. Получить прямой DynamoDBLocal Dependency

Добавить раздел зависимостей вашего файла build.gradle ...

dependencies { testCompile "com.amazonaws:DynamoDBLocal:1.+" }

2. Get Собственные зависимости SQLite4Java

Библиотеки sqlite4java уже будут загружаться как зависимость от DynamoDBLocal, но libra ry файлы должны быть скопированы в нужное место. Добавьте в свой файл build.gradle ...

task copyNativeDeps(type: Copy) { from(configurations.compile + configurations.testCompile) { include '*.dll' include '*.dylib' include '*.so' } into 'build/libs' }

3. Установить sqlite4java.library.path показать собственные библиотеки

Мы должны сказать Gradle запустить copyNativeDeps для тестирования и сказать sqlite4java, где найти файлы. Добавить в файл build.gradle ...

test { dependsOn copyNativeDeps systemProperty "java.library.path", 'build/libs' }

+0

Это сработало для меня. Серьезно за спасение жизни! – anataliocs

+0

@Jeffery Я получаю следующие ошибки: testMapRtbUser STANDARD_ERROR 17: 39: 41.931 [DEBUG] [TestEventLogger] 2017-08-29 17: 39: 41.929: WARN: oejs.AbstractHttpConnection:/ 17: 39: 41.931 [DEBUG ] [TestEventLogger] java.lang.NoSuchMethodError: com.amazon.dynamodb.grammar.DynamoDbExpressionParser.parseAttributeValuesMapKeys (Ljava/lang/String; Lorg/antlr/v4/runtime/ANTLRErrorListener;) V Однако для того же теста, если я запустите его из Eclipse, поскольку тесты Junit работают нормально. Это только при выполнении градиентом в качестве теста, он терпит неудачу. Позже это время будет ошибкой тайм-аута операции обновления. Помогите! – Roy

+0

Это звучит как проблема пути прохождения. Этот класс и этот метод с этой подписью определенно существуют в последней версии JAR. Попробуйте очистить все, что Gradle кэшировало, и повторите попытку. –

4

Я обернул ответы выше на два JUnit rules, которые не требуют изменений в скрипте сборки, поскольку правила обрабатывают материал родной библиотеки. Я сделал это, когда обнаружил, что Idea не понравилась решения Gradle/Maven, поскольку она просто ушла и сделала свое дело anyhoos.

Это означает, что следующие шаги:

  • Получить AssortmentOfJUnitRules версию 1.5.32 или выше зависимости
  • Получить зависимость Direct DynamoDBLocal
  • Добавьте LocalDynamoDbRule или HttpDynamoDbRule к вашему JUnit тест.

Maven:

<!--Dependency:--> 
<dependencies> 
    <dependency> 
     <groupId>com.amazonaws</groupId> 
     <artifactId>DynamoDBLocal</artifactId> 
     <version>1.11.0.1</version> 
     <scope>test</scope> 
    </dependency> 
    <dependency> 
     <groupId>com.github.mlk</groupId> 
     <artifactId>assortmentofjunitrules</artifactId> 
     <version>1.5.36</version> 
     <scope>test</scope> 
    </dependency> 
</dependencies> 
<!--Custom repository:--> 
<repositories> 
    <repository> 
     <id>dynamodb-local</id> 
     <name>DynamoDB Local Release Repository</name> 
     <url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url> 
    </repository> 
</repositories> 

Gradle:

repositories { 
    mavenCentral() 

    maven { 
    url = "https://s3-us-west-2.amazonaws.com/dynamodb-local/release" 
    } 
} 

dependencies { 
    testCompile "com.github.mlk:assortmentofjunitrules:1.5.36" 
    testCompile "com.amazonaws:DynamoDBLocal:1.+" 
} 

Код:

public class LocalDynamoDbRuleTest { 
    @Rule 
    public LocalDynamoDbRule ddb = new LocalDynamoDbRule(); 

    @Test 
    public void test() { 
    doDynamoStuff(ddb.getClient()); 
    } 
} 
+0

ЭТО РАБОТАЕТ ХОРОШО. – eold

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