Challenge #7: Introducing Assertions
Now that the complete exchange is finished, your test code serves as a full demonstration of how to make an HTTP request and respond to it with a server. Now it’s time to start converting this proof of concept into real test and production code. You’ll start by converting your client log messages into an assertion.
Instructions
1. Introduce the client assertion:
- Return the following object from the client’s response handler:
{ status, // the response status code headers, // the response headers body, // the response body }
- Add the following assertion after the exchange is complete:
delete response.headers.date; // deleted because it's hard to assert against, and not particularly important assert.deepEqual(response, { status: 999, headers: { myresponseheader: "myResponseValue", connection: "close", "content-length": "16", }, body: "my response body", });
- Erase the client response logging.
2. The test should pass. Your test output should be:
SERVER LISTENING
SERVER STARTED
CLIENT SENDING REQUEST
SERVER RECEIVING REQUEST
SERVER RECEIVED ENTIRE REQUEST
SERVER RECEIVED METHOD: POST
SERVER RECEIVED PATH: /my/path
SERVER RECEIVED HEADERS: {
myrequestheader: 'myRequestValue',
host: 'localhost:5001',
connection: 'close',
'content-length': '15'
}
SERVER RECEIVED BODY: my request body
SERVER SENT RESPONSE
EXCHANGE COMPLETE
SERVER CLOSED
SERVER STOPPED
Remember to commit your changes when you’re done.
API Documentation
assert.deepEqual(actual, expected);
Assert that two objects (or arrays) and all their recursive contents are equal.
- actual
(any)
- the actual value - expected
(any)
- the expected value
TypeScript Types
clientResponse.headers: HttpHeaders
The headers received from the server.
type HttpHeaders = Record<string, HttpHeader>;
type HttpHeader = string;
JavaScript Primers
Hints
1
The response data you want to return needs to be in variables.
Specifically, the status, headers, and body all need variables.
body
is already in a variable. Create variables for status
and headers
. Your editor might have an “Introduce Variable” refactoring that does this for you automatically.
it("performs request", async () => {
const server = http.createServer();
await new Promise((resolve, reject) => { // add <void> for TypeScript
server.listen(PORT);
server.on("listening", () => {
console.log("SERVER LISTENING");
return resolve();
});
});
console.log("SERVER STARTED");
server.on("request", (serverRequest, serverResponse) => {
console.log("SERVER RECEIVING REQUEST");
let body = "";
serverRequest.on("data", (chunk) => {
body += chunk;
});
serverRequest.on("end", () => {
console.log("SERVER RECEIVED ENTIRE REQUEST");
console.log("SERVER RECEIVED METHOD:", serverRequest.method);
console.log("SERVER RECEIVED PATH:", serverRequest.url);
console.log("SERVER RECEIVED HEADERS:", serverRequest.headers);
console.log("SERVER RECEIVED BODY:", body);
serverResponse.statusCode = 999;
serverResponse.setHeader("myResponseHeader", "myResponseValue");
serverResponse.end("my response body");
console.log("SERVER SENT RESPONSE");
});
});
console.log("CLIENT SENDING REQUEST");
const clientRequest = http.request({
host: HOST,
port: PORT,
method: "POST",
path: "/my/path",
headers: {
myRequestHeader: "myRequestValue",
},
});
clientRequest.end("my request body");
await new Promise((resolve, reject) => { // add <void> for TypeScript
clientRequest.on("response", (clientResponse) => {
console.log("CLIENT RECEIVING RESPONSE");
let body = "";
clientResponse.on("data", (chunk) => {
body += chunk;
});
clientResponse.on("end", () => {
console.log("CLIENT RECEIVED ENTIRE RESPONSE");
const status = clientResponse.statusCode;
const headers = clientResponse.headers;
console.log("CLIENT RECEIVED STATUS:", status);
console.log("CLIENT RECEIVED HEADERS:", headers);
console.log("CLIENT RECEIVED BODY:", body);
resolve();
});
});
});
console.log("EXCHANGE COMPLETE");
await new Promise((resolve, reject) => { // add <void> for TypeScript
server.close();
server.on("close", () => {
console.log("SERVER CLOSED");
return resolve();
});
});
console.log("SERVER STOPPED");
});
2
Your response handler needs to return the response variables.
You can’t use the return
keyword inside a promise.
Use the resolve()
function instead.
You can use an object to return multiple values. (Object shorthand will come in handy.)
it("performs request", async () => {
const server = http.createServer();
await new Promise((resolve, reject) => { // add <void> for TypeScript
server.listen(PORT);
server.on("listening", () => {
console.log("SERVER LISTENING");
return resolve();
});
});
console.log("SERVER STARTED");
server.on("request", (serverRequest, serverResponse) => {
console.log("SERVER RECEIVING REQUEST");
let body = "";
serverRequest.on("data", (chunk) => {
body += chunk;
});
serverRequest.on("end", () => {
console.log("SERVER RECEIVED ENTIRE REQUEST");
console.log("SERVER RECEIVED METHOD:", serverRequest.method);
console.log("SERVER RECEIVED PATH:", serverRequest.url);
console.log("SERVER RECEIVED HEADERS:", serverRequest.headers);
console.log("SERVER RECEIVED BODY:", body);
serverResponse.statusCode = 999;
serverResponse.setHeader("myResponseHeader", "myResponseValue");
serverResponse.end("my response body");
console.log("SERVER SENT RESPONSE");
});
});
console.log("CLIENT SENDING REQUEST");
const clientRequest = http.request({
host: HOST,
port: PORT,
method: "POST",
path: "/my/path",
headers: {
myRequestHeader: "myRequestValue",
},
});
clientRequest.end("my request body");
const response = await new Promise((resolve, reject) => {
clientRequest.on("response", (clientResponse) => {
console.log("CLIENT RECEIVING RESPONSE");
let body = "";
clientResponse.on("data", (chunk) => {
body += chunk;
});
clientResponse.on("end", () => {
console.log("CLIENT RECEIVED ENTIRE RESPONSE");
const status = clientResponse.statusCode;
const headers = clientResponse.headers;
console.log("CLIENT RECEIVED STATUS:", status);
console.log("CLIENT RECEIVED HEADERS:", headers);
console.log("CLIENT RECEIVED BODY:", body);
resolve({
status,
headers,
body,
});
});
});
});
console.log("EXCHANGE COMPLETE");
await new Promise((resolve, reject) => { // add <void> for TypeScript
server.close();
server.on("close", () => {
console.log("SERVER CLOSED");
return resolve();
});
});
console.log("SERVER STOPPED");
});
TypeScript needs some type surgery:
it("performs request", async () => {
const server = http.createServer();
await new Promise<void>((resolve, reject) => {
server.listen(PORT);
server.on("listening", () => {
console.log("SERVER LISTENING");
return resolve();
});
});
console.log("SERVER STARTED");
server.on("request", (serverRequest, serverResponse) => {
console.log("SERVER RECEIVING REQUEST");
let body = "";
serverRequest.on("data", (chunk) => {
body += chunk;
});
serverRequest.on("end", () => {
console.log("SERVER RECEIVED ENTIRE REQUEST");
console.log("SERVER RECEIVED METHOD:", serverRequest.method);
console.log("SERVER RECEIVED PATH:", serverRequest.url);
console.log("SERVER RECEIVED HEADERS:", serverRequest.headers);
console.log("SERVER RECEIVED BODY:", body);
serverResponse.statusCode = 999;
serverResponse.setHeader("myResponseHeader", "myResponseValue");
serverResponse.end("my response body");
console.log("SERVER SENT RESPONSE");
});
});
console.log("CLIENT SENDING REQUEST");
const clientRequest = http.request({
host: HOST,
port: PORT,
method: "POST",
path: "/my/path",
headers: {
myRequestHeader: "myRequestValue",
},
});
clientRequest.end("my request body");
const response = await new Promise<HttpClientResponse>((resolve, reject) => {
clientRequest.on("response", (clientResponse) => {
console.log("CLIENT RECEIVING RESPONSE");
let body = "";
clientResponse.on("data", (chunk) => {
body += chunk;
});
clientResponse.on("end", () => {
console.log("CLIENT RECEIVED ENTIRE RESPONSE");
const status = clientResponse.statusCode as number;
const headers = clientResponse.headers as HttpHeaders;
console.log("CLIENT RECEIVED STATUS:", status);
console.log("CLIENT RECEIVED HEADERS:", headers);
console.log("CLIENT RECEIVED BODY:", body);
resolve({
status,
headers,
body,
});
});
});
});
console.log("EXCHANGE COMPLETE");
delete response.headers.date;
assert.deepEqual(response, {
status: 999,
headers: {
myresponseheader: "myResponseValue",
connection: "close",
"content-length": "16",
},
body: "my response body",
});
await new Promise<void>((resolve, reject) => {
server.close();
server.on("close", () => {
console.log("SERVER CLOSED");
return resolve();
});
});
console.log("SERVER STOPPED");
});
3
Now you can introduce the assertion.
You can use assert.deepEqual()
for that.
You’ll need to delete the date header because it changes frequently.
You can use the delete
keyword for that.
it("performs request", async () => {
const server = http.createServer();
await new Promise((resolve, reject) => { // add <void> for TypeScript
server.listen(PORT);
server.on("listening", () => {
console.log("SERVER LISTENING");
return resolve();
});
});
console.log("SERVER STARTED");
server.on("request", (serverRequest, serverResponse) => {
console.log("SERVER RECEIVING REQUEST");
let body = "";
serverRequest.on("data", (chunk) => {
body += chunk;
});
serverRequest.on("end", () => {
console.log("SERVER RECEIVED ENTIRE REQUEST");
console.log("SERVER RECEIVED METHOD:", serverRequest.method);
console.log("SERVER RECEIVED PATH:", serverRequest.url);
console.log("SERVER RECEIVED HEADERS:", serverRequest.headers);
console.log("SERVER RECEIVED BODY:", body);
serverResponse.statusCode = 999;
serverResponse.setHeader("myResponseHeader", "myResponseValue");
serverResponse.end("my response body");
console.log("SERVER SENT RESPONSE");
});
});
console.log("CLIENT SENDING REQUEST");
const clientRequest = http.request({
host: HOST,
port: PORT,
method: "POST",
path: "/my/path",
headers: {
myRequestHeader: "myRequestValue",
},
});
clientRequest.end("my request body");
const response = await new Promise((resolve, reject) => { // add <HttpClientResponse> for TypeScript
clientRequest.on("response", (clientResponse) => {
console.log("CLIENT RECEIVING RESPONSE");
let body = "";
clientResponse.on("data", (chunk) => {
body += chunk;
});
clientResponse.on("end", () => {
console.log("CLIENT RECEIVED ENTIRE RESPONSE");
const status = clientResponse.statusCode; // add "as number" for TypeScript
const headers = clientResponse.headers; // add "as HttpHeaders" for TypeScript
console.log("CLIENT RECEIVED STATUS:", status);
console.log("CLIENT RECEIVED HEADERS:", headers);
console.log("CLIENT RECEIVED BODY:", body);
resolve({
status,
headers,
body,
});
});
});
});
console.log("EXCHANGE COMPLETE");
delete response.headers.date;
assert.deepEqual(response, {
status: 999,
headers: {
myresponseheader: "myResponseValue",
connection: "close",
"content-length": "16",
},
body: "my response body",
});
await new Promise((resolve, reject) => { // add <void> for TypeScript
server.close();
server.on("close", () => {
console.log("SERVER CLOSED");
return resolve();
});
});
console.log("SERVER STOPPED");
});
4
The test should pass. You’re ready to clean up.
Delete the client logs and inline the variables.
it("performs request", async () => {
const server = http.createServer();
await new Promise((resolve, reject) => { // add <void> for TypeScript
server.listen(PORT);
server.on("listening", () => {
console.log("SERVER LISTENING");
return resolve();
});
});
console.log("SERVER STARTED");
server.on("request", (serverRequest, serverResponse) => {
console.log("SERVER RECEIVING REQUEST");
let body = "";
serverRequest.on("data", (chunk) => {
body += chunk;
});
serverRequest.on("end", () => {
console.log("SERVER RECEIVED ENTIRE REQUEST");
console.log("SERVER RECEIVED METHOD:", serverRequest.method);
console.log("SERVER RECEIVED PATH:", serverRequest.url);
console.log("SERVER RECEIVED HEADERS:", serverRequest.headers);
console.log("SERVER RECEIVED BODY:", body);
serverResponse.statusCode = 999;
serverResponse.setHeader("myResponseHeader", "myResponseValue");
serverResponse.end("my response body");
console.log("SERVER SENT RESPONSE");
});
});
console.log("CLIENT SENDING REQUEST");
const clientRequest = http.request({
host: HOST,
port: PORT,
method: "POST",
path: "/my/path",
headers: {
myRequestHeader: "myRequestValue",
},
});
clientRequest.end("my request body");
const response = await new Promise((resolve, reject) => { // add <HttpClientResponse> for TypeScript
clientRequest.on("response", (clientResponse) => {
let body = "";
clientResponse.on("data", (chunk) => {
body += chunk;
});
clientResponse.on("end", () => {
resolve({
status: clientResponse.statusCode, // add 'as number' for TypeScript
headers: clientResponse.headers, // add 'as HttpHeaders' for TypeScript
body,
});
});
});
});
console.log("EXCHANGE COMPLETE");
delete response.headers.date;
assert.deepEqual(response, {
status: 999,
headers: {
myresponseheader: "myResponseValue",
connection: "close",
"content-length": "16",
},
body: "my response body",
});
await new Promise((resolve, reject) => { // add <void> for TypeScript
server.close();
server.on("close", () => {
console.log("SERVER CLOSED");
return resolve();
});
});
console.log("SERVER STOPPED");
});
Complete Solution
Test code (JavaScript):
it("performs request", async () => {
const server = http.createServer();
await new Promise((resolve, reject) => {
server.listen(PORT);
server.on("listening", () => {
console.log("SERVER LISTENING");
return resolve();
});
});
console.log("SERVER STARTED");
server.on("request", (serverRequest, serverResponse) => {
console.log("SERVER RECEIVING REQUEST");
let body = "";
serverRequest.on("data", (chunk) => {
body += chunk;
});
serverRequest.on("end", () => {
console.log("SERVER RECEIVED ENTIRE REQUEST");
console.log("SERVER RECEIVED METHOD:", serverRequest.method);
console.log("SERVER RECEIVED PATH:", serverRequest.url);
console.log("SERVER RECEIVED HEADERS:", serverRequest.headers);
console.log("SERVER RECEIVED BODY:", body);
serverResponse.statusCode = 999;
serverResponse.setHeader("myResponseHeader", "myResponseValue");
serverResponse.end("my response body");
console.log("SERVER SENT RESPONSE");
});
});
console.log("CLIENT SENDING REQUEST");
const clientRequest = http.request({
host: HOST,
port: PORT,
method: "POST",
path: "/my/path",
headers: {
myRequestHeader: "myRequestValue",
},
});
clientRequest.end("my request body");
const response = await new Promise((resolve, reject) => {
clientRequest.on("response", (clientResponse) => {
let body = "";
clientResponse.on("data", (chunk) => {
body += chunk;
});
clientResponse.on("end", () => {
resolve({
status: clientResponse.statusCode,
headers: clientResponse.headers,
body,
});
});
});
});
console.log("EXCHANGE COMPLETE");
delete response.headers.date;
assert.deepEqual(response, {
status: 999,
headers: {
myresponseheader: "myResponseValue",
connection: "close",
"content-length": "16",
},
body: "my response body",
});
await new Promise((resolve, reject) => {
server.close();
server.on("close", () => {
console.log("SERVER CLOSED");
return resolve();
});
});
console.log("SERVER STOPPED");
});
Test code (TypeScript):
it("performs request", async () => {
const server = http.createServer();
await new Promise<void>((resolve, reject) => {
server.listen(PORT);
server.on("listening", () => {
console.log("SERVER LISTENING");
return resolve();
});
});
console.log("SERVER STARTED");
server.on("request", (serverRequest, serverResponse) => {
console.log("SERVER RECEIVING REQUEST");
let body = "";
serverRequest.on("data", (chunk) => {
body += chunk;
});
serverRequest.on("end", () => {
console.log("SERVER RECEIVED ENTIRE REQUEST");
console.log("SERVER RECEIVED METHOD:", serverRequest.method);
console.log("SERVER RECEIVED PATH:", serverRequest.url);
console.log("SERVER RECEIVED HEADERS:", serverRequest.headers);
console.log("SERVER RECEIVED BODY:", body);
serverResponse.statusCode = 999;
serverResponse.setHeader("myResponseHeader", "myResponseValue");
serverResponse.end("my response body");
console.log("SERVER SENT RESPONSE");
});
});
console.log("CLIENT SENDING REQUEST");
const clientRequest = http.request({
host: HOST,
port: PORT,
method: "POST",
path: "/my/path",
headers: {
myRequestHeader: "myRequestValue",
},
});
clientRequest.end("my request body");
const response = await new Promise<HttpClientResponse>((resolve, reject) => {
clientRequest.on("response", (clientResponse) => {
let body = "";
clientResponse.on("data", (chunk) => {
body += chunk;
});
clientResponse.on("end", () => {
resolve({
status: clientResponse.statusCode as number,
headers: clientResponse.headers as HttpHeaders,
body,
});
});
});
});
console.log("EXCHANGE COMPLETE");
delete response.headers.date;
assert.deepEqual(response, {
status: 999,
headers: {
myresponseheader: "myResponseValue",
connection: "close",
"content-length": "16",
},
body: "my response body",
});
await new Promise<void>((resolve, reject) => {
server.close();
server.on("close", () => {
console.log("SERVER CLOSED");
return resolve();
});
});
console.log("SERVER STOPPED");
});
No production code yet.