jars.nosqlunit-documentation.1.0.0.source-code.mongodb.xml Maven / Gradle / Ivy
The newest version!
<?xml version="1.0" encoding="UTF-8"?> <chapter version="5.0" xml:id="mongodb" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:svg="http://www.w3.org/2000/svg" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:db="http://docbook.org/ns/docbook"> <title>MongoDb Engine</title> <section> <title>MongoDb</title> <para> <application>MongoDb</application> is a <emphasis>NoSQL</emphasis> database that stores structured data as <emphasis>JSON-like</emphasis> documents with dynamic schemas. </para> <para> <emphasis role="bold">NoSQLUnit</emphasis> supports <emphasis>MongoDb</emphasis> by using next classes: </para> <para> <table border="1"> <caption>Lifecycle Management Rules</caption> <tr> <td>In Memory</td> <td> <classname>com.lordofthejars.nosqlunit.mongodb.InMemoryMongoDb </classname> </td> </tr> <tr> <td>Managed</td> <td> <classname>com.lordofthejars.nosqlunit.mongodb.ManagedMongoDb </classname> </td> </tr> </table> </para> <para> <table border="1"> <caption>Manager Rule</caption> <tr> <td>NoSQLUnit Management</td> <td> <classname>com.lordofthejars.nosqlunit.mongodb.MongoDbRule </classname> </td> </tr> </table> </para> <section> <title>Maven Setup</title> <para> To use <emphasis role="bold">NoSQLUnit</emphasis> with <application>MongoDb</application> you only need to add next dependency: </para> <example xml:id="conf.nosqlunit_dep"> <title>NoSqlUnit Maven Repository</title> <programlisting language="xml"><![CDATA[<dependency> <groupId>com.lordofthejars</groupId> <artifactId>nosqlunit-mongodb</artifactId> <version>${version.nosqlunit}</version> </dependency>]]></programlisting> </example> <para> Note that if you are plannig to use <emphasis role="bold">in-memory</emphasis> approach an extra dependency is required. <emphasis role="bold">In-memory</emphasis> mode is implemented using <emphasis>jmockmongo</emphasis> . <emphasis>JMockmongo</emphasis> is a new project that help with unit testing Java-based <application>MongoDb</application> Applications by starting an in-process <emphasis>Netty</emphasis> server that speaks the <emphasis>MongoDb</emphasis> protocol and maintains databases and collections in <acronym>JVM</acronym> memory. It is not a true embedded mode becuase it will starts a server, but in fact for now it is the best way to write <application>MongoDb</application> unit tests. As his author says it is an incomplete tool and will be improved every time a new feature is required. </para> <warning> <para> During development of this documentation, current <emphasis>jmockmongo</emphasis> version was 0.0.2-SNAPSHOT. Author is imporoving version often so before using one specific version, take a look at its <link xlink:href="https://github.com/thiloplanz/jmockmongo">website</link> . </para> </warning> <para> To install add next <link linkend="conf.jmockmongo_repo"> repository </link> and <link linkend="conf.jmockmongo_dep"> dependency </link> : </para> <example xml:id="conf.jmockmongo_repo"> <title>jmockmongo Maven Repository</title> <programlisting language="xml"><![CDATA[<repositories> <repository> <id>thiloplanz-snapshot</id> <url>http://repository-thiloplanz.forge.cloudbees.com/snapshot/</url> </repository> </repositories>]]></programlisting> </example> <example xml:id="conf.jmockmongo_dep"> <title>jmockmongo Maven Dependency</title> <programlisting language="xml"><![CDATA[<dependency> <groupId>jmockmongo</groupId> <artifactId>jmockmongo</artifactId> <version>${mongomock.version}</version> </dependency>]]></programlisting> </example> </section> <section> <title>Dataset Format</title> <para> Default dataset file format in <emphasis>MongoDb</emphasis> module is <emphasis>json</emphasis> . </para> <para> Datasets must have next <link linkend="ex.mongodb_dataset"> format </link> : </para> <example xml:id="ex.mongodb_dataset"> <title>Example of MongoDb Dataset</title> <programlisting language="json"><![CDATA[{ "name_collection1": [ { "attribute_1":"value1", "attribute_2":"value2" }, { "attribute_3":2, "attribute_4":"value4" } ], "name_collection2": [ ... ], .... }]]></programlisting> </example> <para>Notice that if attributes value are integers, double quotes are not required. </para> </section> <section> <title>Getting Started</title> <section> <title>Lifecycle Management Strategy</title> <para> First step is defining which lifecycle management strategy is required for your tests. Depending on kind of test you are implementing (unit test, integration test, deployment test, ...) you will require an <emphasis role="bold">in-memory</emphasis> approach, <emphasis role="bold">managed</emphasis> approach or <emphasis role="bold">remote</emphasis> approach. </para> <para> To configure <emphasis role="bold">in-memory</emphasis> approach you should only instantiate next <link linkend="program.inmemory_conf">rule</link> : </para> <example xml:id="program.inmemory_conf"> <title>In-memory MongoDb</title> <programlisting language="java"><![CDATA[@ClassRule InMemoryMongoDb inMemoryMongoDb = new InMemoryMongoDb();]]></programlisting> </example> <para> To configure the <emphasis role="bold">managed</emphasis> way, you should use <classname>ManagedMongoDb</classname> rule and may require some <link linkend="program.managed_conf">configuration</link> parameters. </para> <example xml:id="program.managed_conf"> <title>Managed MongoDb</title> <programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.mongodb.ManagedMongoDb.MongoServerRuleBuilder.newManagedMongoDbRule; @ClassRule public static ManagedMongoDb managedMongoDb = newManagedMongoDbRule().build();]]></programlisting> </example> <para> By default managed <emphasis>MongoDb</emphasis> rule uses next default values: </para> <itemizedlist> <listitem> <para> <emphasis>MongoDb</emphasis> installation directory is retrieved from <varname>MONGO_HOME</varname> system environment variable. </para> </listitem> <listitem> <para> Target path, that is the directory where <emphasis>MongoDb</emphasis> server is started, is <constant>target/mongo-temp</constant> . </para> </listitem> <listitem> <para> Database path is at <parameter>{target path} </parameter> <constant>/mongo-dbpath</constant> . </para> </listitem> <listitem> <para> Because after execution of tests all generated data is removed, in <parameter>{target path} </parameter> <constant>/logpath</constant> will remain log file generated by the server. </para> </listitem> <listitem> <para> In <emphasis>Windows</emphasis> systems executable should be found as <filename>bin/mongod.exe</filename> meanwhile in <emphasis>MAC OS </emphasis> and <emphasis>*nix</emphasis> should be found as <filename>bin/mongod</filename> . </para> </listitem> </itemizedlist> <para> <classname>ManagedMongoDb</classname> can be created from scratch, but for making life easier, a <emphasis>DSL</emphasis> is provided using <classname>MongoServerRuleBuilder</classname> class. For <link linkend="program.managed_specific_conf">example</link> : </para> <example xml:id="program.managed_specific_conf"> <title>Specific Managed MongoDb Configuration</title> <programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.mongodb.ManagedMongoDb.MongoServerRuleBuilder.newManagedMongoDbRule; @ClassRule public static ManagedMongoDb managedMongoDb = newManagedMongoDbRule().mongodPath("/opt/mongo").appendSingleCommandLineArguments("-vvv").build();]]></programlisting> </example> <para> In <link linkend="program.managed_specific_conf">example</link> we are overriding <varname>MONGO_HOME</varname> variable (in case has been set) and set mongo home at <filename>/opt/mongo</filename> . Moreover we are appending a single argument to <emphasis>MongoDb</emphasis> executable, in this case setting log level to number 3 (-vvv). Also you can append <emphasis>property=value</emphasis> arguments using <function>appendCommandLineArguments(String argumentName, String argumentValue) </function> method. </para> <warning> <para>when you are specifying command line arguments, remember to add slash (-) and double slash (--) where is necessary. </para> </warning> <para> To stop <emphasis>MongoDb</emphasis> instance, <emphasis role="bold">NoSQLUnit</emphasis> sends a <function>shutdown</function> command to server using <emphasis>Java Mongo AP</emphasis> I. When this command is sent, the server is stopped and because connection is lost, <emphasis>Java Mongo API</emphasis> logs automatically an exception (read <link xlink:href="https://groups.google.com/group/mongodb-user/browse_thread/thread/ac9a4c9ea13f3e81">here</link> information about the problem and how to "resolve" it). Do not confuse with a testing failure. You will see something like: </para> <screen><![CDATA[java.io.EOFException at org.bson.io.Bits.readFully(Bits.java:37) at org.bson.io.Bits.readFully(Bits.java:28) at com.mongodb.Response.<init>;(Response.java:39) at com.mongodb.DBPort.go(DBPort.java:128) at com.mongodb.DBPort.call(DBPort.java:79) at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:218) at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:305) at com.mongodb.DB.command(DB.java:160) at com.mongodb.DB.command(DB.java:183) at com.mongodb.DB.command(DB.java:144) at com.lordofthejars.nosqlunit.mongodb.MongoDbLowLevelOps.shutdown(MongoDbLowLevelOps.java:44) at com.lordofthejars.nosqlunit.mongodb.ManagedMongoDb.after(ManagedMongoDb.java:157) at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48) at org.junit.rules.RunRules.evaluate(RunRules.java:18) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:236) at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:134) at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:113) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:616) at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189) at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165) at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:103) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:74)]]></screen> <para> Configuring <emphasis role="bold">remote</emphasis> approach does not require any special rule because you (or System like <application>Maven</application> ) is the responsible of starting and stopping the server. This mode is used in deployment tests where you are testing your application on real environment. </para> </section> <section> <title>Configuring MongoDb Connection</title> <para> Next step is configuring <emphasis> <emphasis role="bold">Mongodb</emphasis> </emphasis> rule in charge of maintaining <emphasis>MongoDb</emphasis> database into known state by inserting and deleting defined datasets. You must register <classname>MongoDbRule</classname> <emphasis>JUnit</emphasis> rule class, which requires a configuration parameter with information like host, port or database name. </para> <para>To make developer's life easier and code more readable, a fluent interface can be used to create these configuration objects. Two different kind of configuration builders exist. </para> <para> The first one is for configuring a connection to in-memory <emphasis>jmockmongo</emphasis> server. Default connection values are: </para> <table border="1"> <caption>Default In-Memory Configuration Values</caption> <tr> <td>Host</td> <td>0.0.0.0</td> </tr> <tr> <td>Port</td> <td>2307</td> </tr> </table> <para> Notice that these values are the default ones of <emphasis>jmockmongo</emphasis> project, so if you are thinking to use <emphasis>jmockmongo</emphasis> , no modifications are required. </para> <example xml:id="program.in_memory_connection_parameters"> <title>MongoDbRule with in-memory configuration</title> <programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.mongodb.InMemoryMongoDbConfigurationBuilder.inMemoryMongoDb; @Rule public MongoDbRule remoteMongoDbRule = new MongoDbRule(inMemoryMongoDb().databaseName("test").build());]]></programlisting> </example> <para> The second one is for configuring a connection to remote <emphasis>MongoDb</emphasis> server. Default values are: </para> <table border="1"> <caption>Default Managed Configuration Values</caption> <tr> <td>Host</td> <td>localhost</td> </tr> <tr> <td>Port</td> <td>27017</td> </tr> <tr> <td>Authentication</td> <td>No authentication parameters.</td> </tr> </table> <example xml:id="program.managed_connection_parameters"> <title>MongoDbRule with managed configuration</title> <programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.mongodb.MongoDbConfigurationBuilder.mongoDb; @Rule public MongoDbRule remoteMongoDbRule = new MongoDbRule(mongoDb().databaseName("test").build());]]></programlisting> </example> <example xml:id="program.remote_connection_parameters"> <title>MongoDbRule with remote configuration</title> <programlisting language="java"><![CDATA[import static com.lordofthejars.nosqlunit.mongodb.MongoDbConfigurationBuilder.mongoDb; @Rule public MongoDbRule remoteMongoDbRule = new MongoDbRule(mongoDb().databaseName("test").host("my_remote_host").build());]]></programlisting> </example> </section> <section> <title>Complete Example</title> <para> Consider a library application, which apart from multiple operations, it allow us to add new books to system. Our <link linkend="example.book_model">model</link> is as simple as: </para> <example xml:id="example.book_model"> <title>Book POJO</title> <programlisting language="java"><![CDATA[public class Book { private String title; private int numberOfPages; public Book(String title, int numberOfPages) { super(); this.title = title; this.numberOfPages = numberOfPages; } public void setTitle(String title) { this.title = title; } public void setNumberOfPages(int numberOfPages) { this.numberOfPages = numberOfPages; } public String getTitle() { return title; } public int getNumberOfPages() { return numberOfPages; } }]]></programlisting> </example> <para> Next business <link linkend="example.book_manager">class</link> is the responsible of managing access to <emphasis>MongoDb</emphasis> server: </para> <example xml:id="example.book_manager"> <title>Book POJO</title> <programlisting language="java"><![CDATA[public class BookManager { private static final Logger LOGGER = LoggerFactory.getLogger(BookManager.class); private static final MongoDbBookConverter MONGO_DB_BOOK_CONVERTER = new MongoDbBookConverter(); private static final DbObjectBookConverter DB_OBJECT_BOOK_CONVERTER = new DbObjectBookConverter(); private DBCollection booksCollection; public BookManager(DBCollection booksCollection) { this.booksCollection = booksCollection; } public void create(Book book) { DBObject dbObject = MONGO_DB_BOOK_CONVERTER.convert(book); booksCollection.insert(dbObject); } }]]></programlisting> </example> <para> And now it is time for testing. In next <link linkend="example.test_insert_book">test</link> we are going to validate that a book is inserted correctly into database. </para> <example xml:id="example.test_insert_book"> <title>Test with Managed Connection</title> <programlisting language="java"><![CDATA[package com.lordofthejars.nosqlunit.demo.mongodb; public class WhenANewBookIsCreated { @ClassRule public static ManagedMongoDb managedMongoDb = newManagedMongoDbRule().mongodPath("/opt/mongo").build(); @Rule public MongoDbRule remoteMongoDbRule = new MongoDbRule(mongoDb().databaseName("test").build()); @Test @UsingDataSet(locations="initialData.json", loadStrategy=LoadStrategyEnum.CLEAN_INSERT) @ShouldMatchDataSet(location="expectedData.json") public void book_should_be_inserted_into_repository() { BookManager bookManager = new BookManager(MongoDbUtil.getCollection(Book.class.getSimpleName())); Book book = new Book("The Lord Of The Rings", 1299); bookManager.create(book); } }]]></programlisting> </example> <para> In <link linkend="example.test_insert_book">previous</link> test we have defined that <emphasis>MongoDb</emphasis> will be managed by test by starting an instance of server located at <filename>/opt/mongo</filename> . Moreover we are setting an <link linkend="example.dataset_book">initial</link> dataset in file <filename>initialData.json</filename> located at classpath <filename>com/lordofthejars/nosqlunit/demo/mongodb/initialData.json </filename> and <link linkend="example.expected_dataset_book">expected</link> dataset called <filename>expectedData.json</filename> . </para> <example xml:id="example.dataset_book"> <title>Initial Dataset</title> <programlisting language="json"><![CDATA[{ "Book": [ {"title":"The Hobbit","numberOfPages":293} ] }]]></programlisting> </example> <example xml:id="example.expected_dataset_book"> <title>Expected Dataset</title> <programlisting language="json"><![CDATA[{ "Book": [ {"title":"The Hobbit","numberOfPages":293}, {"title":"The Lord Of The Rings","numberOfPages":1299} ] }]]></programlisting> </example> <para> You can watch full example at <link xlink:href="https://github.com/lordofthejars/nosql-unit/tree/master/nosqlunit-demo">github</link> . </para> </section> </section> </section> </chapter>
© 2015 - 2025 Weber Informatics LLC | Privacy Policy