Using embedded mongo in spring for local testing only - mongodb

I'm trying to use the flapdoodle embedded mongo database ('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
for local manual testing the way I'd use H2 for relational database testing. I only want the embedded mongo to start up when running under the 'local' SPring profile, but I can't figure out how to make it happen -- it just always starts under any profile.
I've tried adding #SpringBootApplication(exclude=EmbeddedMongoAutoConfiguration.class) to my application (thinking I could reimport that autoconfig to a single profile later) but that doesn't seem to have any affect.
Anyone know how to limit it to a single profile, in a way that will work when running in Eclipse as well as via Gradle?

I can think of two ways:
1- Load your dependency only in specific profile in maven or gradle
Maven
<profiles>
<profile>
<id>local</id>
<dependencies>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>2.0.4-SNAPSHOT</version>
</dependency>
</dependencies>
</profile>
</profiles>
Gradle
if (project.hasProperty('local')) {
dependencies {
compile 'de.flapdoodle.embed:de.flapdoodle.embed.mongo:2.0.4-SNAPSHOT'
}
}
2- Using #Profile and #Import in your class
LoadEmbeded.java
#Profile(value = "local")
#Configuration
#Import(EmbeddedMongoAutoConfiguration.class)
public class LoadEmbeded {
}
Hope it can give your a idea about it
Edit 1:
I have tested my approach and made a very simple application. It works with #Profile. Here is what I tested.
#SpringBootApplication(exclude = EmbeddedMongoAutoConfiguration.class)
public class DemoApplication {
#Autowired
TestRepository repository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
CommandLineRunner runner() {
return args -> {
System.out.println(repository.save(new Test()));
System.out.println(repository.findAll());
};
}
}
#Repository
interface TestRepository extends MongoRepository<Test, String> {
}
#Document
class Test {
#Id
private String id;
public String getId() {
return id;
}
public Test setId(String id) {
this.id = id;
return this;
}
#Override
public String toString() {
return new ToStringBuilder(this)
.append("id", id)
.toString();
}
}
#Configuration
#Profile("local")
#Import(EmbeddedMongoAutoConfiguration.class)
class Load {
}
And application.properties
spring.profiles.active=local
When I changed active profile to something else but local, it throws exception during startup and complaining about connection of mongodb. But when I set it local, it works and shows me the id of saved test entity.
If still you are experiencing same problem, maybe one of your dependencies is loading the Embedded MongoDB again even you exclude it. Usually it should be with test dependencies. Check your dependencies.

Related

Make MongoDB Spring Boot test use existing configuration and embedded test database

this is my existing MongoDB configuration:
#Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {
#Autowired
MongoDBProperties mongoDBProperties;
#Override
public MongoClient mongoClient() {
ConnectionString connectionString = new ConnectionString(mongoDBProperties.getUrl());
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();
return MongoClients.create(mongoClientSettings);
}
#Override
public MongoCustomConversions customConversions() {
return new MongoCustomConversions(Arrays.asList(new OffsetDateTimeReadConverter(), new OffsetDateTimeWriteConverter()));
}
#Override
protected String getDatabaseName() {
return mongoDBProperties.getDatabase();
}
}
I need the converters because the document that I store contains OffsetDataTime fields.
I want to test saving a document. I have included this dependency:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
My test looks like this:
#DataMongoTest
public class VersionRepositoryTestCase {
#Autowired
MyDocumentRepository cut;
#Autowired
MongoDBProperties mongoDBProperties;
#Test
public void speichernVonVersionMitVersionInfoFunktioniert() {
MyDocument myDocument = createMyDocument();
cut.save(versionDocument);
}
}
My problem is: if I use #SpringBootTest, it is not the embedded MongoDBD that is being used. if I use the #DataMongoTest and override the MongoDbProperties bean, the converters are not used so the test fails. So how can I use my config class with the embedded test mongo db instance?
It works with #Import anotation above the Test class.

Swagger 2 with Spring REST API - whitelabel error page

I am working on a simple HelloWorld Spring Boot application and I am trying to configure Swagger to automatically generate my REST service documentation.
I am following this tutorial: http://www.baeldung.com/swagger-2-documentation-for-spring-rest-api
But I am facing some difficulties.
I've added the following Swagger dependencies to the maven pom.xml file:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
and also the Java configuration class SwaggerConfig as follows:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
According to the guide, this should be sufficient to view the generated documentation on http://localhost:8080/v2/api-docs
However, I am getting "Whitespace Error Page: This application has no explicit mapping for /error, so you are seeing this as a fallback."
I am getting the same error for the Swagger UI page at http://localhost:8080/greeting/api/swagger-ui.html too.
For extra information, my HelloWorld code is as follows:
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
and the greeting controller is
package hello;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicLong;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Everything is under main/java
I would really appreciate it if anyone has any ideas/suggestions on how to fix this! Thank you in advance :)

