DZone
Database Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Database Zone > Testing Spring Data Couchbase Apps With TestContainers

Testing Spring Data Couchbase Apps With TestContainers

Docker doesn't always play nicely with your apps, but learning how to configure your work for testing can help ease the pain.

Laurent Doguin user avatar by
Laurent Doguin
·
Jan. 23, 17 · Database Zone · Tutorial
Like (1)
Save
Tweet
5.38K Views

Join the DZone community and get the full member experience.

Join For Free

In a previous series of blog posts, I explained how to use TestContainers for your Java JUnit tests. Some of the issues we did not address were about how to test N1QL, create your own buckets, index, etc. This post will be about building Spring Data Couchbase test cases and cover theses questions we left out.

Hardwire Unconfigurable Ports

One of the limitations we currently have on Couchbase Server is that you cannot change some of the default port. This is a problem with Docker, as it’s changing ports only notified otherwise. This can be great because it means you can have several Couchbase instances running on the same machine. But unfortunately, that won’t work, so some ports will have to be fixed. This can be declared fairly easily with TestContainers using the addFixedExposedPort method.

@Override
protected void configure() {
    addExposedPorts(8091, 11207, 11210, 11211, 18091, 18092, 18093);
    addFixedExposedPort(8092, 8092);
    addFixedExposedPort(8093, 8093);
    addFixedExposedPort(8094, 8094);
    addFixedExposedPort(8095, 8095);
    setWaitStrategy(new HttpWaitStrategy().forPath("/ui/index.html#/"));
}


With that out of the way, our Java SDK will be able to connect to N1QL.

Abstract Spring Data Couchbase Docker Test Case

The goal here is to create an abstract test case that will be used by any class that needs a Couchbase instance and Spring Data Couchbase configured. It starts as in the previous posts by instantiating a CouchbaseContainer field. Since we are testing Spring Data we configure support for Index, Query and let’s throw in FTS for later.

To make sure this class will run tests for your application, add the @RunWith(SpringRunner.class) annotation. And to make sure your application configuration is tested as well as our custom configuration, add @SpringBootTest(classes = {GittalentBackendApplication.class,

AbstractSPDataTestConfig.CouchbaseTestConfig.class}).

Now talking about custom configuration, what do we need? We want to override the default Couchbase configuration of the app. To do so we need to implement a CouchbaseConfigurer. This interface defines all the bean needed for Spring Data Couchbase to work properly. It provides instances for CouchbaseEnvironment, ClusterInfo, Cluster, and Bucket.

They will all come from our CouchbaseContainer setup before running the tests. So we need to make sure that the Container is running and ready before initializing all the beans. This can be achieved by adding an init() method annotated with @PostConstruct. This will allow us to first make sure the container is running, then set up additional stuff. In the following example we setup a bucket called default and setup the Index type to be MOI.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {GittalentBackendApplication.class, AbstractSPDataTestConfig.CouchbaseTestConfig.class})
public abstract class AbstractSPDataTestConfig {

    public static final String clusterUser = "Administrator";
    public static final String clusterPassword = "password";

    @ClassRule
    public static CouchbaseContainer couchbaseContainer = new CouchbaseContainer()
            .withFTS(true)
            .withIndex(true)
            .withQuery(true)
            .withClusterUsername(clusterUser)
            .withClusterPassword(clusterPassword);

    @Configuration
    static class CouchbaseTestConfig implements CouchbaseConfigurer {

        private CouchbaseContainer couchbaseContainer;

        @PostConstruct
        public void init() throws Exception {
            couchbaseContainer = AbstractSPDataTestConfig.couchbaseContainer;
            BucketSettings settings = DefaultBucketSettings.builder()
                    .enableFlush(true).name("default").quota(100).replicas(0).type(BucketType.COUCHBASE).build();
            settings =  couchbaseCluster().clusterManager(clusterUser, clusterPassword).insertBucket(settings);
            couchbaseContainer.callCouchbaseRestAPI("/settings/indexes", "indexerThreads=0&logLevel=info&maxRollbackPoints=5&storageMode=memory_optimized", "Administrator", "password");
            waitForContainer();
        }

        public void waitForContainer(){
            CouchbaseWaitStrategy s = new CouchbaseWaitStrategy();
            s.withBasicCredentials(clusterUser, clusterPassword);
            s.waitUntilReady(couchbaseContainer);
        }

        @Override
        @Bean
        public CouchbaseEnvironment couchbaseEnvironment() {
            return couchbaseContainer.getCouchbaseEnvironnement();
        }

        @Override
        @Bean
        public Cluster couchbaseCluster() throws Exception {
            return couchbaseContainer.geCouchbaseCluster();
        }

        @Override
        @Bean
        public ClusterInfo couchbaseClusterInfo() throws Exception {
            Cluster cc = couchbaseCluster();
            ClusterManager manager = cc.clusterManager(clusterUser, clusterPassword);
            return manager.info();
        }

        @Override
        @Bean
        public Bucket couchbaseClient() throws Exception {
            return couchbaseContainer.geCouchbaseCluster().openBucket("default");
        }

    }
}


Once we have this abstract test case, all we have to do next is create a class that extends it and start writing tests! Here we can inject services from our application as well as a lower level bucket. What you see in this test is first a call to an importer service that creates documents. Then we create an Index on the default bucket and test a query on it.

public class GitTalentGHImportTests extends AbstractSPDataTestConfig {

    @Autowired
    private GithubImportService githubImportService;

    @Autowired
    private Bucket bucket;

    @Test
    public void importDevAdvocateTeam(){
        githubImportService.importOneDeveloper("ldoguin");
        N1qlQueryResult result = bucket.query(N1qlQuery.simple("CREATE PRIMARY INDEX ON default"));
        N1qlQuery query = N1qlQuery.simple("SELECT * FROM default WHERE developerInfo.username = 'ldoguin'");
        result = bucket.query(query);
        N1qlQueryRow row = result.rows().next();
        Assert.assertNotNull(row);
    }
}


As you can see, once the Abstract test case is created, the amount of code is really minimal and corresponds exactly to what you want to test.

Spring Data Spring Framework Data (computing) app Testing Docker (software) Test case

Published at DZone with permission of Laurent Doguin, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Legacy Modernization and Hybrid Cloud with Kafka in Healthcare
  • 8 Must-Have Project Reports You Can Use Today
  • A Simple Guide to Rust Data Types
  • Introduction to Word Vectors

Comments

Database Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo