Lesson Tuesday

As our projects get more complex, we need to break our code down into a series of smaller, more manageable programming tasks. It's absolutely essential we do this - otherwise, we'll be overwhelmed! When we are working on complex business logic, we can use a development method called Test-Driven Development, or TDD, to break our logic down into smaller problems that are easier to solve and reason about.

Test-Driven Development is exactly that. In TDD, we break our code down into the smaller pieces of functionality. Next, we write a test for a piece of functionality before we add that functionality to our code. Finally, we add the code to make that test pass.

These tests are also known as specs. For now, we are going to manually test all of our specs. Then, when we get to Test-Driven Development and Environments with JavaScript, we'll start using software that automates the testing process. We'll also dive more deeply into the testing process by learning about the Red Green Refactor workflow.

However, we aren't quite there yet. In the past, students have found automated testing overwhelming when we introduce it too early in the program. For that reason, we wait until Intermediate JavaScript to cover automated testing. However, we believe that the thought process behind Test-Driven Development is absolutely essential to breaking down tough problems into more manageable solutions. That's why we will start pseudocoding tests now. Instead of using automated tests, we'll test our code in the console until it passes.

Pseudocode is exactly what it sounds like - fake code. To be more specific, it's using plain English to write coding logic without writing it in an actual coding language such as JavaScript.

Here's an example of a test using Jest, a testing framework we'll learn about in Intermediate JavaScript.

describe('Triangle', () => {
  test('should correctly create a triangle object with three lengths', () => {
    const triangle = new Triangle(2,4,5);
    expect(triangle.side1).toEqual(2);
    expect(triangle.side2).toEqual(4);
    expect(triangle.side3).toEqual(5);
  });
});

It almost looks like plain English already. The test above just makes sure that a triangle has three sides. We'll use some of the coding terms in this test right now to create our pseudocoded specs.

<!-- This is pseudo-code! -->

Describe: functionName
Test: the thing that we are testing
Code: the code we need to run to complete the test
Expect: theCodeWeRun.toEqual(SomeValue)
  • Describe is a common term in testing. It's used to group tests together. For example, we might make a little application with three different functions, all of which need several tests. In the Jest example above, we are describing code that belongs to a Triangle object. All Triangle-related tests will go in this describe block. We'll get to objects in the next section - for now, we can just group our tests around functions. (We'll cover an example in a moment.)

  • Test breaks down what the test is doing in plain English. In the Jest example above, it states the test 'should correctly create a triangle object with three lengths'. That's actually not code for the machine. It's just a way for us to understand what the test is doing.

  • Code is where we'll put any code we need to check that our test is working. We'll cover this in our example.

  • Expect is what we expect our test result to be. This often means we are checking to see if a variable we've computed equals the value we expect.

Let's look at an example now. We want to write a function that adds two numbers together. It's the same function that we wrote for our calculator. Here's what our pseudocoded test might look like:

Describe: add()
Test: "It adds two numbers together and returns the sum"
Code: const sum = add(2,3);
Expect(sum).toEqual(5);

As we can see, we are describing the name of the function, which is add().

Next, we explain what the test will determine: that our function adds two numbers together and returns the sum of those numbers. This is always written in plain English - even when we work with Jest. Jest will ignore this line - it's there for us to read and understand what the test is supposed to do.

We will need to test our add() function with some code. We'll save the value of add(2,3) inside a constant called sum.

When we run our code (const sum = add(2,3)), we'll expect the value of sum to be equal to 5.

As we mentioned earlier in this lesson, we'll write each test before we add that functionality to our code. This way, we can determine what our logic should do and what the code might look like before we even write it. Then, when we are done writing the code, we can immediately test it to see if it works correctly.

By the way, we won't always need the code section. In fact, we could rewrite the test above without it:

Describe: add()
Test: "It adds two numbers together and returns the sum"
Expect(add(2,3)).toEqual(5);

In the example above, we don't save the value of add(2,3) in a variable - instead, we just pass the function directly into the expect section of the test. This is completely fine! Sometimes we'll need the code section, sometimes we won't.

When we get to Intermediate JavaScript, these tests will be automated, but for now, we'll test the results in the Chrome console, the Node REPL, or anywhere you'd like to verify that the function you are testing returns what you expect. To do that, we'll paste the function into the console and then run the expectation add(2,3) to make sure it equals 5. It might feel like slow going, but we should always be testing our code to make sure it is working correctly even if we have to do so manually. Manual testing will also allow us to solidify coding concepts and really take a look at what's going wrong (or right) with our code.

If writing tests is still a bit murky, don't worry. In the next lesson, we'll apply Test-Driven Development to a more complex problem.

Terminology


  • Test-Driven Development: A workflow used by developers across coding languages. In TDD, we write tests that describe our application's behavior. Then we write the minimal amount of code we need to get the test passing. The goal is to break larger problems into more manageable steps and also to make sure our code is working correctly.

  • Tests, Specifications or Specs: Examples of small, isolated behaviors a program should demonstrate, including input and output examples. Spec and test are interchangeable terms.

Example


Here's an example of a pseudocoded test:

Describe: add()
Test: "It adds two numbers together and returns the sum"
Code: const sum = add(2,3);
Expect(sum).toEqual(5);

Here's what the same test would look like using Jest, the testing framework we'll be using in Intermediate JavaScript and beyond:

describe('add()', () => {
  test('should correctly add two numbers together', () => {
    const sum = add(2,3);
    expect(sum).toEqual(5);
  });
});

Lesson 22 of 34
Last updated December 1, 2020