Skip to content

Commit 1bd093c

Browse files
committed
Examples with fastify added and working. Fixed the nyc issue (all: true does not work..)
1 parent 4fd4419 commit 1bd093c

15 files changed

+129
-79
lines changed

.mocharc.yml

+6
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ require:
55
recursive: true
66
color: true
77
exit: true
8+
extension:
9+
- ts
10+
- test.ts
11+
ignore:
12+
# this is so that mocha doesn'T auto execute app.ts before the tests does load it.
13+
- "**/src/app.ts"

.nycrc.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# according to https://github.com/istanbuljs/istanbuljs/tree/master/packages/nyc-config-typescript
22
extends: "@istanbuljs/nyc-config-typescript"
3-
all: true
43

54
reporter:
65
- html
@@ -13,7 +12,7 @@ report-dir: coverage
1312

1413
# Coverage gates
1514
check-coverage: true
16-
functions: 90
15+
functions: 80
1716
lines: 80
1817
statements: 80
1918
branches: 70

.vscode/launch.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
2323
"args": [
2424
"--no-timeouts",
25-
"${workspaceFolder}/src/**/*.test.ts",
26-
"${workspaceFolder}/test/**/*.ts"
25+
"${workspaceFolder}/src",
26+
"${workspaceFolder}/test"
2727
],
2828
"internalConsoleOptions": "openOnSessionStart"
2929
},

README.md

+1-4
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ Whilst working with typescript for a while now I found that most examples on the
3737
* Out of the box VsCode config for debugging app + tests no matter where with proper sourcemaps
3838
* Multi stage docker build
3939
* All type infos needed as well
40-
41-
I will probably extend to show off:
42-
43-
* Fastify with native async await in Typescript
40+
* Fastify with native async await in Typescript, including mocked / stubbed and spy tests as well as using inject to test the calls
4441
* Sinon for mocking / stubbing
4542

