@apparts/backend-test
v4.0.1
Published
A framework for database-incorborating backend-testing
Downloads
66
Readme
#+TITLE: @apparts/backend-test #+DATE: [2021-02-11 Thu] #+AUTHOR: Philipp Uhl
This library supports testing @apparts-based backends that use Postgresql databases provide express based APIs.
- Usage
Add to your =package.json= in the =scripts= section: #+BEGIN_SRC js "testOne": "jest", "test": "jest --watch --detectOpenHandles", "testCoverage": "jest --coverage" #+END_SRC
Create a =jest.config.js= file in the root directory of your project. #+BEGIN_SRC js const jestConfig = require("@apparts/backend-test").getJestConfig();
module.exports = { ...jestConfig, // additional config }; #+END_SRC
Create a =config/db-test-config.json= or =config/db-test-config.js= or export the values as an environment variable with the name =DB_TEST_CONFIG= with this content (as JSON, for the js file you'd need to export the object): #+BEGIN_EXAMPLE { "use": "postgresql", "postgresql": { "host": "localhost", "port": 5432, "user": "postgres", "pw": "", "db": "", "maxPoolSize": 1, "connectionTimeoutMillis": 10000, "idleTimeoutMillis": 10000, "bigIntAsNumber": true } } #+END_EXAMPLE
Create tests. To use this library, start the tests with this on top: #+BEGIN_SRC js const { app, url, error, getPool } = require("@apparts/backend-test")({ testName: "", // apiContainer: myEndpoint, ...require("./tests/config.js")
}); #+END_SRCCreate a =tests/config.js= for storing test information that is valid for more than one test: #+BEGIN_SRC js const fs = require("fs"); module.exports = { schemas: ["schema-file-name-0001.sql" /, .../] .map(name => fs.readFileSync(name).toString()), apiVersion: 1, }; #+END_SRC
Run tests with #+BEGIN_SRC sh npm run test
for coverage report:
npm run testCoverage
without watching file changes:
npm run testOne #+END_SRC
** Parameters
=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 [[https://github.com/phuhl/apparts-types][@apparts/types]] =checkApiTypes= to test against. If this parameter is not supplied, =checkType= and =allChecked= 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.
** Returns
=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 [[https://github.com/phuhl/apparts-types][@apparts/types]] package. The first parameter of =checkType= is already set (with value from =apiContainer= parameter) for convenience.
- =allChecked : (string) => boolean= :: A wrapper around the ==allChecked= function from the [[https://github.com/phuhl/apparts-types][@apparts/types]] package. The first parameter of =allChecked= is already set (with value from =apiContainer= parameter) for convenience.
- =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//= to the string. =apiVersion= is the value of the parameter =apiVersion=. 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 [[https://github.com/phuhl/apparts-error][@apparts/error]]. Can be used with the supertest =toMatchObject= matcher.
- =runDBQuery : async (async () => any) => any= :: A function that expects a function as parameter. This function receives a [[https://github.com/phuhl/apparts-db][@apparts/db]] =DBS= object as parameter. It's return value will be awaited and returned by =runDBQuery=.
- =getPool : () => = :: Returns a [[https://github.com/phuhl/apparts-db][@apparts/db]] =DBS= object.
** Minimal example
=jest.config.js=: #+BEGIN_SRC js const jestConfig = require("@apparts/backend-test").getJestConfig();
module.exports = { ...jestConfig, // additional config }; #+END_SRC
=config/db-test-config.json= as described above
Tests with #+BEGIN_SRC js const { app, url } = require("@apparts/backend-test")({ 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); }); #+END_SRC
** Full-ish example
#+BEGIN_SRC js const { app, url, checkType, allChecked, error, getPool, } = require("@apparts/backend-test")({ 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);
});
}); #+END_SRC