Using embedded MongoDB in Spring JUnit #WebMvcTest

I'm currently using MongoDB in my Spring application. Since I added Mongo my endpoint tests no longer work due to the following error:
No qualifying bean of type 'xxx' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
The repository which I Autowire in a controller is as follows:
private final RuleRepository ruleRepository;
#Autowired
public TestController(RuleRepository ruleRepository) {
this.ruleRepository = ruleRepository;
}
I assume that this has to do with Mongo and the fact that I currently use AutoConfiguration for it. For the test I added the Flapdoodle Embed Mongo dependency, which seems to be used for testing purposes in a lot of examples, to my pom.xml with the scope set to test:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>2.0.3</version>
<scope>test</scope>
</dependency>
My test class looks like this:
RunWith(SpringRunner.class)
#WebMvcTest(value = RouteController.class, secure = false)
#ActiveProfiles("test")
public class TestControllerEndpointTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private RuleRepository ruleRepository;
#Before
public void setupTests() {
//Setup for the tests
}
//Actual tests
}
I also created a Configuration class for the Mongo Test Database, but I don't know how to correctly register it:
#Configuration
#Profile("test")
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();
return new MongoClient(net.getServerAddress().getHostName(), net.getPort());
}
#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();
}
}
How do I get an endpoint test that is annotated with #WebMvcTest to use the embedded Mongo database?
After banging our heads for a while we found the #AutoConfigureDataMongo annotation.
import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo;
#RunWith(SpringRunner.class)
#WebMvcTest(value = SampleController.class, secure = false)
#AutoConfigureDataMongo
public class SampleControllerTest {
Just annotate your controller with it and you should see org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongo logs when running this controller tests.

How to unit test a Spring Boot MongoRepository?

In my Spring Boot web application I use MongoDB to store data. In the application I access the database using interfaces that extend MongoRepository.
How do I set up a unit test for such a repository class? What I would like is to
start an embedded/in memory instance of MongoDB
insert testdata from JSON or XML
use an autowired repository to perform queries on the testdata
I have tried using Embedded MongoDB, but I can't figure out how to insert testdata from a file.
I've also tried using NoSQLUnit, but the SpringApplicationConfiguration conflicts with the unit test configuration, resulting in different databases for reading and writing.
Just use #DataMongoTest from Spring Boot.
#RunWith(SpringRunner.class)
#DataMongoTest
public class FooRepositoryTest {
#Autowired
FooRepository fooRepository;
#Before
public void setUp() throws Exception {
fooRepository.save(new Foo());
}
#Test
public void shouldBeNotEmpty() {
assertThat(fooRepository.findAll()).isNotEmpty();
}
}
Dependencies
<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>
An update for 2018 and Spring Boot 2. First of all, you can have data-only integration tests with the embedded db as per the documentation. This uses the DataMongoTest annotation. This configures only the necessary dependencies that make mongoDB tests possible.
If you want to do full integration tests, add the AutoConfigureDataMongo annotation instead:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#AutoConfigureDataMongo
public class PriceApiControllerIT {
Dependencies you should have in your pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
This is what I did.
public interface UserRepository extends MongoRepository<Users, Long> {
public void deleteByUserId(String userId);
public List<Users> findAllByUserStatus(String userStatus);
}
#Document
public class Users {
#Id
private long id;
#Transient
public static final String SEQUENCE_NAME = "users_sequence";
#Indexed
#NotNull
private String userId;
private String firstName;
private String lastName;
private String userType;
private String userStatus;
#Email
private String emailId;
#Size(min = 10, max = 10)
#NumberFormat
private String phoneNumber;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getUserType() {
return userType;
}
public void setUserType(String userType) {
this.userType = userType;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public static String getSequenceName() {
return SEQUENCE_NAME;
}
public String getUserStatus() {
return userStatus;
}
public void setUserStatus(String userStatus) {
this.userStatus = userStatus;
}
}
Here is the junit
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MockodsApplication.class)
#SpringBootTest
#AutoConfigureMockMvc
public class UserControllerIT {
#Autowired
private UserRepository userRepository;
#Autowired
MongoTemplate mongoTemplate;
#Autowired
private MockMvc mvc;
#After
public void tearDown() {
}
#Test
public void test1() {
Users user = new Users();
long userId = 1L;
user.setId(userId);
user.setFirstName("FirstName");
user.setLastName("FirstName");
user.setEmailId("fisrtname.secondname#gmail.com");
user.setPhoneNumber("1234567890");
assertEquals(user, userRepository.save(user));
}
#Test
public void test2() {
List<Users> persistedUser = userRepository.findAll();
assertEquals("fisrtname.secondname#gmail.com", persistedUser.get(0).getEmailId());
}
}
This link helped me to implement https://dzone.com/articles/spring-integration-tests
I faced the same problem and we used a separate MongoConfiguration class to specify a particular configuration for our tests.
You can create an embedded mongo instance by using EmbeddedMongoBuilder like this :
import com.mongodb.Mongo;
import cz.jirutka.spring.embedmongo.EmbeddedMongoBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.net.ServerSocket;
#Configuration
public class MongoConfiguration {
#Bean
public Mongo mongo() throws IOException {
System.setProperty("DB.TRACE","true");
return new EmbeddedMongoBuilder()
.version("2.13.1")
.bindIp("127.0.0.1")
.port(allocateRandomPort())
.build();
}
}
Then in your test class, specify that you want to use that particular configuration with the #Import annotation :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#Import(MongoConfiguration.class)
Hope that helps.
Check out https://mongoUnit.org
From its docs:
It's a data driven Integration testing framework for Spring Boot
based applications that use MongoDB for persistence. The framework
enables the developer to test the data access logic with relative
ease.
First, make sure that you have added the following Spring Boot parent to your project:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
Since we added Spring Boot parent, we can add required dependencies without specifying their versions:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring-boot-starter-data-mongodb will enable Spring support for
MongoDB
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
de.flapdoodle.embed.mongo provides embedded MongoDB for integration
tests
After adding de.flapdoodle.embed.mongo dependency Spring Boot will automatically try to download and start the embedded MongoDB when running tests. The following snippet shows how you can configure the embedded MongoDB instance manually
class EmbeddedMongoDbIntegrationTest {
private MongodExecutable mongodExecutable;
private MongoTemplate mongoTemplate;
#After
void clean() {
mongodExecutable.stop();
}
#Before
void setup() throws Exception {
String ip = "localhost";
int port = 27017;
IMongodConfig mongodConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION)
.net(new Net(ip, port, Network.localhostIsIPv6()))
.build();
MongodStarter starter = MongodStarter.getDefaultInstance();
mongodExecutable = starter.prepare(mongodConfig);
mongodExecutable.start();
mongoTemplate = new MongoTemplate(new MongoClient(ip, port), "test");
}
#Test
void test() throws Exception {
// given
DBObject objectToSave = BasicDBObjectBuilder.start()
.add("key", "value")
.get();
// when
mongoTemplate.save(objectToSave, "collection");
// then
assertThat(mongoTemplate.findAll(DBObject.class, "collection")).extracting("key")
.containsOnly("value");
}
}
Note, that we can quickly create MongoTemplate bean configured to use
our manually configured embedded database and register it inside the
Spring container, so your mongo repository will start leveraging this
mongoTemplate as well.

Testing an EJB with JUnit

How should I test an EJB 3.1 which gets an instance of EntityManager injected?
A possible EJB:
#Stateless
#LocalBean
public class CommentService {
#PersistenceContext
private EntityManager em;
public List<Comment> findAll() {
TypedQuery<Comment> query = em.createNamedQuery(
Comment.FIND_ALL, Comment.class
);
return query.getResultList();
}
}
A possible test:
#Test
public void testFindAll() {
List<Comment> all = service.findAll();
Assert.assertEquals(8, all.size());
}
I am only using GlassFish 3.1 and Eclipse Indigo for Java EE Developers. I already tried things like that:
#Before
public void setUp() throws Exception {
ejbContainer = EJBContainer.createEJBContainer();
service = (CommentService) ejbContainer.getContext()
.lookup("java:global/classes/CommentService");
}
But all I got was:
javax.ejb.EJBException:
No EJBContainer provider available: no provider names had been found.
The accepted answer requires mocking a lot of code, including the persistence layer. Use an embedded container to test the actual beans, instead; otherwise, mocking the persistence layer results in code that barely tests anything useful.
Use a session bean with an entity manager that references a persistence unit:
#Stateless
public class CommentService {
#PersistenceContext(unitName = "pu")
private EntityManager em;
public void create(Comment t) {
em.merge(t);
}
public Collection<Comment> getAll() {
Query q = em.createNamedQuery("Comment.findAll");
Collection<Comment> entities = q.getResultList();
return entities;
}
}
The entity bean:
#Entity
#NamedQueries({#NamedQuery(name = "Comment.findAll", query = "select e from Comment e")})
public class Comment implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
This persistence unit is defined in the persistence.xml file as follows:
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="pu" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>org.glassfish.embedded.tempconverter.Comment</class>
<properties>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>
The transaction type must be JTA.
Then write a test that creates and destroys the EJB container (GlassFish embedded container):
public class CommentTest extends TestCase {
private Context ctx;
private EJBContainer ejbContainer;
#BeforeClass
public void setUp() {
ejbContainer = EJBContainer.createEJBContainer();
System.out.println("Opening the container" );
ctx = ejbContainer.getContext();
}
#AfterClass
public void tearDown() {
ejbContainer.close();
System.out.println("Closing the container" );
}
public void testApp() throws NamingException {
CommentService converter = (CommentService) ctx.lookup("java:global/classes/CommentService");
assertNotNull(converter);
Comment t = new Comment();
converter.create(t);
t = new Comment();
converter.create(t);
t = new Comment();
converter.create(t);
t = new Comment();
converter.create(t);
Collection<Comment> ts = converter.getAll();
assertEquals(4, ts.size());
}
}
Next, add two dependencies (such as to a Maven POM):
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.glassfish.main.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.1.2</version>
<scope>compile</scope>
</dependency>
Having the dependencies, session and entity bean, persistence file, test files implemented exactly as shown, then the test(s) should pass. (The examples on the Internet are woefully inadequate.)
First of all, make sure you distinguish between unit tests and integration tests. JUnit is just a framework that helps you organize and run the tests, but you have to determine the scope of your tests.
I assume you're interested in defining a unit test of CommentService.findAll(). What does that mean? That means I'll verify that calling the findAll() method results in CommentService invoking the named query named by the FIND_ALL string constant.
Thanks to dependency injection and stubbing, you can easily achieve that using e.g. Mockito to stub out the EntityManager. For the unit test, we're only focusing on the business logic in findAll(), so I won't bother testing lookup of the Comment service either--testing that the Comment service can be looked up and is wired to a proper entity manager instance is in the scope of an integration test, not a unit test.
public class MyCommentServiceUnitTest {
CommentService commentService;
EntityManager entityManager;
#Before
public void setUp() {
commentService = new CommentService();
entityManager = mock(EntityManager.class);
commentService.setEm(entityManager); // inject our stubbed entity manager
}
#Test
public void testFindAll() {
// stub the entity manager to return a meaningful result when somebody asks
// for the FIND_ALL named query
Query query = mock(Query.class);
when(entityManager.createNamedQuery(Comment.FIND_ALL, Comment.class)).thenReturn(query);
// stub the query returned above to return a meaningful result when somebody
// asks for the result list
List<Comment> dummyResult = new LinkedList<Comment>();
when(query.getResultList()).thenReturn(dummyResult);
// let's call findAll() and see what it does
List<Comment> result = commentService.findAll();
// did it request the named query?
verify(entityManager).createNamedQuery(Comment.FIND_ALL, Comment.class);
// did it ask for the result list of the named query?
verify(query).getResultList();
// did it return the result list of the named query?
assertSame(dummyResult, result);
// success, it did all of the above!
}
}
With the unit test above, I tested the behavior of the findAll() implementation. The unit test verified that the correct named query is obtained and that the result returned by the named query was returned to the callee.
What's more, the unit test above verifies that the implementation of findAll() is correct independently of the underlying JPA provider and the underlying data. I don't want to test JPA and the JPA provider unless I suspect there are bugs in the 3rd party code, so stubbing out these dependencies lets me focus the test entirely on the business logic of the Comment service.
It can take a little while to adjust to the mindset of testing behavior using stubs, but it is a very powerful technique for testing the business logic of your EJB 3.1 beans because it lets you isolate and narrow the scope of each test to exclude external dependencies.
Why not using Arquillian to write even unit tests and run them in a real container!?
No more mocks. No more container lifecycle and deployment hassles. Just real tests!
Mocks can be tactical, but more often than not, they are used to make code work outside of a real environment. Arquillian let's you ditch the mocks and write real tests. That's because Arquillian brings your test to the runtime, giving you access to container resources, meaningful feedback and insight about how the code really works.
More about Arquillian features.
It's possible to write unit tests that run against a container, but the caveat is that the container/appserver has to be up. Since that's not really practical, the general approach is to use a "mock" container to run your unit tests against. For that, check out JUnitEE or ejb3unit:
junitee
ejb3unit