4.0.1 • Published 9 months ago

@apparts/backend-test v4.0.1

Weekly downloads
9
License
UNLICENSED
Repository
github
Last release
9 months ago

#+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
  1. Add to your =package.json= in the =scripts= section: #+BEGIN_SRC js "testOne": "jest", "test": "jest --watch --detectOpenHandles", "testCoverage": "jest --coverage" #+END_SRC
  2. 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

  3. 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

  4. 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_SRC
  5. Create 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
  6. 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] =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] 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] 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]. 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] =DBS= object as parameter. It's return value will be awaited and returned by =runDBQuery=.
  • =getPool : () => = :: Returns a [https://github.com/phuhl/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

4.0.1

9 months ago

4.0.0

10 months ago

3.0.0

2 years ago

2.0.3

2 years ago

2.0.2

2 years ago

2.0.4

2 years ago

2.0.1

2 years ago

2.0.0

2 years ago

1.2.0

2 years ago

1.1.0

3 years ago

1.0.3

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago