2015-07-22 6 views
14

Ich habe eine ziemlich einfache Spring-Boot-Anwendung, die eine kleine REST-API verfügbar macht und Daten von einer Instanz von MongoDB abruft. Abfragen an die MongoDB-Instanz durchlaufen ein auf Spring Data basierendes Repository. Einige Schlüsselbits unten.Wie konfigurieren Sie Embedded MongDB für Integrationstests in einer Spring Boot-Anwendung?

// Main application class 
@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) 
@ComponentScan 
@Import(MongoConfig.class) 
public class ProductApplication { 
    public static void main(String[] args) { 
     SpringApplication.run(ProductApplication.class, args); 
    } 
} 
// Product repository with Spring data 
public interface ProductRepository extends MongoRepository<Product, String> { 

    Page<Product> findAll(Pageable pageable); 

    Optional<Product> findByLineNumber(String lineNumber); 
} 
// Configuration for "live" connections 
@Configuration 
public class MongoConfig { 

    @Value("${product.mongo.host}") 
    private String mongoHost; 

    @Value("${product.mongo.port}") 
    private String mongoPort; 

    @Value("${product.mongo.database}") 
    private String mongoDatabase; 

    @Bean(name="mongoClient") 
    public MongoClient mongoClient() throws IOException { 
     return new MongoClient(mongoHost, Integer.parseInt(mongoPort)); 
    } 

    @Autowired 
    @Bean(name="mongoDbFactory") 
    public MongoDbFactory mongoDbFactory(MongoClient mongoClient) { 
     return new SimpleMongoDbFactory(mongoClient, mongoDatabase); 
    } 

    @Autowired 
    @Bean(name="mongoTemplate") 
    public MongoTemplate mongoTemplate(MongoClient mongoClient) { 
     return new MongoTemplate(mongoClient, mongoDatabase); 
    } 
} 
@Configuration 
@EnableMongoRepositories 
public class EmbeddedMongoConfig { 

    private static final String DB_NAME = "integrationTest"; 
    private static final int DB_PORT = 12345; 
    private static final String DB_HOST = "localhost"; 
    private static final String DB_COLLECTION = "products"; 

    private MongodExecutable mongodExecutable = null; 

    @Bean(name="mongoClient") 
    public MongoClient mongoClient() throws IOException { 
     // Lots of calls here to de.flapdoodle.embed.mongo code base to 
     // create an embedded db and insert some JSON data 
    } 

    @Autowired 
    @Bean(name="mongoDbFactory") 
    public MongoDbFactory mongoDbFactory(MongoClient mongoClient) { 
     return new SimpleMongoDbFactory(mongoClient, DB_NAME); 
    } 

    @Autowired 
    @Bean(name="mongoTemplate") 
    public MongoTemplate mongoTemplate(MongoClient mongoClient) { 
     return new MongoTemplate(mongoClient, DB_NAME); 
    } 

    @PreDestroy 
    public void shutdownEmbeddedMongoDB() { 
     if (this.mongodExecutable != null) { 
      this.mongodExecutable.stop(); 
     } 
    } 
} 
@RunWith(SpringJUnit4ClassRunner.class) 
@SpringApplicationConfiguration(classes = TestProductApplication.class) 
@IntegrationTest 
@WebAppConfiguration 
public class WtrProductApplicationTests { 

    @Test 
    public void contextLoads() { 
     // Tests empty for now 
    } 

} 
@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) 
@ComponentScan 
@Import(EmbeddedMongoConfig.class) 
public class TestProductApplication { 

    public static void main(String[] args) { 
     SpringApplication.run(TestProductApplication.class, args); 
    } 
} 

Also hier die Idee ist es, die Integrationstests (leer im Moment) haben, um die eingebetteten Mongo Instanz verbinden und nicht die "live" ein. Es funktioniert jedoch nicht. Ich kann die Tests sehen, die mit der "Live" -Instanz von Mongo verbunden sind, und wenn ich das schließe, scheitert der Build einfach, da er immer noch versucht, sich mit der Live-Instanz von Mongo zu verbinden. Weiß jemand, warum das so ist? Wie bekomme ich die Tests zur Verbindung mit der eingebetteten Instanz?

+3

