/* * Hibernate OGM, Domain model persistence for NoSQL datastores * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.ogm.datastore.mongodb.utils; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import com.mongodb.MongoClient; import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; import com.mongodb.WriteConcern; import com.mongodb.client.FindIterable; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoIterable; import com.mongodb.client.model.FindOneAndUpdateOptions; import com.mongodb.client.result.DeleteResult; import org.bson.Document; /** * A builder for mocked {@link MongoClient} instances which return given {@link Document}s for given collections. * <p> * Note that currently only one {@code Document} is allowed per collection, but this could be expanded into a more * general mechanism if required. * * @author Gunnar Morling */ public class MockMongoClientBuilder { /** * Builds an new mock MongoDB client. * * @return a builder context following the fluent invocation pattern. */ public static MockMongoClientBuilderContext mockClient() { return new MockMongoClientBuilderContext(); } public static class MockMongoClientBuilderContext { private final Map<String, MongoCollection<Document>> collections = new HashMap<>(); /** * Registers the given {@link Document} with the specified collection. The object can be retrieved from the * collection via {@link MongoCollection<Document>#findOne(Document, Document))}. * <p> * Note that currently only one {@code Document} is supported per collection, but this could be expanded into a * more general mechanism if required. */ public MockMongoClientBuilderContext insert(String collectionName, Document object) { MongoCollection<Document> collection = mock( MongoCollection.class ); when( collection.withWriteConcern( any( WriteConcern.class ) ) ).thenReturn( collection ); when( collection.withReadConcern( any( ReadConcern.class ) ) ).thenReturn( collection ); when( collection.withReadPreference( any( ReadPreference.class ) ) ).thenReturn( collection ); collections.put( collectionName, collection ); FindIterable<Document> findIterableMock1 = mock( FindIterable.class ); FindIterable<Document> findIterableMock2 = mock( FindIterable.class ); when( findIterableMock1.projection( any( Document.class ) ) ).thenReturn( findIterableMock2 ); when( findIterableMock2.first() ).thenReturn( object ); when( collection.find( any( Document.class ) ) ).thenReturn( findIterableMock1 ); when( collection.findOneAndUpdate( any( Document.class ), any( Document.class ), any( FindOneAndUpdateOptions.class ) ) ).thenReturn( object ); DeleteResult deleteResult = mock( DeleteResult.class ); when( collection.deleteMany( any( Document.class ) ) ).thenReturn( deleteResult ); return this; } /** * Builds and returns a mock MongoDB client based on the given configuration. * @see <a href="https://jaihirsch.github.io/straw-in-a-haystack/mongodb/2016/07/25/mocking-the-mongodb-java-driver/">MongoDb Driver mocking</a> */ public MockMongoClient build() { MongoDatabase database = mock( MongoDatabase.class ); MongoCollection<Document> defaultCollection = mock( MongoCollection.class ); when( defaultCollection.withWriteConcern( any( WriteConcern.class ) ) ).thenReturn( defaultCollection ); when( database.getCollection( anyString() ) ).thenReturn( defaultCollection ); when( defaultCollection.withWriteConcern( any( WriteConcern.class ) ) ).thenReturn( defaultCollection ); when( defaultCollection.withReadConcern( any( ReadConcern.class ) ) ).thenReturn( defaultCollection ); when( defaultCollection.withReadPreference( any( ReadPreference.class ) ) ).thenReturn( defaultCollection ); for ( Entry<String, MongoCollection<Document>> collection : collections.entrySet() ) { when( database.getCollection( collection.getKey() ) ).thenReturn( collection.getValue() ); } MongoClient mongoClient = mock( MongoClient.class ); MongoIterable<String> iterable = mock( MongoIterable.class ); MongoCursor<String> cursor = mock( MongoCursor.class ); when( iterable.iterator() ).thenReturn( cursor ); when( cursor.hasNext() ).thenReturn( true ).thenReturn( false ); when( cursor.next() ).thenReturn( "testdb" ); //@TODO prepare mock for MongoCursor when( mongoClient.listDatabaseNames() ).thenReturn( iterable ); when( mongoClient.getDatabase( anyString() ) ).thenReturn( database ); return new MockMongoClient( collections, defaultCollection, mongoClient ); } } /** * A mock client for MongoDB. * * @author Gunnar Morling */ public static class MockMongoClient { private final Map<String, MongoCollection<Document>> collections; private final MongoCollection<Document> defaultCollection; private final MongoClient client; public MockMongoClient(Map<String, MongoCollection<Document>> collections, MongoCollection<Document> defaultCollection, MongoClient client) { this.collections = collections; this.defaultCollection = defaultCollection; this.client = client; } /** * Returns a mock {@link MongoClient}. */ public MongoClient getClient() { return client; } /** * Returns the collection with a given name. This is a Mockito mock object, so verifications can be performed on * it. */ public MongoCollection<Document> getCollection(String collectionName) { MongoCollection<Document> collection = collections.get( collectionName ); return collection != null ? collection : defaultCollection; } } }