MongoDB Meta Driver Project
The MongoDB Meta Driver is an attempt to formally specify the client library interface for MongoDB. Eventually the documents and this specification will enhance and replace the current MongoDB driver writing tutorial. In the meantime, all content in this repository should be considered incomplete, draft, and non-final.
The MongoDB Meta Driver strives to be language-independent, producing a specification that can be used and tested directly against (almost) any language. To do so, we make use of the Cucumber framework to define the specifications and translate them into tests against the driver code. It is our hope that this specification will eventually be applied to as many production drivers as possible, in order to better standardize driver functionality and provide a more consistent developer experience with MongoDB across languages.
The Meta Driver consists of two components: a specification of driver behavior, using the Cucumber behavior-driven testing framework; and a reference implementation that meets the specification. The Cucumber files are located in the features/ directory, and the implementation is split between the bson-ruby/ submodule (BSON implementation) and the lib/ directory (wire protocol and client interface).
In features/, the files with a .feature extension are the actual specifications for the driver. These are human-readable documents that describe the behavior the driver should exhibit in various scenarios. features/ also contains several .rb files, which contain Ruby code specifying the step definintions, transformations, and environment setup that bridges the specifications to tests against the driver code. For more information on how this process works, see this introduction to Cucumber and its underlying philosophy, as well as the following section.
Running the Tests
In order to run the tests defined by the Meta Driver specifications, you will need the cucumber utility, part of the Cucumber Ruby Gem. If necessary, install RubyGems, then run gem install cucumber, or run bundle (gem install bundle first, if necessary) at the root of the repository, to install Cucumber. Installing with bundle will pull in all the other dependencies needed by the reference driver implementation and its tests.
To run the tests with default options, navigate to the root of the repository and execute cucumber. This will run all the tests described in the features/ directory. To run just a subset of the tests, execute cucumber --tags @tag1,@tag2 to run tests marked with @tag1 or @tag2. Many of the tests require the MongoDB server (mongod) to be running on the default port (27017) on the system the tests are being run on. Eventually server process management will be added to the testing code, so that this will not have to be done by hand; for now, it must be started manually. If the tests are run on Travis CI, the Travis configuration file provided in the repository will take care of this for you.
For more information about Cucumber, the Cucumber wiki is a good place to start. cucumber --help has a reasonable description of the different command-line options Cucumber supports. The Pragmatic Programmer's The Cucumber Book contains detailed information on using the Cucumber framework, with in-depth worked examples.
Binding the Meta Driver Specification To Another Driver
The Meta Driver specification is designed to be usable with any language with a specification runner that supports Cucumber .feature (Gherkin) files. A partial listing of Cucumber-like frameworks for different languages can be found here. Even if your language is not listed there, there is a good chance a Google search will turn up something.
Here is a high-level view of the process:
- Import the Meta Driver repo as a submodule. Since the specifications are not set in stone and may have bugs when applied to particular languages, it's probably a good idea to fork the official repo first.
- In the future the Meta Driver will be restructured to make this process easier. In particular, the reference implementation should be separate from the specifications, so that projects that need just the specifications (e.g., other language drivers) won't need to pull in a bunch of Ruby code they don't need.
- Find, install, and learn how to use a Cucumber/Gherkin implementation for your language.
- Write step definitions for your driver.
- This is the bulk of the work. Since step definitions are essentially code in your driver's language, and different Gherkin runners embody different design decisions, your step definitions will probably look at least somewhat different from the Ruby ones in this repository.
- That said, the overall structure of the step definitions tends to be similar between languages and implementations, so the definitions provided in this repo can still be a useful starting point. Most likely you will be following the same basic pattern as with Cucumber for Ruby: establishing a mapping between regular-expression matches against the .feature files and code that calls into your driver.
- You should understand how step transforms work, if the implementation of Cucumber for your language supports them. If it does not, try to find one that does. Transforms are used extensively in the reference Ruby implementation, and save a large amount of code duplication.
- Run your tests, and report any bugs you find, either in your driver, or (more likely at this point) bugs or warts in the Meta Driver specification.
The Meta Driver specifications are far from a finished state, and will always be an evolving standard. Therefore, if you choose to fork this repository, it is important to keep your version of the spec in sync with the official upstream version, to ensure you are conforming to the most up-to-date version of the spec. By the same coin, if you notice inconsistencies or problems in the specification that emerge while writing the step files for your language (for instance, subtle language-dependent assumptions in the .feature files), change the features as necessary (without introducing dependencies on your language; and only as necessary). If you do this, you should submit a pull request so that your changes can be merged back into the upstream version. While some slight differences in the .feature files between languages are acceptable, the specifications are more useful the more similar they all are. Ideally, the only difference between languages should be in the step files, though this may not always be possible.
- Add launching/killing the server to Cucumber hooks so that mongod does not have to be started by hand.
- Restructure the repo, separating out the specification and implementation, to make it easier to import just the specification as a submodule.
- Expand the specifications. Particularly, add support for more advanced CRUD operations, administration, and replica-set/shard behavior.
- Expand the documentation, particularly the documentation about binding the specification for new drivers, with recommendations about how to change the .feature files when needed in such a way as not to introduce new language dependencies into the specification.