4643
The biggest challenge was trying to make code coverage work without ts-node which unfortunately wasn't really possible. It kindof worked but did not track any files that were not called in any test counteracting the purpose of finding uncovered code. So this template uses ts-node for all test execution and a transpiled build (no ts-node hence for "prod") to run the app.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"lint": "eslint src",
1010
"build": "npm run clean && npm run lint && tsc -p .",
1111
"start": "npm run build && node build/app.js",
12-
"test": "npm run lint && nyc mocha src/**/*.test.ts test/**/*.ts" ,
12+
"test": "npm run lint && nyc mocha src test",
1313
"docker": "docker build -t typescript-node-template ."
1414
},
1515
"repository": {

src/app.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ export async function stop(): Promise<void> {
1818
await app.server.close();
1919
}
2020

21-
// start();
21+
start();

src/handler/Persons.test.ts

-11
This file was deleted.

src/handler/hello.test.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
import { expect } from "chai";
3+
import { hello } from "./hello";
4+
import { fastify} from "fastify";
5+
6+
describe("Hello", () => {
7+
it("handler", async () => {
8+
const app = fastify();
9+
app.get("/", hello);
10+
const response = await app.inject({
11+
method: "GET",
12+
url: "/",
13+
});
14+
expect(response.statusCode).to.equal(200);
15+
expect(response.headers["content-type"]).to.equal("text/html; charset=utf-8");
16+
expect(response.body).to.contain("Hallo!");
17+
});
18+
});

src/handler/hello.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { FastifyRequest, FastifyReply } from "fastify";
2+
3+
export async function hello(req: FastifyRequest, reply: FastifyReply): Promise<string> {
4+
reply.type("text/html; charset=utf-8").code(200);
5+
return "<h1>Hallo!</h1><a href='/persons'>Fetch persons here</a>";
6+
}

src/handler/persons.test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
import { expect } from "chai";
3+
import { getAll } from "./persons";
4+
import { fastify} from "fastify";
5+
6+
describe("Persons", () => {
7+
it("handler", async () => {
8+
const app = fastify();
9+
app.get("/", getAll);
10+
const response = await app.inject({
11+
method: "GET",
12+
url: "/",
13+
});
14+
expect(response.statusCode).to.equal(200);
15+
expect(response.headers["content-type"]).to.equal("application/json; charset=utf-8");
16+
const persons = JSON.parse(response.body);
17+
expect(persons.length).to.equal(1);
18+
expect(persons[0].firstName).to.equal("Rick");
19+
expect(persons[0].lastName).to.equal("Sanchez");
20+
});
21+
});

src/handler/persons.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
// import { FastifyRequest, FastifyReply } from "fastify";
1+
import { FastifyRequest, FastifyReply } from "fastify";
22
import { Person } from "../types/Person";
33

4-
export async function getAll(/* req: FastifyRequest, reply: FastifyReply */): Promise<Person[]> {
5-
// reply.type("application/json").code(200);
4+
export async function getAll(req: FastifyRequest, reply: FastifyReply): Promise<Person[]> {
5+
reply.type("application/json; charset=utf-8").code(200);
66
return [new Person({
77
firstName: "Rick",
88
lastName: "Sanchez",

src/routes.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { FastifyInstance } from "fastify";
2-
import * as persons from "./handler/persons";
2+
import { getAll } from "./handler/persons";
3+
import { hello } from "./handler/hello";
34

45
export function register(app: FastifyInstance): void {
5-
app.get("/persons", persons.getAll);
6+
app.get("/", hello);
7+
app.get("/persons", getAll);
68
}

src/unused.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// intentionally uncovered to show nyc corectly finds unused files as well.
2+
export function unused(): void {
3+
console.log("This should be uncovered code");
4+
}

test/app.ts

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { expect, use } from "chai";
3+
import * as chaiAsPromised from "chai-as-promised";
4+
import * as sinon from "sinon";
5+
import * as routes from "../src/routes";
6+
import * as fastify from "fastify";
7+
8+
use(chaiAsPromised);
9+
describe("Integration", () => {
10+
11+
let sandbox: sinon.SinonSandbox;
12+
beforeEach(() => {
13+
sandbox = sinon.createSandbox();
14+
});
15+
16+
afterEach(() => {
17+
sandbox.restore();
18+
});
19+
20+
const fakeFastify: any = {
21+
listen: sinon.spy(),
22+
get: sinon.spy(),
23+
log: {
24+
error: sinon.spy(),
25+
},
26+
server: {
27+
close: sinon.spy(),
28+
},
29+
};
30+
31+
before(() => {
32+
const fastifyStub = sinon.stub(fastify, "fastify");
33+
fastifyStub.returns(fakeFastify);
34+
});
35+
36+
let app: any;
37+
it("Successful app startup", async () => {
38+
const mock = sandbox.mock(routes);
39+
40+
// conditional loading as we can only test this when module loads FIRST time as its auto executing!
41+
// This is just as app auto executes itself on start
42+
app = await import("../src/app");
43+
44+
mock.expects("register").calledOnce;
45+
expect(fakeFastify.listen.calledOnce, "listen called once").to.be.true;
46+
expect(fakeFastify.get.calledTwice, "get called twice").to.be.true;
47+
});
48+
49+
it("Failed app startup", async () => {
50+
const exitStub = sandbox.stub(process, "exit");
51+
fakeFastify.listen = () => { throw new Error(); };
52+
await app.start();
53+
expect(fakeFastify.log.error.calledOnce, "log.error called once").to.be.true;
54+
expect(exitStub.calledOnce, "process.exit called once").to.be.true;
55+
});
56+
57+
it("Close the server", async () => {
58+
await app.stop();
59+
expect(fakeFastify.server.close.calledOnce, "server close called once").to.be.true;
60+
});
61+
});

test/integration.ts

-53
This file was deleted.

0 commit comments

Comments
 (0)