Joel Andrews is a polyglot developer living on the rainy west coast of Canada. He fills a variety of roles at Kashoo including backend developer, database admin, DevOps guy, product owner and occasionally web and Android frontend developer. He has a deep and abiding love for Couchbase (especially Couchbase Mobile) that makes him completely unbiased when discussing the pros and cons of any data storage solution.

In my previous blog post, I introduced synctos, a handy open source tool that we built and use at Kashoo to ease the process of creating comprehensive sync functions for Couchbase Sync Gateway. Near the end of that post I alluded to the fact that synctos includes a built-in test-helper module that helps you to write tests that validate your document definitions. It’s always a good idea to test your code/configuration for bugs, and your synctos document definitions are no different.

In this post I will walk you through what it takes to get started writing your own specifications/test cases. Before continuing, I suggest reading the introductory post, if you haven’t already, to ensure you have a general understanding of what synctos is all about and how it works.

First, you’ll need to install Node.js to use synctos. Once installed, you should create an empty project directory with a new file called “package.json”:

This file tells the Node.js package manager (npm) which dependencies synctos and your test cases will need: expect.js for test assertions, mocha for running your tests, and simple-mock for mocking/stubbing functions from the Sync Gateway sync function API. It also specifies the “test” command that will execute your tests with mocha.

Next, run the following command from the root of your project directory to download the packages it needs to its local “node_modules” directory:

The project will need some document definitions, so create “my-example-doc-definitions.js” in the project’s root directory:

As you can see, this is a very simple document definition for demonstration purposes. Your own document definitions will undoubtedly be larger and more complex, but the same principles apply. The file defines a single document property (a required string called “foo” whose value must satisfy the specified regular expression), a simple type filter that determines the document’s type based on the contents of the implicit “type” property (i.e., a document’s “type” property must be “exampleDoc” to match this document type), and document channels that are constructed dynamically from the document ID.

Now create a new file called “generate-sync-function.sh” in the root directory of your project:

This file will be used to generate the sync function in the project’s “build” directory as “my-example-sync-function.js”. Make sure “generate-sync-function.sh” is executable by running:

At this point, you have everything you need to generate a sync function from the document definitions file:

./generate-sync-function.sh

If you look in the “build” directory, you will find a fully-formed Sync Gateway sync function file called “my-example-sync-function.js”. If you felt so inclined, you could insert the sync function’s contents into a Sync Gateway configuration file now. When doing so, remember to surround the sync function with backticks/backquotes (`), since it is more than one line long.

Now it’s time to validate that sync function! Create a directory called “test” in the root of the project and add a file called “my-example-spec.js”:

This is the skeleton of the specification file. The first two lines of the file import the synctos test-helper module and the error message formatter, which will greatly ease the process of writing test cases. The “describe” block will encompass all of the code that we add in later steps.

Next, add the following snippet inside the “describe” block of the specification file:

This block ensures that the test-helper module is re-initialized at the start of each (i.e., before each) test case with the contents of the generated sync function.

Below the “beforeEach” block and still inside the “describe” block, add the following test case:

Now we’re getting somewhere. Here we’ve defined the document that we’d like to test and we’re asserting that the document can be created because it meets the criteria specified by the document definition. The second parameter of the “verifyDocumentCreated” function expects a complete list of the document channels that are accepted for the write option, which allows you to verify that the document definition’s channel assignment logic is correct.

How about a document that is invalid? Add another test case:

Since the document’s “foo” property does not match the regular expression that was specified in the document definition, we expect that this document will be rejected. Some notes on the arguments to the “verifyDocumentNotCreated” function:

  1. This is the document under test.
  2. This is the expected document type name.
  3. A complete list of all errors that are expected due to the failure. Note that the “errorFormatter” exposes formatter functions for all supported error types.
  4. A complete list of the expected document channels that are accepted for the write operation. As in the previous test case, this helps to verify that correct channels are assigned to the document during the operation.

Now that there are some test cases, you can run the test suite by executing the following from the project root:

You’ll find that both test cases ran and passed (indicated by a green check mark next to each)! If ever a test case fails, mocha (the test runner tool) will generate a detailed error message that should help you to figure out where to find the problem.

So, what’s next? There is plenty more that the test-helper module can do to help you write your specifications. Your next stop should be the test-helper module’s documentation to learn what other options are available; notably, you’ll find that you can also verify your sync function’s behaviour when a document is replaced or deleted (handy if your documents or their properties are meant to be immutable). The validation-error-message-formatter’s documentation should also be a big help in verifying errors that are returned when a document revision is rejected. And finally, you’ll find the complete source code for these examples on GitHub.

Happy testing!

Posted by Laura Czajkowski, Developer Community Manager, Couchbase

Laura Czajkowski is the Snr. Developer Community Manager at Couchbase overseeing the community, our incentive programs, Experts and Champions group, meetups, and defining our presence at developer events. She’s also responsible for our monthly developer newsletter and engaging with our community in various forms. Laura has been active in Open Source communities since 2000 and has been involved in various activities, including leading and organising conferences on software testing, documentation, and advocacy. Laura is an Open Source advocate and regular conference speaker who is passionate about getting people – everyone from primary school students to technology professionals – involved in Open Source communities both on IRC and in face-to-face discussions, she is easily found online at @czajkowski on twitter and on freenode.

Leave a reply