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:

  1. Return the following object from the client’s response handler:
    {
    	status,   // the response status code
    	headers,  // the response headers
    	body,     // the response body
    }
  2. 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",
    });
  3. 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.

Next challenge

Return to module overview