🧮 Ordinal numbers
Learning Objectives
🏢 Let’s imagine you’re working in a 10 storey office building. There are 10 different levels. We need a way to describe each level of the building. We start on the ground floor of the building - level with the ground. We use an ordinal number to describe the other levels in the building.
To form the ordinal number we take a number and add the correct
☝🏿 Up from the ground floor, we are then on the 1st floor (first floor)
☝🏽 Up from the 1st floor, we are on the 2nd floor (second floor)
number | + suffix | = ordinal number |
---|---|---|
1 | st | 1st |
2 | nd | 2nd |
What will the ordinal number be for:
a) 21?
b) 40?
c) 49?
d) 13?
Use ordinal numbers to write the days of the month for the following events:
a) Tomorrow
b) A week from now
c) Easter Sunday 2024
d) When is Eid expected to occur in 2024
- 1st
- 2nd
- 3rd
- 4th
- 5th
- 6th
- 7th
- 8th
- 9th
- 10th
📋 Specification
Let’s consider a function called getOrdinalNumber
that needs to work like this:
- it takes one argument - a whole number, like 1, 2, 3, etc
- it returns a string that represents the ordinal number
Interactive code block
getOrdinalNumber(1); // returns "1st";
getOrdinalNumber(2); // returns "2nd";
getOrdinalNumber(6); // returns "6th";
The requirements above form a
getOrdinalNumber
to behave when it is called with different inputs.
🖼️ Testing frameworks
Learning Objectives
To help us think about the requirements of getOrdinalNumber
, let’s consider one case:
💼 Case 1
Interactive code block
const input = 1;
const currentOutput = getOrdinalNumber(input);
const targetOutput = "1st";
Case 1 states that when getOrdinalNumber
is called with an input of 1, it has a target output of “1st”. Our first step is to check that getOrdinalNumber
works as we have stated.
We have used console.assert
to write assertions to check our code before, but console.assert
is limited. Now we will write tests to check our code is behaving in a particular way.
🔑 A test is any piece of code that runs an assertion on the code we’re testing
We want our tests to:
- be easy to write
- be easy to read
- to give clear feedback on what the current output is
- to give clear feedback on what the target output is
- easily write multiple test cases
To help us build test cases like this, we will use a
🧑🏽🧑🏿 Dialogue
We can use a short dialogue to think about why we want to use a testing framework:
- 🧑🏽 Büşra
- Ali, looks like I need to implement a function.
- 🧑🏿 Ali
- Cool. How are you going to check it works?
- 🧑🏽 Büşra
- I’m going to use tests to check that the function gives the target output as described in the specification
- 🧑🏿 Ali
- Seems wise. How are you going to write a lot of tests efficiently?
- 🧑🏽 Büşra
- I’m going to use a testing framework to write test cases quickly. The framework will make sure that the tests give fast, reliable feedback.
🎒 Starting a project
Learning Objectives
Let’s start a brand new project in a directory called week-4-example
and create a file called package.json
in our project.
- Open your terminal and ensure you’re inside the
cyf
directory you created earlier in the course. - Make a new directory on your local machine called
week-4-example
. - Change directory into
week-4-example
and double-check your current working directory.
pwd .../cyf/week-4-example
Package
package.json
stores useful information about our project, like the name, description, and version. It is written in the JSON format.- Create a
package.json
inweek-4-example
. - Make sure it contains a name and description.
👉🏽 Need help? Follow step by step instructions
- Create a
package.json
file in your project directory:
touch package.json
- Add a name to it like this:
{
"name": "week-4-test-example"
}
- Add a description:
{
"name": "week-4-test-example",
"description": "An example application showing how to write tests using the jest framework"
}
We can continue adding more information about our project as the project grows. For now, double-check we only have a package.json
in our project:
ls package.json
📦 Using packages
Learning Objectives
When writing software, we continually make use of software written by other developers. We can call these
We use packages so that we don’t have to solve every problem ourselves. Other people have often solved some things we need to do really well. Using other people’s solutions to parts of a problem means we can focus our time and effort on what’s special about our problem.
Imagine we wanted to work out what the time is in a user’s city. Instead of writing code to work out the time for every city’s time zone (and when they change!), we can use a package some “city time” experts have written, and which they keep up to date.
Different programming languages give developers different ways of accessing packages for use in their code. We will use
npm
, downloads and manages useful packages of code from the npm registry.
🃏 Installing Jest
Learning Objectives
Jest is a package used to help us to write and run test cases in JavaScript.
Our next step will be to figure out how to install the Jest package on our machine, so that we can use it in our project.
We can find out more about the Jest framework from the documentation online.
In the Getting started section of the documentation, Jest gives us the following command:
npm install jest --save-dev
Let’s break down the different parts of this command.
npm
- in the terminal, we can use thenpm
command to install packages from the npm registryinstall
- download a package on to our machine.jest
- this is the name of the package we want to install on our machine--save-dev
- this means the package is needed for development but not needed in production
So overall we can think of this command as saying:
“Please go to the npm database, find the Jest package and install it on my local machine”
Let’s execute this command in the same directory as the package.json
.
To double check we’re in the correct directory, we can run pwd
:
$ pwd
.../cyf/week-4-example
pwd
is telling us we’re in the week-4-example
directory.
We need to double check the package.json
is also there too.
$ ls
package.json
Now we can execute the command
npm install --save-dev jest
Our project structure will now look as follows:
week-4-example
├── node_modules
├── package-lock.json
└── package.json
1 directory, 3 files
After running the command, we now have a directory called node_modules
in our project too.
The
node_modules
directory contains all the code from thedependencies 🧶 we installed in our project. You won’t need to look inside the🧶 dependencies A dependency is a package that your project depends upon. node_modules directory
- you just need to know it contains the code for Jest and any other dependencies we install in our project.
Running the npm
command also updated our package.json
file for us:
{
"name": "week-4-test-example",
"description": "An example application showing how to write tests using the jest framework",
"devDependencies": {
"jest": "^29.5.0"
}
}
We’ve now got some additional information inside the package.json
:
"devDependencies" : {
"jest": "^29.5.0"
}
🕹️ Follow along
Install Jest on your local machine. Double check you’ve got the correct files and folders written to your local machine.
🎛️ Application Programming Interface
Learning Objectives
With Jest installed, we need to figure out how to use the Jest framework to write tests.
This means we need to look at
API stands for
- Application
- Programming
- Interface.
We’ve encountered the word interface already.
But we can break down each word in this acronym to understand it altogether.
An application is a program or piece of software designed to serve some purpose.
Programming refers to the process of writing code or software.
An 🕹️interface is a shared boundary between two or more systems.
We’ve encountered several functions like console.log
, Math.round
already.console.log
and Math.round
are APIs.
console.log
is actually implemented in a different language (C++), but that doesn’t matter - its functionality is exposed to us when we write JavaScript, and we don’t need to care how it’s actually implemented or how it works.
Jest provides an API so we can write tests.
So we have to find out about the Jest API to start writing tests with Jest.
Start a thread in Slack to discuss with your class.
💼 First test case
Learning Objectives
🎯 Goal: Write a test for the case below, using Jest:
Case 1 💼
Interactive code block
const input = 1;
const currentOutput = getOrdinalNumber(input);
const targetOutput = "1st";
We can create a file called get-ordinal-number.test.js
and write our first test there.
We can use documentation to work out how to write our first test using Jest.
get-ordinal-number.test.js
Interactive code block
test("converts 1 to an ordinal number", function () {});
Let’s break down this syntax.
The test
function is part of the Jest API, a function we use to perform a particular task.
In particular, we’re using test
to create a test case.
Before, we could use Math.round
and console.log
because Math
and console
are provided for us by Node.
test
isn’t provided by Node, but when we ask Jest to run our tests, it will make sure the test
function exists and that our code can use it.
Let’s break down the arguments we’re passing to test
:
- 1st argument:
"converts 1 to an ordinal number"
, a string which describes the behaviour we’re testing for - 2nd argument:
function () {}
, we will write some assertions in thisfunction () {}
to check the behaviour
⚖️ Creating assertions
We need to write an assertion inside the body of function () {}
inside get-ordinal-number.test.js
get-ordinal-number.test.js
Interactive code block
test("converts 1 to an ordinal number", function () {});
Recall
In this example, we want to check that the following is true:
We expect getOrdinalNumber(1)
to be "1st"
An assertion in Jest looks like this:
Interactive code block
expect(getOrdinalNumber(1)).toBe("1st");
The function toBe
is used to check that the current output of getOrdinalNumber(1)
and the target output of "1st"
are equal to each other.
toBe
is just one example of a function called a matcher.
A matcher is a function we use to compare values in Jest.
So the whole test looks like this:
Interactive code block
test("converts 1 to an ordinal number", function () {
const input = 1;
const currentOutput = getOrdinalNumber(input);
const targetOutput = "1st";
expect(currentOutput).toBe(targetOutput);
});
👟 Running tests
We can try running the file get-ordinal-number.test.js
with node in the following way:
node get-ordinal-number.test.js
but we get an error:
ReferenceError: test is not defined
This is because test
isn’t defined anywhere in the file.
We need to execute this file so that the Jest API is available in our file. We can do this by running the test file using Jest: we do this using an npm script.
The “scripts” section of the package.json
is where we can write useful commands we’ll use in our project. We can add a “scripts” section to the package.json
so that it reads as follows:
|
|
Finally, we’ll need to run our tests.
Now we can run the command npm test
.
When we execute the command, npm test
, we will run npm
, and npm
will look inside the “scripts” section of the package.json and look up the command for “test” - in this case, “jest”. npm
will then run “jest”.
✅ ❌ Interpreting feedback
Learning Objectives
We currently have a project structure like this:
week-4-test-example
├── get-ordinal-number.test.js
├── package.json
├── package-lock.json
└── node_modules
1 directory, 3 files
And get-ordinal-number.test.js
looks like this
Interactive code block
test("converts 1 to an ordinal number", function () {
const input = 1;
const currentOutput = getOrdinalNumber(input);
const targetOutput = "1st";
expect(currentOutput).toBe(targetOutput);
});
After running the test above, we should get feedback indicating whether or not the test has passed.
Predict and explain
🚢 Defining the function
At the moment, our test feedback gives the following:
The test code is throwing a
This means that we haven’t defined a function named getOrdinalNumber
, but we’re trying to use it.
To fix this, we can declare getOrdinalNumber
.
Interactive code block
function getOrdinalNumber() {}
test("converts 1 to an ordinal number", function () {
expect(getOrdinalNumber(1)).toBe("1st");
});
Now we can run the tests again and check the test feedback.
Assertion errors
We now get the following feedback:
Jest tells us 3 main things:
- The test case that failed
- The target output and the current output
- The line number where error occurred
Jest defines Expected and Received in the test feedback:
- Expected: “1st”
- Received:
undefined
exercise
What are the values of Expected and Received in the test output?
How do Received and Expected match up with the target output and expected output ?
What line number did the test case fail on?
Passing getOrdinalNumber
We can now pass the test by implementing functionality for the first test case.
We could write the following:
get-ordinal-number.test.js
Interactive code block
function getOrdinalNumber() {
return "1st";
}
test("converts 1 to an ordinal number", function () {
const input = 1;
const currentOutput = getOrdinalNumber(input);
const targetOutput = "1st";
expect(currentOutput).toBe(targetOutput);
});
🗄️ Generalising further
Learning Objectives
In English, ordinal numbers mostly follow the same pattern.
Numbers ending in 1 will generally have an ordinal number ending in “st”.
Here are some examples of this pattern,
1st, 11th, 21st, 31st, 41st,…
All the numbers ending in 1 will continue to end in "st"
, with the exception of 11.
11 is slightly different and ends with a "th"
.
We can now update our test case to check that getOrdinalNumber
works for lots of different numbers ending in 1.
get-ordinal-number.test.js
Interactive code block
function getOrdinalNumber() {
return "1st";
}
test("works for any number ending in 1", function () {
expect(getOrdinalNumber(1)).toBe("1st");
expect(getOrdinalNumber(11)).toBe("11th");
expect(getOrdinalNumber(21)).toBe("21st");
});
We’ve also updated the test description because we’re adding more assertions and checking slightly different functionality.
🔧 Implement
getOrdinalNumber
so it passes the test case above.Prep - Asking Good Questions 🔗
Learning Objectives
Preparation
Introduction
Research asking good questions
🎯 Goal: Learn how to ask good questions (15 minutes)
Read here what Julia Evans wrote about how to ask good questions.
Which five of the following ideas do you think are the most important when asking good questions?
- Stating what you know
- Asking questions where the answer is a fact
- Being willing to say what you don’t understand
- Identifing terms you don’t understand
- Doing some research before asking
- Deciding who to ask
- Asking questions to show what’s not obvious
- Answering other people’s questions
- Preparing useful information to save the other person’s time
- Identifying what you do already know, to put that out of scope of the “question”.
- Other _________________________________________
Learn how to answer questions
🎯 Goal: Learn how to answer questions helpfully (15 minutes)
Read here what Julia Evans wrote about how to answer questions helpfully:
Which five of the following ideas do you think are the most important when answering good questions?
- If they’re not asking clearly, helping them clarify
- Figuring out what they know already
- Pointing them to the documentation
- Pointing them to a useful search
- Writing new documentation
- Explaining what you did
- Solving the underlying problem
- Asking “Did that answer your question?”
- Not acting surprised
- Letting them know what you level you expect them to be at.
- Asking what steps they have taken to solve the question themselves.
Coming up with questions
🎯 Goal: To think of technical questions that you will improve during the session (15 minutes)
Think of three examples of technical problems that you have recently had. These could either be from JavaScript, HTML, CSS or any other technical area. For example, they could be about error messages you didn’t expect or understand, or about code that didn’t work as you expected, or about webpages that didn’t get rendered as you anticipated. You’ll need these questions as you will work on them to structure them into good questions that are likely to get valuable responses on Saturday.Unblocking someone or helping them to learn?
🎯 Goal: Compare different ways of answering questions (15 minutes)
A fellow trainee is asking a question on the channel that they are stuck, sends an example and asks for help.
You check it and you can find the problem immediately. What do you do?
- Give them the answer
- Prompt them with questions so they can find the answer themselves?
Think about the differences and the benefits between these questions.
And now, considering we want to help each other to learn, which strategy will you use more moving forward?