Basics of Javascript Test Driven Development (TDD) with Jest
Tags: javascript, codenewbie, webdev, todayilearned
Published: 2021-07-04 06:21:34

## [JS#4 WIL :thinking: Post]
Test Driven Development (TDD)'s main idea is to simply start working on code by writing automated tests **BEFORE** writing the code that is being tested. There are many test-running systems in Javascript: _Jasmine_, _Jest_, _Tape_, and _Mocha_ to name a few. They have their special features but the syntax is very similar. The framework chosen should not be an issue because 
> writing tests is less about the syntax but more on the TDD philosophy, 

so I tried internalizing the concepts using Jest. My main goal while doing the exercise is to know the why's and whats of testing.

Before diving in, here are some notes I took from this brilliant talk, [The Magic of Testing](https://www.youtube.com/watch?v=URSWYvyc42M).

1. Why does most devs hate tests? Because they are slow and fragile and expensive (time).
2. It is perfectly valid to delete some tests. 
3. Unit Test Goals: They must be **thorough** (we want them to prove logically and completely that the single object under test is behaving correctly) and **stable** (we dont want to break the test everytime the implementation detail is changed 😟), **fast** and **few** (write tests for the most parsimonious expression [mmmmmm 🤔]).
4. Do not test private methods. But break this rule if it saves money during development.
5. A mock is a test double, it plays the role of some object in your real app. Ensure test double stays in sync with the API.
6. Trust collaborators that they will do the right thing. Insist on simplicity.
7. Getting better at testing takes time and practice.

**The object under test have three origin of messages :**

   :pushpin: **Incoming** - messages to the object from outside
   :pushpin: **Self** - messages sent by the object under test to itself
   :pushpin: **Outgoing** - messsages sent by the object to the outside.

> There are two types of messages : **query** and **command**. Queries return something or changes nothing. Command types return nothing but changes something.

### :pushpin: Grid of Test Rules
The grid of test results below shows how each type of message can be unit tested.

|Message Type|Query|Command|
|:---|:---|:---|
|Incoming|**Assert result**<br>Test incoming query messages by making assertions about what they send back.<br>Test the interface and not the implementation.|**Test incoming command messages by making assertions about direct public side effects.**<br>DRY it out. Receiver of incoming message has sole responsibility for asserting the result of direct public side effects.|
|Sent to Self|Ignore: Do not test private methods.|Ignore: Do not test private methods.|
|Outgoing|Ignore. The receiver of an incoming query is solely responsible for assertions that involve state.<br>If a message has no visible side effects, the sender should not test it|**Expect** to send outgoing command messages using mocks|

#### :pushpin: Advantages of TDD
1. Reduces bugs that may be introduced when adding new features or modifying existing features
2. Builds a safety net against changes of other programmers that may affect a specific part of the code
3. Reduces the cost of change by ensuring that the code will still work with the new changes
4. Reduces the need for manual (monkey) checking by testers and developers
5. Improves confidence in code
6. Reduces the fear of breaking changes during refactors

### :pushpin: Getting Started with Jest
Jest is a javascript testing framework focusing on simplicity but still ensures the correctness of the Javascript code base. It boasts itself to be **_fast and safe_**, reliably running tests in parallel with unique global state. To make things quick, Jest runs previously failed tests first and re-organizes runs based on how long test files take.

Moreover, Jest is very well-documented and requires little configuration. It indeed makes javascript testing delightful. It can be installed by using either `yarn` or `npm`.

#### :pushpin: Three Modes of TDD
1. **Obvious implementation**. You write the test with the implementation since you know how to implement the method to test.
2. **Fake it til you make it**. If you know the problem and solutions, but the way you code them up is not immediately obvious to you, then you can use a trick called "fake it 'til you make it."
3. **Triangulation** . This is the most conservative way of doing TDD. If you don't even know the solution, you just get to green at all costs , red-green, red-green loop.

#### :pushpin: Using Jest Matchers
##### Common Matchers
The simplest way to test a value is with exact equality.
```javascript
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});
```
The code snippet above returns an "expectation" object. The `toBe(3)` portion is the matcher. When Jest runs, it tracks all the failing matchers so it can print nice error messages. The `toBe` matcher uses `Object.is` to test the equality. 

##### Truthiness
In unit tests, the special values `undefined`, `null`, `false` might be needed to be checked also. Jest contains helpers that lets developers be explicit with what to expect. It is then good to use a matcher that most precisely corresponds to what the code is doing.

* `toBeNull` matches only `null`
* `toBeUndefined` matches only `undefined`
* `toBeDefined` is the opposite of `toBeUndefined`
* `toBeTruthy` matches anything that an `if` statement treats as true
* `toBeFalsy` matches anything that an `if` statement treats as false

##### Numbers
There are also Jest matchers for comparing numbers such as `toBeGreaterThan`, `toBeGreaterThanOrEqual`, `toBeLessThan`, `toBeLessThanOrEqual`. For floating point numbers, there are equality matcher like `toBeCloseTo`.

##### Strings
Strings can be checked against regular expressions using `toMatch`.

##### Arrays and Iterables
`toContain` can be used to check if a particular item can be found in an array or iterable.

##### Exceptions
`toThrow` can be used to check if a particular function throws a specific error. It is to be noted that the function being checked needs to be invoked within a wrapping function for the `toThrow` exception to work.

> There are also more advanced Jest matchers used for testing asynchronous code, i.e for callbacks and promises. 
 
#### :pushpin: Jest Testing Practice
This is my first time writing javascript unit tests using Jest. It is quite new so I needed some practice :smile:. I tried using the **obvious implementation** and **triangulation** mode of testing for some of the methods below. The full implementation of the methods and their corresponding tests can be found in my [Jest practice github repository](https://github.com/fatrixienicolieopetina/practice-jest-testing/tree/main/_js).

* `capitalize(string)` takes a string and returns that string with the first character capitalized.

[`capitalize.test.js`](https://github.com/fatrixienicolieopetina/practice-jest-testing/blob/main/_js/tests/capitalize.test.js)
```javascript
const capitalize = require('../capitalize');

test('should capitalize lowercase string correctly', () => {
  expect(capitalize("capitalize")).toBe("Capitalize");
});

test("should return '' for strings with length 0", () => {
    expect(capitalize("")).toBe("");
});

// other tests here
```

* `reverseString(string)` takes a string and returns it reversed. Below is a snippet of the test I wrote for a normal scenario.

[`reverse-string-test.js`](https://github.com/fatrixienicolieopetina/practice-jest-testing/blob/main/_js/tests/reverse-string.test.js)
```javascript
const reverseString = require('../reverse-string');

test('should reverse normal strings', () => {
  expect(reverseString("reverse")).toBe("esrever");
});

//other tests here
```
* A `calculator` object that contains the basic operations: `add`, `subtract`, `divide`, and `multiply`. The following test snippet below shows that the method will throw an error message if the divisor is zero.

[`calculator.test.js`](https://github.com/fatrixienicolieopetina/practice-jest-testing/blob/main/_js/tests/calculator.test.js)
```javascript
const calculator = require("../calculator");

//other tests here

test("should throw an error if divisor is 0", () => {
    expect(() => calculator.divide(20, 0)).toThrow("cannot divide by 0");
});
```
* `caesar cipher`. A caesar cipher is a substitution cipher where each letter in the text is shifted a certain places number down the alphabet. More info can be read [here](http://practicalcryptography.com/ciphers/caesar-cipher/). 

One thing to remember from this part of the exercise is that it is not needed to explicity test the smaller functions, just the public ones. If the larger function works then it must be the case that the helper methods are functioning well.

[`caesar-cipher.test.js`](https://github.com/fatrixienicolieopetina/practice-jest-testing/blob/main/_js/tests/caesar-cipher.test.js)
```javascript
const caesar = require("../caesar-cipher");

//other tests here

test('wraps', function() {
    expect(caesar('Z', 1)).toEqual('A');
});

test('works with large shift factors', function() {
    expect(caesar('Hello, World!', 75)).toEqual('Ebiil, Tloia!');
});

test('works with large negative shift factors', function() {
    expect(caesar('Hello, World!', -29)).toEqual('Ebiil, Tloia!');
});
```
* Array Analysis. This function takes an array of numbers and returns an object with the following properties: `average`, `min`, `max`, and `length`.

[`analyze.test.js`](https://github.com/fatrixienicolieopetina/practice-jest-testing/blob/main/_js/tests/array-analyze.test.js)
```javascript
const analyze = require("../analyze");
const object = analyze([1,8,3,4,2,6]);

test("should return correct average", () => {
    expect(object.average).toEqual(4);
});

test("should return correct min", () => {
    expect(object.min).toEqual(1);
});

// other tests here
```

Check out the github repository of the included snippets in [here](https://github.com/fatrixienicolieopetina/practice-jest-testing) for a complete picture of the tests.

The concepts and points above are the very basics of TDD using Jest. There are a lot more to learn, from more advanced matchers, mocking, testing asynchronous parts of the code, and others.  I am still to learn them and that is for another dev post :laughing:.

Cheers to continued learning! :wine_glass:
 

### [REFERENCES]
1. [The Importance of TDD](http://godswillokwara.com/index.php/2016/09/09/the-importance-of-test-driven-development/)
2. [TOP Testing Basics](https://www.theodinproject.com/paths/full-stack-javascript/courses/javascript/lessons/testing-basics)
3. [Jest Getting Started Documentation](https://jestjs.io/docs/getting-started)
4. [Jest Official Docs](https://jestjs.io/)