Error creating mongobee bean on springboot app (on run of tests) - java

I use all lasts versions (fix others problems)
mongobee 0.13
fongo 2.2.0-RC3-SNAPSHOT
mongodb-driver 3.8.2
When I run my app, I have this error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mongobeeStandalone' defined in class path resource [com/myproject/company/configuration/MongoConfiguration.class]: Invocation of init method failed; nested exception is java.lang.AbstractMethodError: com.mongodb.client.internal.FongoOperationExecutor.execute(Lcom/mongodb/operation/ReadOperation;Lcom/mongodb/ReadPreference;Lcom/mongodb/ReadConcern;)Ljava/lang/Object; ... Caused by: java.lang.AbstractMethodError: com.mongodb.client.internal.FongoOperationExecutor.execute(Lcom/mongodb/operation/ReadOperation;Lcom/mongodb/ReadPreference;Lcom/mongodb/ReadConcern;)Ljava/lang/Object;
My class:
#Bean
public Mongobee mongobeeStandalone(final MongoTemplate mongoTemplate, final MongoClient mongoClient) {
final Mongobee runner = new Mongobee(mongoClient);
runner.setChangeLogsScanPackage(Changelog1.class.getPackage().getName()); // package to scan for changesets
runner.setDbName(mongoTemplate.getDb().getName());
return runner;
}
#Bean
public MongoClient mongoClient(final MongoDbFactory mongoDbFactory) {
final Mongo mongo = mongoDbFactory.getLegacyDb().getMongo();
if (!MongoClient.class.isInstance(mongo)) {
throw new UnsupportedOperationException("Must be a MongoClient");
}
return MongoClient.class.cast(mongo);
}
EDIT
My test configuration:
#Configuration
public class FoncgoConfiguration extends AbstractMongoConfiguration {
#Bean
public Fongo fongo() {
return new Fongo("mongo-test");
}
#Override
#Bean
public MongoTemplate mongoTemplate() throws Exception {
final SimpleMongoDbFactory simpleMongoDbFactory = new SimpleMongoDbFactory(mongoClient(), getDatabaseName());
final MongoTemplate mongoTemplate = new MongoTemplate(simpleMongoDbFactory, mappingMongoConverter());
return mongoTemplate;
}
#Override
#Bean
public MongoClient mongoClient() {
return MockMongoClient.create(fongo());
}
}

You should use Spring Data Mongo, it will create and inject com.mongodb.MongoClient bean. So remove mongoClient() method and add the Mongodb Spring Data dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
Spring Boot also provides a MongoTemplate bean and auto-configures it. For testing you should use Embedded Mongo with #DataMongoTest annotation, because it configures a MongoTemplate. See here for test examples.

Related

How to make the junit tests use the embedded mongoDB in a springboot application?

