This library supports testing @apparts-based backends that use Postgresql databases provide express based APIs.
- Add to your
package.json
in thescripts
section:"testOne": "jest", "test": "jest --watch --detectOpenHandles", "testCoverage": "jest --coverage"
- Create a
jest.config.js
file in the root directory of your project.const jestConfig = require("@apparts/backend-test").getJestConfig(); module.exports = { ...jestConfig, // additional config };
- Create a
config/db-test-config.json
orconfig/db-test-config.js
or export the values as an environment variable with the nameDB_TEST_CONFIG
with this content (as JSON, for the js file you’d need to export the object):{ "use": "postgresql", "postgresql": { "host": "localhost", "port": 5432, "user": "postgres", "pw": "", "db": "<dbname>", "maxPoolSize": 1, "connectionTimeoutMillis": 10000, "idleTimeoutMillis": 10000, "bigIntAsNumber": true } }
- Create tests. To use this library, start the tests with this on
top:
const { app, url, error, getPool } = require("@apparts/backend-test")({ testName: "<testname>", // apiContainer: myEndpoint, ...require("./tests/config.js") });
- Create a
tests/config.js
for storing test information that is valid for more than one test:const fs = require("fs"); module.exports = { schemas: ["schema-file-name-0001.sql" /*, ...*/] .map(name => fs.readFileSync(name).toString()), apiVersion: 1, };
- Run tests with
npm run test # for coverage report: npm run testCoverage # without watching file changes: npm run testOne
require("@apparts/backend-test")
returns a function with the following parameters:
testName : string
- (Required) The name of this particular test (needs to be unique). Used to create a database for this test to run in
apiVersion : int
- (Required) The API version of a versioned API, that is being tested
apiContainer: object
- The container for the functions you want
@apparts/types
checkApiTypes
to test against. If this parameter is not supplied,checkType
andallChecked
will throw an error. schemas : [string]
- Database schemas, to be run first to create all tables
databasePreparations : [async function : string]
- A list of functions (that can be async) that return strings, containing sql code that should be run to setup the database
testApp : express app
- If supplied, this app will be used as the express app, the tests will be performed on. Otherwise a default test app will be used.
require("@apparts/backend-test")
returns a function wich returns an
object with the following key/value pairs:
checkType : (any, string) => boolean
- A wrapper around the
checkType
function from the @apparts/types package. The first parameter ofcheckType
is already set (with value fromapiContainer
parameter) for convenience. allChecked : (string) => boolean
- A wrapper around the
==allChecked= function from the @apparts/types package. The first
parameter of
allChecked
is already set (with value fromapiContainer
parameter) for convenience. app : <express app>
- A express app. It lacks routes, these have to be added manually. A minimal middleware is already applied to this app (injection of database, body parser).
url : (string) => string
- A helper that turns a string to a
versioned version of this string, by prepending
/v/<apiVersion>/
to the string.apiVersion
is the value of the parameterapiVersion
. This allows to change the api-version of all calls with one variable. error : (string, ?string) => object
- A helper that creates an
object of the form of the body, produced by the
HttpError
from the package @apparts/error. Can be used with the supertesttoMatchObject
matcher. runDBQuery : async (async (<dbs>) => any) => any
- A function that
expects a function as parameter. This function receives a
@apparts/db
DBS
object as parameter. It’s return value will be awaited and returned byrunDBQuery
. getPool : () => <dbs>
- Returns a @apparts/db
DBS
object.
jest.config.js
:const jestConfig = require("@apparts/backend-test").getJestConfig(); module.exports = { ...jestConfig, // additional config };
config/db-test-config.json
as described above- Tests with
const { app, url } = require("@apparts/backend-test")({ testName: "<testname>", apiVersion: 1 }); test("My test", async () => { // requesting GET "/v/1/test" const response = await request(app).get(url("test")); expect(response.status).toBe(200); });
const {
app,
url,
checkType,
allChecked,
error,
getPool,
} = require("@apparts/backend-test")({
testName: "<testname>",
apiContainer: require("./myEndpoint"),
// Returns everything that is the same for all endpoints of this
// APIs version: apiVersion, schemas
...require("./tests/config.json") ,
// Insert values for the tests to use.
databasePreparations: [
// Common setup queries can be stored in a file
require("./tests/insertUsers.sql.js"),
// Simple insertations
() => 'INSERT INTO "myTable" (myCollumn) VALUES (1), (2)';
// More complicated calculated values
async () => {
const hash = await require("bcryptjs").hash("password123", 10);
return `INSERT INTO "passwords" (password) VALUES (${hash})`;
};
],
});
const request = require("supertest");
describe("GET test", () => {
// Using a variable for the function name makes it easy to copy this
// test for another endpoint and not forgot to change the function
// name in some places.
const functionName = "myEndpoint";
test("Check return code", async () => {
// Requesting GET "/v/1/test", using the url function. This makes
// it easy to copy this file, edit the tests to reflect api changes
// and thus reuse it for the next api version.
const response = await request(app).get(url("test"));
expect(response.status).toBe(200);
// Checking against the database
// const dbs = getPool();
// await dbs.raw("SELECT ...");
// expect(...);
// Throws if not correct, so no expect is needed
checkType(response, functionName);
});
test("Check error", async () => {
const response = await request(app).get(url("test/error"));
expect(response.status).toBe(400);
expect(response.body).toMatchObject(error("This endpoint fails", "Reason: \"error\""));
checkType(response, functionName);
});
test(("All possible responses tested") => {
// Throws if not all checked, so no expect is needed
allChecked(functionName);
});
});