Ehrlich gesagt ich habe mit Anmerkungen und Einstellungen bastelt den ganzen Tag ohne Glück. Eine Spring Boot-Anwendung so zu modifizieren, dass sie testbar ist, scheint dem Ziehen von Zähnen ähnlich zu sein. – Jon

Antwort

17

Das funktioniert für mich.

Prüfklasse:

@RunWith(SpringJUnit4ClassRunner.class) 
    @SpringApplicationConfiguration(classes = { 
     Application.class, 
     TestMongoConfig.class // <--- Don't forget THIS 
    }) 
    public class GameRepositoryTest { 

     @Autowired 
     private GameRepository gameRepository; 

     @Test 
     public void shouldCreateGame() { 
      Game game = new Game(null, "Far Cry 3"); 
      Game gameCreated = gameRepository.save(game); 
      assertEquals(gameCreated.getGameId(), gameCreated.getGameId()); 
      assertEquals(game.getName(), gameCreated.getName()); 
     } 

    } 

Einfache MongoDB-Repository:

public interface GameRepository extends MongoRepository<Game, String>  { 

    Game findByName(String name); 
} 

MongoDB Testkonfiguration:

import com.mongodb.Mongo; 
import com.mongodb.MongoClientOptions; 
import de.flapdoodle.embed.mongo.MongodExecutable; 
import de.flapdoodle.embed.mongo.MongodProcess; 
import de.flapdoodle.embed.mongo.MongodStarter; 
import de.flapdoodle.embed.mongo.config.IMongodConfig; 
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; 
import de.flapdoodle.embed.mongo.config.Net; 
import de.flapdoodle.embed.mongo.distribution.Version; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.autoconfigure.mongo.MongoProperties; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 

import java.io.IOException; 

@Configuration 
public class TestMongoConfig { 

    @Autowired 
    private MongoProperties properties; 

    @Autowired(required = false) 
    private MongoClientOptions options; 

    @Bean(destroyMethod = "close") 
    public Mongo mongo(MongodProcess mongodProcess) throws IOException { 
     Net net = mongodProcess.getConfig().net(); 
     properties.setHost(net.getServerAddress().getHostName()); 
     properties.setPort(net.getPort()); 
     return properties.createMongoClient(this.options); 
    } 

    @Bean(destroyMethod = "stop") 
    public MongodProcess mongodProcess(MongodExecutable mongodExecutable) throws IOException { 
     return mongodExecutable.start(); 
    } 

    @Bean(destroyMethod = "stop") 
    public MongodExecutable mongodExecutable(MongodStarter mongodStarter, IMongodConfig iMongodConfig) throws IOException { 
     return mongodStarter.prepare(iMongodConfig); 
    } 

    @Bean 
    public IMongodConfig mongodConfig() throws IOException { 
     return new MongodConfigBuilder().version(Version.Main.PRODUCTION).build(); 
    } 

    @Bean 
    public MongodStarter mongodStarter() { 
     return MongodStarter.getDefaultInstance(); 
    } 

} 

pom.xml

 <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-data-mongodb</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>de.flapdoodle.embed</groupId> 
      <artifactId>de.flapdoodle.embed.mongo</artifactId> 
      <version>1.48.0</version> 
      <scope>test</scope> 
     </dependency> 
+1

Das fehlende Bit war der letzte Teil von: @SpringApplicationConfiguration (classes = {Application.class, TestMongoConfig.class}) - I didn ' Ich weiß, dass Sie die Konfiguration auch in der Annotation benötigt haben, um sie abzurufen. – Jon

+3

Ich habe Ihre Implementierung verfolgt, aber es funktioniert nicht für mich, wenn ich im POM spezifiziere, dass der Bereich Test ist. Beim Erstellen der App erhalte ich den Fehler "verursacht durch : java.lang.ClassNotFoundException: de.flapdoodle.embed.mongo.distribution.IFeatureAwareVersion "und wenn ich den Scope-Test vom POM entfernte, erhalte ich den folgenden Fehler" NoSuchBeanDefinitionException: Kein Bean namens 'embeddedMongoServer' ist definiert " – naoru

+0

Ich bin Fehler beim Erstellen des Beans mit dem Namen 'testMongoConfig', Kein berechtigender Bean-Fehler. –

3

Stellen Sie sicher, dass Sie explizit mit Ihrer @ComponentScan sind. Standardmäßig sind

