Challenge #3: Configuring Responses
In the previous three challenges, you made the code call the ROT-13 server when it received a POST
request. In this challenge, you’ll make it return a web page containing the encoded text.
Instructions
1. Implement the "POST renders result of ROT-13 service call"
test:
- Configure the ROT-13 client to return
"my_response"
. - Assert that
HomePageController.postAsync()
returnshomePageView.homePage("my_response")
.
2. Revise HomePageController.postAsync()
:
- Render and return the home page.
- Don’t worry about edge cases—just program the happy path.
3. Verify that it worked:
- Run
./serve_dev.sh 5010 5011
from a command prompt, if it’s not already running. - Visit
http://localhost:5010
in a browser. - Enter
"hello"
into the text field and press the button. - If it worked, the contents of the text field will change to
"uryyb"
.
Remember to commit your changes when you’re done.
API Documentation
const rot13Client = Rot13Client.createNull([{ response }]);
Create a Nulled Rot13Client
that responds with the specified value the first time it’s called. Note that the parameter is an array of objects. To specify additional responses, add more { response }
objects to the array.
- response
(string)
- the response to return - returns rot13Client
(Rot13Client)
- the ROT-13 client
const response = homePageView.homePage(text)
Render the home page with the specified string in the text field.
- text
(string)
- the string to put in the text field - returns response
(HttpResponse)
- the home page
JavaScript Primers
No new concepts.
Hints
1
Just like in the previous challenges, you’ll need to create a Rot13Client
in your test. But this time, you’ll need to control what data it returns.
You can do that by providing an array of objects to Rot13Client.createNull()
.
it("POST renders result of ROT-13 service call", async () => {
// Arrange
const rot13Client = Rot13Client.createNull([{
response: "my_response",
}]);
// Act
// Assert
});
2
You’ll need a HomePageController
and a Clock
, too, but you don’t need to track requests.
This is the same as previous challenges.
it("POST renders result of ROT-13 service call", async () => {
// Arrange
const rot13Client = Rot13Client.createNull([{
response: "my_response",
}]);
const clock = Clock.createNull();
const controller = new HomePageController(rot13Client, clock);
// Act
// Assert
});
3
You’ll need an HttpServerRequest
with a valid request body, or your parsing logic will fail. Use a body that makes it clear it’s not part of the test.
You can specify it the same way as in the last challenge.
it("POST renders result of ROT-13 service call", async () => {
// Arrange
const rot13Client = Rot13Client.createNull([{
response: "my_response",
}]);
const clock = Clock.createNull();
const controller = new HomePageController(rot13Client, clock);
const request = HttpServerRequest.createNull({
body: "text=irrelevant_text",
});
// Act
// Assert
});
4
You’ll need a WwwConfig
, but you don’t need to configure a specific port.
This is just like the first challenge.
it("POST renders result of ROT-13 service call", async () => {
// Arrange
const rot13Client = Rot13Client.createNull([{
response: "my_response",
}]);
const clock = Clock.createNull();
const controller = new HomePageController(rot13Client, clock);
const request = HttpServerRequest.createNull({
body: "text=irrelevant_text",
});
const config = WwwConfig.createTestInstance();
// Act
// Assert
});
5
You’re ready to simulate the POST
request.
This uses controller.postAsync()
, just like the previous challenge, except that you need to keep the response.
it("POST renders result of ROT-13 service call", async () => {
// Arrange
const rot13Client = Rot13Client.createNull([{
response: "my_response",
}]);
const clock = Clock.createNull();
const controller = new HomePageController(rot13Client, clock);
const request = HttpServerRequest.createNull({
body: "text=irrelevant_text",
});
const config = WwwConfig.createTestInstance();
// Act
const response = await controller.postAsync(request, config);
// Assert
});
6
Before you write your assertion, you’ll need an expected value.
You’re expecting the home page with "my_response"
in the text field.
You can use homePageView.homePage()
for that.
it("POST renders result of ROT-13 service call", async () => {
// Arrange
const rot13Client = Rot13Client.createNull([{
response: "my_response",
}]);
const clock = Clock.createNull();
const controller = new HomePageController(rot13Client, clock);
const request = HttpServerRequest.createNull({
body: "text=irrelevant_text",
});
const config = WwwConfig.createTestInstance();
// Act
const response = await controller.postAsync(request, config);
// Assert
const expected = homePageView.homePage("my_response");
});
7
Now you can assert that the response matches the assertion.
This is the same as the first challenge.
it("POST renders result of ROT-13 service call", async () => {
// Arrange
const rot13Client = Rot13Client.createNull([{
response: "my_response",
}]);
const clock = Clock.createNull();
const controller = new HomePageController(rot13Client, clock);
const request = HttpServerRequest.createNull({
body: "text=irrelevant_text",
});
const config = WwwConfig.createTestInstance();
// Act
const response = await controller.postAsync(request, config);
// Assert
const expected = homePageView.homePage("my_response");
assert.deepEqual(response, expected);
});
8
You’re ready to run the test.
It should fail, saying that the response was undefined
.
This means your production code isn’t returning a response.
9
Your production code needs to return a response.
You can do that by calling homePageView.homePage(output)
.
The output comes from the ROT-13 service.
Specifically, the call to rot13Client.transformAsync()
.
async postAsync(request, config) {
const form = await request.readBodyAsUrlEncodedFormAsync();
const userInput = form[INPUT_FIELD_NAME][0];
const output = await this._rot13Client.transformAsync(
config.rot13ServicePort,
userInput,
config.correlationId,
);
return homePageView.homePage(output);
}
TypeScript no longer needs a placeholder return, but userInput
still needs a temporary workaround:
// TypeScript
async postAsync(request: HttpServerRequest, config: WwwConfig): Promise<HttpServerResponse> {
const form = await request.readBodyAsUrlEncodedFormAsync();
const userInput = form[INPUT_FIELD_NAME]?.[0] ?? "not implemented";
const output = await this._rot13Client.transformAsync(
config.rot13ServicePort,
userInput,
config.correlationId,
);
return homePageView.homePage(output);
}
Complete Solution
Test code:
it("POST renders result of ROT-13 service call", async () => {
// Arrange
const rot13Client = Rot13Client.createNull([{
response: "my_response",
}]);
const clock = Clock.createNull();
const controller = new HomePageController(rot13Client, clock);
const request = HttpServerRequest.createNull({
body: "text=irrelevant_text",
});
const config = WwwConfig.createTestInstance();
// Act
const response = await controller.postAsync(request, config);
// Assert
const expected = homePageView.homePage("my_response");
assert.deepEqual(response, expected);
});
Production code (JavaScript):
async postAsync(request, config) {
const form = await request.readBodyAsUrlEncodedFormAsync();
const userInput = form[INPUT_FIELD_NAME][0];
const output = await this._rot13Client.transformAsync(
config.rot13ServicePort,
userInput,
config.correlationId,
);
return homePageView.homePage(output);
}
Production code (TypeScript):
async postAsync(request: HttpServerRequest, config: WwwConfig): Promise<HttpServerResponse> {
const form = await request.readBodyAsUrlEncodedFormAsync();
const userInput = form[INPUT_FIELD_NAME]?.[0] ?? "not implemented";
const output = await this._rot13Client.transformAsync(
config.rot13ServicePort,
userInput,
config.correlationId,
);
return homePageView.homePage(output);
}