I am learning springboot and have created a simple springboot application. I want it to use the embedded mongoDB when it runs the unit tests and the external mongoDB for the rest of the application. However it uses the external mongoDB for the unit tests instead of the embedded one.
I have following two dependencies in my POM.
<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>
<scope>test</scope>
</dependency>
my properties file has the following:
# MongoDB properties
mongo.db.name=person_testDB
mongo.db.url=localhost
#external Mongo url
spring.data.mongodb.uri=mongodb://localhost:27017/personDB
I have a config file(MongoDBConfig.java) which includes embedded MongoDB configurations:
#EnableMongoRepositories
public class MongoDBConfig {
#Value("${mongo.db.url}")
private String MONGO_DB_URL;
#Value("${mongo.db.name}")
private String MONGO_DB_NAME;
#Bean
public MongoTemplate mongoTemplate() {
MongoClient mongoClient = new MongoClient(MONGO_DB_URL);
MongoTemplate mongoTemplate = new MongoTemplate(mongoClient, MONGO_DB_NAME);
return mongoTemplate;
}
}
Following is my PersonService.java class:
#Service
public class PersonService {
private static final Logger logger = LoggerFactory.getLogger(PersonService.class);
#Autowired
MongoTemplate mongoTemplate;
public void savePerson(Person person) {
String name = person.getName();
String collectionName = name.substring(0, 1);
mongoTemplate.save(person, collectionName);
}
}
My unit test for the PersonsService class is as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MongoDBConfig.class})
#SpringBootTest(classes = PersonService.class)
#DataMongoTest
public class PersonServiceTest {
#Autowired
PersonService personService;
MongodForTestsFactory factory;
MongoClient mongo;
#Before
public void setup() throws Exception {
factory = MongodForTestsFactory.with(Version.Main.PRODUCTION);
mongo = factory.newMongo();
}
#After
public void teardown() throws Exception {
if (factory != null)
factory.shutdown();
}
#Test
public void testSave(){
Person person = new Person("Bob Smith " , 25);
personService.savePerson(person);
}
}
It creates the collection name and the document name correctly in the external MongoDB which is not what I want. How can I restrict the unitTests to an embedded Mongo?
An alternative would be to run entire spring boot application in test. In this case your spring boot application will be discovered automatically and embedded mongoDB will be downloaded and started by Spring Boot
#RunWith(SpringRunner.class)
#SpringBootTest
public class YourSpringBootApplicationTests {
08:12:14.676 INFO EmbeddedMongo:42 - note: noprealloc may hurt
performance in many applications 08:12:14.694 INFO EmbeddedMongo:42 -
2017-12-31T08:12:14.693+0200 I CONTROL [initandlisten] MongoDB
starting : pid=2246 port=52299 08:12:22.005 INFO connection:71 -
Opened connection [connectionId{localValue:2, serverValue:2}] to
localhost:52299
In case of your example you might modify the code in order to start embedded Mongo on different port:
add test/resoures/test.properties file in order to override properties from application.properties
mongo.db.name=person_testDB
mongo.db.url=localhost
mongo.db.port=12345
modify MongoDBConfig: add MONGO_DB_PORT field
#EnableMongoRepositories
public class MongoDBConfig {
#Value("${mongo.db.url}")
private String MONGO_DB_URL;
#Value(("${mongo.db.port:27017}"))
private int MONGO_DB_PORT;
#Value("${mongo.db.name}")
private String MONGO_DB_NAME;
#Bean
public MongoTemplate mongoTemplate() {
MongoClient mongoClient = new MongoClient(MONGO_DB_URL, MONGO_DB_PORT);
MongoTemplate mongoTemplate = new MongoTemplate(mongoClient, MONGO_DB_NAME);
return mongoTemplate;
}
}
modify test class:
remove #DataMongoTest annotation. This annotation forces starting embedded mongoDB instance
static MongodExecutable mongodExecutable;
#BeforeClass
public static void setup() throws Exception {
MongodStarter starter = MongodStarter.getDefaultInstance();
String bindIp = "localhost";
int port = 12345;
IMongodConfig mongodConfig = new MongodConfigBuilder()
.version(Version.Main.PRODUCTION)
.net(new Net(bindIp, port, Network.localhostIsIPv6()))
.build();
mongodExecutable = null;
try {
mongodExecutable = starter.prepare(mongodConfig);
mongodExecutable.start();
} catch (Exception e){
// log exception here
if (mongodExecutable != null)
mongodExecutable.stop();
}
}
#AfterClass
public static void teardown() throws Exception {
if (mongodExecutable != null)
mongodExecutable.stop();
}
One more way is to use MongoRepository and init embedded Mongo as part of test #Configuration class: it's outlined here: How do you configure Embedded MongDB for integration testing in a Spring Boot application?

How to I configure my own GraphDatabaseService and GraphAwareRuntime in a spring unit test with boot 2.0 and Neo4j SDN5

I'm writing some unit tests and want to use TimeTree along with Spring repositories, to auto attach event nodes to a time tree. Something like this issue, but I'm using boot 2.0 and SDN5. I think my main issue is I don't know how to set up the configuration so my repositories and my TimeTree use the same GraphDatabaseService. My #Confuration is like this:
#Configuration
public class SpringConfig {
#Bean
public SessionFactory sessionFactory() {
// with domain entity base package(s)
return new SessionFactory(configuration(), "org.neo4j.boot.test.domain");
}
#Bean
public org.neo4j.ogm.config.Configuration configuration() {
return new org.neo4j.ogm.config.Configuration.Builder()
.uri("bolt://localhost")
.build();
}
#Bean
public Session getSession() {
return sessionFactory().openSession();
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory()
.newEmbeddedDatabase(new File("/tmp/graphDb"));
}
#Bean
public GraphAwareRuntime graphAwareRuntime() {
GraphDatabaseService graphDatabaseService = graphDatabaseService();
GraphAwareRuntime runtime = GraphAwareRuntimeFactory
.createRuntime(graphDatabaseService);
runtime.registerModule(new TimeTreeModule("timetree",
TimeTreeConfiguration
.defaultConfiguration()
.withAutoAttach(true)
.with(new NodeInclusionPolicy() {
#Override
public Iterable<Node> getAll(GraphDatabaseService graphDatabaseService) {
return null;
}
#Override
public boolean include(Node node) {
return node.hasLabel(Label.label("User"));
}
})
.withRelationshipType(RelationshipType.withName("CREATED_ON"))
.withTimeZone(DateTimeZone.forTimeZone(TimeZone.getTimeZone("GMT+1")))
.withTimestampProperty("createdOn")
.withResolution(Resolution.DAY)
// .withCustomTimeTreeRootProperty("timeTreeName")
.withResolution(Resolution.HOUR), graphDatabaseService));
runtime.start();
return runtime;
}
}
And my test looks like this:
User user = new User("Michal");
user.setCreatedOn(1431937636995l);
userRepository.save(user);
GraphUnit.assertSameGraph(graphDb, "CREATE (u:User {name:'Michal', createdOn:1431937636995})," +
"(root:TimeTreeRoot)," +
"(root)-[:FIRST]->(year:Year {value:2015})," +
"(root)-[:CHILD]->(year)," +
"(root)-[:LAST]->(year)," +
"(year)-[:FIRST]->(month:Month {value:5})," +
"(year)-[:CHILD]->(month)," +
"(year)-[:LAST]->(month)," +
"(month)-[:FIRST]->(day:Day {value:18})," +
"(month)-[:CHILD]->(day)," +
"(month)-[:LAST]->(day)," +
"(day)<-[:CREATED_ON]-(u)"
);
GraphUnit.printGraph(graphDb);
graphDb.shutdown();
There's a host of errors, but I think they all stem from this one:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.springframework.data.repository.support.Repositories]:
Factory method 'repositories' threw exception; nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException: Error
creating bean with name 'userRepository': Unsatisfied dependency
expressed through method 'setSession' parameter 0; nested exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
qualifying bean of type 'org.neo4j.ogm.session.Session' available:
expected single matching bean but found 2: getSession,
org.springframework.data.neo4j.transaction.SharedSessionCreator#0
It is because the configuration class redefines some beans already automatically configured by Spring boot (here the Session).
So spring injection does not know how to choose between the 2.
Removing the getSession() should help.
A second thing is that your SessionFactory has to use the embedded DB setup in the graphDatabaseService() method. For this, configure an embedded driver with the existing database.
Summary config that should work fine for you :
#Bean
public SessionFactory sessionFactory() {
EmbeddedDriver driver = new EmbeddedDriver(graphDatabaseService());
return new SessionFactory(driver, "org.neo4j.boot.test.domain");
}
#Bean
public PlatformTransactionManager transactionManager() {
return new Neo4jTransactionManager(sessionFactory());
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder().newGraphDatabase();
}
#Bean
public GraphAwareRuntime graphAwareRuntime() {
...

How can I lazy create the EntityManagerFactory using Spring Data ORM/JPA?

I'm creating a standalone Java application that uses Spring Data with JPA.
Part of the class that creates the factory for the EntityManagerFactory is below:
#Configuration
#Lazy
public class JpaConfig {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(MultiTenantConnectionProvider connProvider, CurrentTenantIdentifierResolver tenantResolver) {
...
}
The problem is: I can only detect the Hibernate Dialect after the ApplicationContext is initialized, because this information is read from an external configuration service.
Since #Lazy did not work, is there any strategy to avoid creating this bean before it is used, i.e, only create it when another bean injects an instance of EntityManager?
I stumbled upon this issue recently and found a solution that worked. Unfortunately "container" managed beans will be initialized during startup and #Lazy is ignored even if the EntityManager is not injected anywhere.
I fixed it by using an in-memory H2 DB to construct the factory bean during startup and changed it later. I think here's what you can do for your issue.
pom.xml:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
Source code:
#Configuration
public class DataSourceConfig {
#Bean
public HikariDataSource realDataSource() {
...
}
#Bean
public DataSource localH2DataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean myEntityManagerFactory() throws PropertyVetoException {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(localH2DataSource());
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
return factoryBean;
}
}
#Component
#Lazy
public class Main {
#Autowired
private LocalContainerEntityManagerFactoryBean emf;
#Autowired
private HikariDataSource realDataSource;
#PostConstruct
private void updateHibernateDialect() {
// read the external config here
emf.setDataSource(realDataSource);
Properties jpaProperties = new Properties();
jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.DB2Dialect");
factoryBean.setJpaProperties(jpaProperties);
}
}

Unit testing a Spring Boot service class with(out) repository in JUnit

I am working on a spring boot based webservice with following structure:
Controller (REST) --> Services --> Repositories (as suggested in some tutorials).
My Database Connection (JPA/Hibernate/MySQL) is defined in a #Configuration class. (see below)
Now I'd like to write simple tests for methods in my Service classes, but I don't really understand how to load ApplicationContext into my test classes and how to mock the JPA / Repositories.
This is how far I came:
My service class
#Component
public class SessionService {
#Autowired
private SessionRepository sessionRepository;
public void MethodIWantToTest(int i){
};
[...]
}
My test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class SessionServiceTest {
#Configuration
static class ContextConfiguration {
#Bean
public SessionService sessionService() {
return new SessionService();
}
}
#Autowired
SessionService sessionService;
#Test
public void testMethod(){
[...]
}
}
But I get following exception:
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'sessionService': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.myApp.SessionRepository
com.myApp.SessionService.sessionRepository; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [com.myApp.SessionRepository] found for
dependency: expected at least 1 bean which qualifies as autowire
candidate for this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
For completeness: here's my #Configuration for jpa:
#Configuration
#EnableJpaRepositories(basePackages={"com.myApp.repositories"})
#EnableTransactionManagement
public class JpaConfig {
#Bean
public ComboPooledDataSource dataSource() throws PropertyVetoException, IOException {
...
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
...
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
...
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
...
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
...
}
}
use #SpringBootTest and #RunWith(SpringRunner.class) to load the context
#RunWith(SpringRunner.class)
#SpringBootTest
class Demo{
#Test
void contextLoad(){}
}
In your test Spring will use configuration only from inner ContextConfiguration class. This class describes your context. In this context you created only service bean and no repository. So the only bean that will be created is SessionService. You should add description of SessionRepository in inner ContextConfiguration. Your JpaConfig class will not be used in test class(you don't specify this), only in application.

Spring Batch configuration NoSuchMethodError Exception

This is the first time I am using Spring batch and I have an exception :
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'stepScope' defined in class path resource [org/springframework/batch/core/configuration/annotation/StepScopeConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.batch.core.scope.StepScope org.springframework.batch.core.configuration.annotation.StepScopeConfiguration.stepScope()] threw exception; nested exception is java.lang.NoSuchMethodError: org.springframework.batch.core.scope.StepScope.setAutoProxy(Z)V
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:581)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1031)
....
Caused by: java.lang.NoSuchMethodError: org.springframework.batch.core.scope.StepScope.setAutoProxy(Z)V
at org.springframework.batch.core.configuration.annotation.StepScopeConfiguration.stepScope(AbstractBatchConfiguration.java:130)
I don't know how to solve this error. I am using a Java configuration to define the job, the step, the reader, the processor and the writer :
public class SpringBatchTestJobConfig extends PersistenceSpringConfig {
#Autowired
private JobBuilderFactory jobBuilders;
#Autowired
private StepBuilderFactory stepBuilders;
#Bean
#StepScope
public FlatFileItemReader<EntiteJuridique> cvsReader(#Value("#{jobParameters[input.file]}") String input)
{
log.info("cvsReader");
FlatFileItemReader<EntiteJuridique> flatFileReader = new FlatFileItemReader<EntiteJuridique>();
flatFileReader.setLineMapper(lineMapper());
flatFileReader.setResource(new ClassPathResource(input));
return flatFileReader;
}
#Bean
public ItemWriter<EntiteJuridiqueJPA> writer()
{
log.info("writer");
JpaItemWriter<EntiteJuridiqueJPA> jpaWriter = new JpaItemWriter<EntiteJuridiqueJPA>();
try
{
jpaWriter.setEntityManagerFactory(entityManagerFactory().nativeEntityManagerFactory);
}
catch (Exception exception)
{
log.debug(exception);
}
return jpaWriter;
}
#Bean
public Job dataInitializer()
{
return jobBuilders.get("dataInitializer").start(step()).build();
}
#Bean
public Step step()
{
return stepBuilders.get("step").<EntiteJuridique, EntiteJuridiqueJPA> chunk(1).reader(cvsReader(null)).processor(processor())
.writer(writer()).build();
}
Any clues to how this problem?
EDIT : I am using
spring-core 3.2.2.RELEASE
spring-batch 2.2.0.RELEASE
spring-batch-infrastructure 2.2.0.RELEASE
spring-test 3.2.2.RELEASE
I got this same error, the solution for me was removing the version from my dependency and it worked fine.
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
</dependency>

Categories