Wenn bestimmte Pakete nicht definiert sind, erfolgt das Scannen aus dem Paket der Klasse mit dieser Anmerkung. (@ComponentScan Javadoc)

Deshalb, wenn Sie Ihre TestProductApplication und ProductApplication Konfigurationen sind beide im selben Paket ist es möglich, Frühling Komponente-Scannen Ihre ProductApplication Konfiguration und Verwendung.

Zusätzlich würde ich empfehlen, Ihre Testmongobohnen in ein "Test" oder "Lokales" Profil zu setzen und die @ActiveProfiles Annotation in Ihrer Testklasse zu verwenden, um das Test/lokale Profil zu aktivieren.

+0

Die aktive Profile Annotation ist wahrscheinlich eine gute Idee. Ich komme darauf. – Jon

22

Seit Spring Boot Version 1.3 gibt es eine EmbeddedMongoAutoConfiguration Klasse, die aus der Box kommt. Dies bedeutet, dass Sie keine Konfigurationsdatei erstellen müssen und wenn Sie Dinge ändern möchten, die Sie noch können.

Auto-Konfiguration für Embedded MongoDB wurde hinzugefügt. Eine Abhängigkeit von de.flapdoodle.embed: de.flapdoodle.embed.mongo ist alles, was notwendig ist, um loszulegen. Die Konfiguration, z. B. die zu verwendende Version von Mongo, kann über application.properties gesteuert werden. Weitere Informationen finden Sie in der Dokumentation.(Spring Boot Release Notes)

Die grundlegendste und wichtige Konfiguration, die zu den application.properties Dateien hinzugefügt werden muss, ist
spring.data.mongodb.port=0 (0 bedeutet, dass es zufällig aus dem Freien ausgewählt wird)

für mehr Details prüfen: Spring Boot Docs MongoDb

+1

Danke. Der spring.data.mongodb.port = 0 ist wichtig - ohne ihn versagen einige Tests zufällig, wenn viele von ihnen zusammen laufen. – Marwin

+0

Dies funktioniert leider nicht in Boot 1.5 –

+0

Es funktioniert immer noch. Für Spring Boot 1.5 hat sich in Sachen Embedded Mongo Configuration zum Testen nichts geändert. nur stellen Sie sicher, dass Sie die richtige Version von plappdoodle.embed.mongo hinzufügen, die der Version des Mongo-Treibers entspricht – magiccrafter

4

ich vorherige Antwort

pom.xml vervollständigen werde

<parent> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-parent</artifactId> 
    <version>1.3.2.RELEASE</version> 
</parent> 
... 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-data-mongodb</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>de.flapdoodle.embed</groupId> 
     <artifactId>de.flapdoodle.embed.mongo</artifactId> 
     <version>${embedded-mongo.version}</version> 
    </dependency> 

MongoConfig

@Configuration 
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class }) 
public class MongoConfig{ 
} 
+0

Das ist, was ich in meiner Mongo-Konfiguration brauchte, um es zu beenden, meine Tests für Swagger2 zu töten (die nachfolgenden Tests würden nicht richtig starten) Die oben genannten Punkte haben mich denken, das endete für mich. Spring Version 4.x Spring autoconfigure 1.3.2 '@EnableAutoConfiguration (ausschließen = {EmbeddedMongoAutoConfiguration.class' – ken

4

In Version 1.5.7 Verwendung nur dieses:

@RunWith(SpringRunner.class) 
@DataMongoTest 
public class UserRepositoryTests { 

    @Autowired 
    UserRepository repository; 

    @Before 
    public void setUp() { 

     User user = new User(); 

     user.setName("test"); 
     repository.save(user); 
    } 

    @Test 
    public void findByName() { 
     List<User> result = repository.findByName("test"); 
     assertThat(result).hasSize(1).extracting("name").contains("test"); 
    } 

} 

Und

 <dependency> 
      <groupId>de.flapdoodle.embed</groupId> 
      <artifactId>de.flapdoodle.embed.mongo</artifactId> 
     </dependency> 
+0

Wenn Sie einen Controller testen möchten, der auf embedded mongo db zugreift? – sintetico82

+0

Sie können einfach eine Instanz von' MongoClient' injizieren, wie Sie es auch tun würden mit einer echten MongoDB, auch 'MongoTemplate'. – bigZee77

Verwandte Themen