Couchbase Junit Mock QueryResult,GetResult and MutationResul

Hi Everyone,

I am writing JUNIT for Java classes that inturn used Couchbase QueryResult , GetResult and MutationResult.

Class demo{

 @Autowried
 CouchbaseDao coushbaseDao;

 public void getDataBasedonKv(String documentId){
  GetResult getResult =coushbaseDao.getDocument(documentId);
  SampleClass sampleObject = null;
  if(getResult != null){
    sampleObject = getResult.contentAs(SampleClass.class);
  }
  if(sampleObject != null){
   //some logic
  }
 }
 
 public void createDocument(String documentId){
  MutationResult mutationResult =  coushbaseDao.createDocument(documentId);
  if(mutationResult != null){
   //some Logic 
  }else{
   //some Logic 
  }
 }
 
 public List<String> getDataUsingQuery(String documentId){
  String query = "";
  QueryResult queryResult = coushbaseDao.executeQuery(query);
  if (queryResult != null) {
	variableList = queryResult.rowsAs(String.class);
  }
  if(variableList != null){
   //some logic
  }
  return variableList;
 }
}


interface coushbaseDao;

GetResult getDocument(String docId);

QueryResult executeQuery(String query);

MutationResult createDocument(String docId, Object objectData);

In the implementation class , I have written a abstraction layer using couchbase SDK to get the data, execute query and create operation.

I need to know how to mock the instance of GetResult,QueryResult and MutationResult to return mocked result and check the next underlying logic using JUNIT.

Something along lines of Mockito.when(coushbaseDao.getDocument(anyString()).return(Mocked Instance Of GetResult);

If this is not possible with mockito, I can use Powermock also.

If there is any reference for the same , kindly point me in the right direction.

Thanks,
Manzoor

@praneeth.bokka can you assist ?

Hi Manzoor,

tldr; It’s not easy to mock the Java SDK 3 API.

The Couchbase *Result classes don’t have public constructors, so you’d need to create them via reflection or some other sneaky technique.

I’m afraid we’re not heavy users of mocking frameworks inside the Java SDK team at Couchbase, so we’re probably not the best resource for tips on how to best apply Mockito or Powermock.

The tools we use most often are:

  • CouchbaseMock - An embedded server that pretends to be Couchbase but just stores everything in memory.
  • TestContainers - A library for running a real Couchbase Server in Docker.

Kevin Wittek and Michael Nitchinger gave a great presentation on using TestContainers with Couchbase at Couchbase Connect 2021.

Both CouchbaseMock and TestContainers are best described as integration testing tools, rather than tools for unit tests. If you’re determined to do unit testing of your components, I can suggest a couple of alternatives:

A) Rework your DAO interface to hide the details of the storage backend. Instead of returning a Couchbase GetResult or MutationResult, your DAO methods could return a class / interface of your own design. This has the added benefit that if for some reason you decide to store your data in some other database, the scope of that change will be much smaller.

B) Mock at a higher level. Instead of mocking your CouchbaseDao, mock the service that uses CouchbaseDao (or create one… although that would be essentially the same as the first suggestion).

The Mockito home page has these words of advice:

Do not mock types you don’t own.

I believe that applies here, and should steer you towards suggestions A & B. Maybe the Mockito experts on StackOverflow have an idea? If you learn something, please share it in this thread.

Thanks,
David

1 Like

Hi David,

CouchbaseMock hasn’t been updated since Jan 2020.

Is there any other workaround to test/write Test cases for Java SDK 3.3?

Similar test cases in SDK 2.X

@Test
    public void saveDocumentShouldSucceed() throws Exception {
        Map<String,Object> test = dummyItem();
        Mockito.when(couchbaseConfiguration.getBucket()).thenReturn(mockBucket);
        Mockito.when(mockBucket.upsert(Mockito.any() )).thenReturn(createDocument("document::identifier", dummyItem()));
        assertNotNull(dao.upsertDocument("document::identifier", 5, test));
    }

Thanks.

Hi @strike,

As far as I know, the situation hasn’t changed since December 2021.

I see how the lack of interfaces is complicating your migration effort, and I empathize with you.

In the particular case of the code you shared, my personal view is that unit tests for DAOs are less valuable than integration tests. Testing against a real server managed by Testcontainers would give you confidence your system is working end-to-end.

Thanks,
David

Hi David,

Do we have an example/how-to/blog post on Testcontainers with Couchbase?

Thanks,
Haro.

Here’s a crash course in using Testcontainers with Couchbase.

The Testcontainers documentation describes how to use Testcontainers with various testing frameworks. The following code just shows how to create a CouchbaseContainer and connect to it using Couchbase Java SDK 3.x.

Maven dependencies:

<dependency>
    <groupId>com.couchbase.client</groupId>
    <artifactId>java-client</artifactId>
    <version>3.3.3</version>
</dependency>

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.17.3</version>
<!--    <scope>test</scope> --> 
</dependency>

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>couchbase</artifactId>
    <version>1.17.3</version>
<!--    <scope>test</scope> -->
</dependency>

Example source:

String bucketName = "my-bucket";

// Real code would share the container between tests, since it takes a while to start.
try (CouchbaseContainer couchbase = new CouchbaseContainer("couchbase/server:7.1.1")
    .withBucket(new BucketDefinition(bucketName))) {

  couchbase.start();

  Cluster cluster = Cluster.connect(
      couchbase.getConnectionString(),
      couchbase.getUsername(),
      couchbase.getPassword()
  );

  try {
    Bucket bucket = cluster.bucket(bucketName);
    bucket.waitUntilReady(Duration.ofSeconds(10));

    bucket.defaultCollection().upsert("foo", "bar");
    System.out.println(bucket.defaultCollection().get("foo"));

  } finally {
    cluster.disconnect();
  }
}