@withjoy/server-core-test v1.2.0
server-core-test
Test Suite support for Gateway Services and other Server components
Getting Started
You do not need any external components / dependencies to manage this module.
# set up Node
nvm use
yarn install
# run the Test Suite
yarn test:setup # once only, then ...
yarn testAll shell tasks for the Service are executed with scripts from package.json.
Here are some of the basics:
# compile TypeScript => `dist/*`
yarn build
yarn test:build
# run the Test Suite
yarn test
yarn test:only test/examples/canary.tsUpgrading @withjoy/server-core in this repo's 'package.json'
Because of complex dependences, resolved by
{ "dependencies": { "@withjoy/server-core": "*" } }in this module{ "dependencies": { "@withjoy/server-core": "*" } }+{ "resolutions": { "@withjoy/server-core": "^#.##.#" } }in the importing Service
... we avoid TypeScript hell, like
error TS2344: Type 'EventContext' does not satisfy the constraint 'Context'.
Types have separate declarations of a private property '_userId'.However (last thing I knew), we can't specify { "resolutions" } locally because it mucks up everything again.
Instead, we have to toss our lock file every time we need to upgrade.
# wait for it to publish
yarn info --json @withjoy/server-core | jq -r '.data.version'
# full relock
rm yarn.lock && yarn installUsing this Module
This module introduces a lot of Features -- they are covered below.
This module also represents a baseline for shared Test Suite tooling. The modules in this repo are intended to work as a unified set.
We've had various testing library modules conflict with each other;
the set of libraries in this module's 'package.json' should all play together nicely.
Hence the recommendation about peerDependencies below.
Sometimes the library versions are also coupled to the test framework (jest) version.
Several of these framework dependencies are "runtime-only" -- they are never imported or required --
which makes it challenging for yarn to infer the 'correct' version for "*".
That is why peerDependencies wildcards are not the correct strategy for test framework modules.
Each version of server-core-test is built to work with the specific set of framework versions in its own devDependencies.
Call out those same frameworks and versions in your project's devDependencies.
Best Practices for Adopting this Module
- install
@withjoy/server-core-testin your Service with an equal or later version of its@withjoy/server-coredependency
{
"name": "my_service",
"resolutions": {
"@withjoy/server-core": "^1.2.3"
},
"dependencies": {
"@withjoy/server-core": "*"
},
"devDependencies": {
"@withjoy/server-core-test": "~0.1.0"
}
}- use its toolkits as
peerDependencieswhen possible- eg.
nock,node-mocks-http, and their matching@types/*module
- eg.
{
"name": "my_service",
"peerDependencies": {
...
"@types/jest": "*",
"@types/mock-fs": "*",
"@types/sinon": "*",
"@types/supertest": "*",
"jest": "*",
"mock-fs": "*",
"nock": "*",
"node-mocks-http": "*",
"sinon": "*",
"supertest": "*",
"ts-jest": "*",
...
},
}- call out specific test framework versions in
devDependenciesjestandts-jestare the usual suspectsserver-core-testis built to work with the framework versions in its owndevDependencies
{
"name": "my_service",
"devDependencies": {
...
"@types/jest": "___",
"jest": "___",
"ts-jest": "___",
...
}- add the Standard
test:*Tasks to yourpackage.json- (and always run them from the root of your repo -- there is path-related weirdness)
- @see the Features documentation below
- the
"..."is a bit custom per repo -- it's best to copy-and-refine these Tasks from a similar repo's 'package.json'
{
"name": "my_service",
"scripts": {
"test:build": "...",
"test:database": "...",
"test:debug": "...",
"test:jest": "...",
"test:only": "...",
"test:repl": "...",
"test:setup": "yarn test:jest",
"test": "..."
},
}- create a
test/.envfile in your repo with the following minimum configuration
| Name | Purpose |
|---|---|
| JEST_CANARY_SPEC | a repo-relative reference to any of its Test Suites, big or small, for use by test:jest |
| ... and if your repo has a Postgres database ... | |
| POSTGRES_HOST | Postgres / typeorm configuration referencing a unique database for your Test Suite |
| POSTGRES_PORT | ... |
| POSTGRES_DATABASE | ... |
| POSTGRES_USER | ... |
| POSTGRES_PASSWORD | (non-blank value) |
- integrate the module into your
jestconfiguration /jest.config.jsglobalSetupshould reference '@withjoy/server-core-test/dist/globalSetup/index.js', or a local JavaScript file which includes & leverages itsetupFilesAfterEnvshould reference a local TypeScript file which sets up your Service
module.exports = {
globalSetup: '<rootDir>/node_modules/@withjoy/server-core-test/dist/globalSetup/index.js',
setupFilesAfterEnv: [
'<rootDir>/test/helpers/setupFilesAfterEnv.ts',
],
};- add custom integration points for your Service
- a
setupFilesAfterEnvfile (see above), which configures the Database Lifecycle (see below) - a
fakeComponentsfile, to provide Fake Components (see below) - @see the Features documentation below
- it's best to copy-and-refine these files from a similar repo
- a
- provide CircleCI config & setup
- the details of setting up a Postgres sidecar (etc.) can be derived from the working example of another Service.
- if your Workflow uses an Alpine container, make sure to install
bashin any Step which runs atest:*task
apk update && apk add bashExample Module Adoption PRs
Here are some example PRs which demonstrate the steps required for integrating this module into a Service:
for version 0.1.x (the initial adoption)
- backend-seed
- event_service
- identity_service, specifically this commit
then, later on,
- media_service, a new introduction with little back-correction needed
- product_service, with a significant amount of in-place changes to meet 'server-core-test' conventions
- email_service, a massive upgrade which integrates this module, and applies other repo Best Practices du jour
Features
Standard test:* Tasks
Add the following to the 'scripts' of your package.json:
| Name | Purpose |
|---|---|
| test:build | use your Test Suite's tsconfig file to compile and help identify TypeScript issues, etc. |
| test:database | drop and reconstruct your Test Suite database with the latest typeorm schema |
| test:debug | run your Test Suite with the Node 'inspect' debugging client |
| test:jest | recompile your Test Suite in jest (so that it won't timeout the first Test Case) |
| test:only | run specific Test Cases without a Coverage summary (plus other refinements of jest) |
| test:repl | run your Test Suite within the Node repl environment |
| test:setup | run test:database + test:jest in sequence to prepare a clean environment |
If you have set up a custom Postgres superuser, you may specify it via the environment:
POSTGRES_SUPERUSER=root yarn test:setup
POSTGRES_SUPERUSER=root yarn test:databasesetupFilesAfterEnv File
This is a custom file, implemented by your Service, which makes use of jest's Lifecycle.
It leverages the setupAfterEnv method in our module to provide setup & teardown of
- the Database Lifecycle (see below)
nockconfiguration, which ensures that your Test Suite will never make an external HTTP request
The setupAfterEnv helper is not a named export; you'll need to import it from 'dist/*'.
The reasons are explained in other places of this codebase.
import { setupAfterEnv } from '@withjoy/server-core-test/dist/setupFilesAfterEnv/index';Database Lifecycle
We've found its a good practice to have a real Postgres database backing our Test Suites for use by typeorm.
It take a bit more time to run, and requires single-threaded Test execution (to provide exclusive access to the database),
but significantly reduces the need for mocking and emulates real-world use cases RE: foreign keys, index collisions, etc.
In your Service's setupFilesAfterEnv file, use setupAfterEnv({ database }) to provide:
- a full count of all Fixtures saved to in the database, which (when 0) tells the Lifecycle that the database instance is empty and safe to use
- any "seeding" logic which saves global Fixtures to the database
- "weeding" logic, which deletes all Fixtures from the database, resulting in a Fixture count of 0
Fake Components
A "Fake Component" is a Class instance used by your Test Suite and associated with your Service's Context implementation.
Your Service is expected to implement:
- a
ContextClass- extending
@withjoy/server-core's base Class - whose constructor accepts standard and Service-specific arguments (as separate parameters)
- extending
- a TypeScript definition for the Service-specific Context arguments
- a Context constructor Function
- a Context "injection handler" Function, which returns an
expressmiddleware Function that binds a Request and a Context
There are more details beyond the scope of this README. Suffice is to say; there are opinionated conventions.
Leveraging your Service's Context tooling, create a custom fakeComponents file which:
- implements a
FakeComponentProvidersingleton, which (at a minimum) can construct a Context and an ApolloServer - exports the singleton's
testSetup*methods for use by your Test Suite
Fixture Factories
A "Fixture" is a populated Model, ready to be saved to the Test Suite database.
We have extended the rosie module with some typeorm tooling to suit our purposes.
There is one "Factory" per Model, and each should return an instance of the Model (a Fixture) with all required fields (etc.) satisfied; the caller should be able to save the resulting Fixture to the database without specifying any custom arguments.
There are two core methods on a Factory;
buildcan be used to produce a Fixture which is used within the scope of one Test Caseconstmust be used for any shared Fixture, one that is declared outside the scope of a specific Test Case
typeorm will mutate a Model in-place, so re-saving a shared Fixture across more than one Test Case will cause problems.
The purpose of Factory#const is to freeze the populuated Model so that it cannot be saved by accident.
The 'clone'-related methods on a Factory are intended for use with const Fixtures.
Fake Config
Normally, the Service-wide configuration is a shared mutable singleton Object initially read from test/.env.
There are times it is useful to mutate the configuration to meet the needs of a Test Case.
You may implement a FakeServerConfigProvider which can mutate your singleton config Object in-place,
with the ability to safely restore back to its original state when your Test Case is done.
Much like with Fake Components,
- implement a
FakeServerConfigProvidersingleton constructed around your singleton config Object - exports the singleton's
*FakeServerConfigmethods for use by your Test Suite
Publishing
To publish a new version of this module,
- do not up-version on your development branch
- merge your fixes into
master - from the
masterbranch,
yarn version --patch # or whatever is suitableAs a follow-up,
package.jsonis up-versioned- a semver-ish tag is pushed to Git
- CircleCI will perform the
yarn publishoperation when it detects the tag - it's ready once the 'versions' in
yarn info @withjoy/server-corehave been updated
CircleCI
Its Project uses the following Environment Variables:
- ARTIFACTORY_TOKEN
- NPM_TOKEN
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago