Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PRO-2913: add writing tests for modules documentation #237

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
3 changes: 2 additions & 1 deletion docs/.vuepress/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ module.exports = {
'guide/front-end-helpers.md'
]
},
[ 'guide/media.md', 'Working with images and media' ]
[ 'guide/media.md', 'Working with images and media' ],
[ 'guide/writing-tests-for-modules.md', 'Writing tests for modules' ]
]
},
{
Expand Down
236 changes: 236 additions & 0 deletions docs/guide/writing-tests-for-modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# Writing tests for modules
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved

BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
Tests are useful to guarantee that the requirements of your application are met over time.

You don't have to test over and over again the same scenarios, it's a time saving tool.

There are many kind of tests
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved

- [unit testing](https://en.wikipedia.org/wiki/Unit_testing) where we test a single part of the application, a single
module, file commonly referred as a unit
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
- [integration testing](https://en.wikipedia.org/wiki/Integration_testing) where we test multiple parts of an
application working together as a group
- [system testing](https://en.wikipedia.org/wiki/System_testing), sometimes referred as [end-to-end testing](https://www.indeed.com/career-advice/career-development/end-to-end-testing) where we test the entire application
- and many more

This documentation focuses on how to test Apostrophe modules, not Apostrophe itself using integration testing.

Modules should be stand-alone npm packages.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we expand this to something like: "Modules should be stand-alone npm packages. They need to be hosted somewhere that our testing can access. For this example, we are hosting this module in a github repo. We will use a fictitious module named article to illustrate how to write tests for modules."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad -> "github" -> "GitHub"


We will use a fictitious module named `article` to illustrate how to write tests for modules.

## Requirements

- A running MongoDB server

## Setup

This setup assumes you will use the following packages

- [apostrophe](https://www.npmjs.com/package/apostrophe): *ApostropheCMS is a full-featured, open source CMS built with Node.js that seeks to empower organizations by combining in-context editing and headless architecture in a full-stack JS environment*
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
- [eslint](https://www.npmjs.com/package/eslint): *ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code*
- [eslint-config-apostrophe](https://www.npmjs.com/package/eslint-config-apostrophe): *An ESLint configuration for ApostropheCMS core and officials modules*
- [mocha](https://www.npmjs.com/package/mocha): *Simple, flexible, fun JavaScript test framework for Node.js & The Browser*
```sh
npm install --save-dev apostrophe eslint eslint-config-apostrophe mocha
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linting and integration testing aren't the same thing. Should we acknowledge this somewhere, maybe mention we're including linting too and what that gets us?

```

You will need to add some new scripts to your `package.json`.

```json
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
{
"name": "article",
"version": "1.0.0",
"description": "A fictitious Apostrophe module",
"main": "index.js",
"scripts": {
"lint": "npm run eslint",
"eslint": "eslint .",
"test": "npm run lint && mocha"
},
"keywords": [],
"author": "",
"license": "ISC"
}
```

Add the following to `.eslintrc.json` to tell eslint to use ApostropheCMS ESLint confiuration.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good chance to say why and what this gets us.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an additional sentence here or below the code block at around line 77 should be added. Something like:
"While not strictly part of integration testing, running ESLint alongside the Mocha test(s) reduces potential code error and ensures your module code conforms to the style of other Apostrophe modules."

Note: I'm not sure if this is the most accurate statement. You might want to edit it.


```json
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
{
"extends": [ "apostrophe" ]
}
```

When you call `npm test`, it will lint your files according to [eslint-config-apostrophe](https://www.npmjs.com/package/eslint-config-apostrophe) rules and run the tests using [mocha](https://www.npmjs.com/package/mocha).

### Test folder

You will need a `test/package.json` file referencing the repository URL of your module. Please replace `article` & `%article-repository-url%` with your module informations.

e.g. for the module [@apostrophecms/login-hcaptcha](https://github.com/apostrophecms/login-hcaptcha) we use

> `"@apostrophecms/login-hcaptcha": "git://github.com/apostrophecms/login-hcaptcha.git"`
>

```json
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
{
"/**": "This package.json file is not actually installed.",
" * ": "Apostrophe requires that all npm modules to be loaded by moog",
" */": "exist in package.json at project level, which for a test is here",
"dependencies": {
"apostrophe": "^3.26.0",
"article": "%article-repository-url%"
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
}
}
```

You don’t want to commit test files artifacts to git, please add the following to your `.gitignore` file

```gitignore
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
# Test files
/test/apos-build
/test/public
/test/locales
```

## Writing tests

We start with a single test file `test/index.js`.
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved

As your tests grows, you can break it down into multiple files based on your liking.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"As your test grows, ..."
or
"As your tests grow, you can break them down..."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my mistake.
Grammatically the first one doesn't make exact sense. Use:
"As your tests grow, you can break them down ..."


Mocha does not play well with arrow-functions, more info at [https://mochajs.org/#arrow-functions](https://mochajs.org/#arrow-functions)

<AposCodeBlock>
```javascript
const assert = require('assert').strict;
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
const t = require('apostrophe/test-lib/util.js');

// getAppConfig is used to set options for Apostrophe and the module you want to test
const getAppConfig = function (options = {}) {
return {
article: {
options: {
// Pass the required options here, if your module doesn't have any options
// please skip the `options` attribute
...options,
custom: true
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
}
}
};
};

describe('article', function () {
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
let apos;

this.timeout(t.timeout);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment about whether this is optional and that you need to set an environment variable? I know you have a description of setting it below, but a reminder here would be good.


before(async function () {
apos = await t.create({
shortName: 'article',
testModule: true,
modules: getAppConfig()
});
});

after(async function () {
await t.destroy(apos);
});

it('should have module options', function () {
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved
const actual = apos.modules.article.options;
const expected = {
apos,
custom: true
};

assert.deepEqual(actual, expected);
});
});
```
<template v-slot:caption>
test/index.js
</template>
</AposCodeBlock>

### Dependencies

[apostrophe/test-lib/util.js](https://github.com/apostrophecms/apostrophe/blob/main/test-lib/util.js) contains some logic to create and destroy apostrophe instances for testing purposes.

It exposes:

- `t.create` to create an apostrophe instance
- `t.destroy` to delete the database when the apostrophe instance is destroyed
- `t.createAdmin` to create an admin user
- `t.getUserJar` to get a cookie jar for the admin user
- `t.timeout` can be set using an environment variable `TEST_TIMEOUT`, e.g.`TEST_TIMEOUT=5000 npm test`

[testModule](https://github.com/apostrophecms/apostrophe/blob/main/index.js#L468) will tweak the Apostrophe environment suitably for unit tests (i.e. set default modules options, check test files location, build module paths)

- `describe` & `it` functions are provided by mocha, more info at [BDD](https://mochajs.org/#bdd)
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved

### Output

By running the test using `npm test` command, you should see the output below
BoDonkey marked this conversation as resolved.
Show resolved Hide resolved

```
$ npm test

> @apostrophecms/[email protected] test
> npm run lint && mocha


> @apostrophecms/[email protected] lint
> npm run eslint


> @apostrophecms/[email protected] eslint
> eslint .



Apostrophe Login hCAPTCHA
Listening at http://127.0.0.1:7780
✓ should have module options


1 passing (909ms)
```

### What's next?

You can now test the additional methods you've provided with your module on [self](https://v3.docs.apostrophecms.org/reference/module-api/module-overview.html#methods-self).

You can use any existing apostrophe modules with a `test` folder as a reference.

You'll find below a non exhaustive list of such modules:

- [@apostrophecms/form](https://github.com/apostrophecms/form/blob/main/test/test.js)
- [@apostrophecms/login-hcaptcha](https://github.com/apostrophecms/login-hcaptcha/blob/main/test/test.js)
- [@apostrophecms/svg-sprite](https://github.com/apostrophecms/svg-sprite/blob/main/test/test.js)
- [@apostrophecms/scheduled-publishing](https://github.com/apostrophecms/scheduled-publishing/blob/main/test/test.js)
- [@apostrophecms/sitemap](https://github.com/apostrophecms/sitemap/blob/main/test/test.js)

## FAQ

### MongoServerSelectionError: connect ECONNREFUSED 127.0.0.1:27017

Apostrophe assumes by default that there is MongDB server running on `127.0.0.1:27017`.

You can change it by using the environment variable `APOS_MONGODB_URI`.

```sh
APOS_MONGODB_URI=mongodb://%mongodb-address% npm test
```

### Mocha doesn't seems to work with apostrophe

Starting from Apostrophe 3.26.0, we now support Mocha 9+
([Apostrophe 3.26.0 Changelog](https://github.com/apostrophecms/apostrophe/blob/main/CHANGELOG.md#3260-2022-08-03))

If you are using an older version of Apostrophe, please use Mocha 8.

```sh
npm install --save-dev mocha@8
```