> For the complete documentation index, see [llms.txt](https://mistborn.gitbook.io/til-coding/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mistborn.gitbook.io/til-coding/javascript/automated-testing-with-mocha.md).

# Automated testing with mocha

## Why we need tests?

當撰寫一個函式，我們會預期產出的結果，我們可能會帶入不同參數用 `console` 來檢視結果，但若有錯誤修改後，又要一一測試之前帶入的參數，所以測試跟開發是分開的，測試可以很容易執行，檢查主要使用沒問題。

## Behavior Driven Development (BDD)

行為驅動的開發被用在許多專案中，BDD 包含 3 件事測試、文件、範例。

## Development of “pow”: the spec

我們要寫一個函式，pow(x, n) 該函式將產出 x 的 n 次方，在創建函式之前可以描述該函式的功能有 3 個規範。

1. `describe("title", function() { ... })` 描述函式名稱。
2. `it("title", function() { ... })`  描述函式功能
3. `assert.equal(value1, value2)` 帶入參數測試函數的產出是否正確

## The development flow

1. 撰寫規範
2. 撰寫基本函式可以跑測試
3. 執行測試框架 mocha ，檢測函示是否正確
4. 增加更多案例到測試中
5. 測試失敗修改函式
6. 重複 3 \~ 5 步驟

## The spec in action

測試主要用到 3 個 librarues。

* mocha 主要執行的框架 包含 `descibe` `it` 等函數。
* chai 有很多 assertions 函式，範例使用  `assert.equal`。
* Sinon 用來監視函數模擬內建函式。

```javascript
<!DOCTYPE html>
<html>
<head>
  <!-- add mocha css, to show results -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css">
  <!-- add mocha framework code -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
  <script>
    mocha.setup('bdd'); // minimal setup
  </script>
  <!-- add chai -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
  <script>
    // chai has a lot of stuff, let's make assert global
    let assert = chai.assert;
  </script>
</head>

<body>

  <script>
    function pow(x, n) {
      /* function code is to be written, empty now */
    }
  </script>

  <!-- the script with tests (describe, it...) -->
  <script src="test.js"></script>

  <!-- the element with id="mocha" will contain test results -->
  <div id="mocha"></div>

  <!-- run tests! -->
  <script>
    mocha.run();
  </script>
</body>

</html>
```

頁面分為 5 個部分

1. head 引入 libraries
2. script 撰寫測試的函示
3. 測試程式碼寫在 test.js
4. `<div id='mocha'>` 用來輸出 mocha 執行的結果
5. `mocha.run()` 執行 mocha

## Improving the spec

增加更多情況到測試中，一個測試只檢查一個功能。

```javascript
// method1
describe("pow", function() {

  it("raises to n-th power", function() {
    assert.equal(pow(2, 3), 8);
    assert.equal(pow(3, 4), 81);
  });

});

// method2 better
describe("pow", function() {

  it("2 raised to power 3 is 8", function() {
    assert.equal(pow(2, 3), 8);
  });

  it("3 raised to power 3 is 27", function() {
    assert.equal(pow(3, 3), 27);
  });

});
```

## Improving the implementation

```javascript
function pow(x, n) {
  let result = 1;

  for (let i = 0; i < n; i++) {
    result *= x;
  }

  return result;
}

// test
describe("pow", function() {

  function makeTest(x) {
    let expected = x * x * x;
    it(`${x} in the power 3 is ${expected}`, function() {
      assert.equal(pow(x, 3), expected);
    });
  }

  for (let x = 1; x <= 5; x++) {
    makeTest(x);
  }

});
```

## Nested describe

`makeTest()` 只用在 for 迴圈內，因此將他們綁在一起，下一個測試不會在用到。

```javascript
describe("pow", function() {

  describe("raises x to power 3", function() {

    function makeTest(x) {
      let expected = x * x * x;
      it(`${x} in the power 3 is ${expected}`, function() {
        assert.equal(pow(x, 3), expected);
      });
    }

    for (let x = 1; x <= 5; x++) {
      makeTest(x);
    }

  });

  // ... more tests to follow here, both describe and it can be added
});
```

```javascript
describe("test", function() {

  before(() => alert("Testing started – before all tests"));
  after(() => alert("Testing finished – after all tests"));

  beforeEach(() => alert("Before a test – enter a test"));
  afterEach(() => alert("After a test – exit a test"));

  it('test 1', () => alert(1));
  it('test 2', () => alert(2));

});

// result
Testing started – before all tests (before)
Before a test – enter a test (beforeEach)
1
After a test – exit a test   (afterEach)
Before a test – enter a test (beforeEach)
2
After a test – exit a test   (afterEach)
Testing finished – after all tests (after)
```

## Extending the spec

增加規範，程式碼出現錯誤，修改程式碼。

```javascript
// add spec
describe("pow", function() {

  // ...

  it("for negative n the result is NaN", function() {
    assert.isNaN(pow(2, -1));
  });

  it("for non-integer n the result is NaN", function() {
    assert.isNaN(pow(2, 1.5));
  });

});

// improve pow(x ,n)
function pow(x, n) {
  if (n < 0) return NaN;
  if (Math.round(n) != n) return NaN;

  let result = 1;

  for (let i = 0; i < n; i++) {
    result *= x;
  }

  return result;
}


```

* `assert.equal(value1, value2)` – checks the equality `value1 == value2`.
* `assert.strictEqual(value1, value2)` – checks the strict equality `value1 === value2`.
* `assert.notEqual`, `assert.notStrictEqual` – inverse checks to the ones above.
* `assert.isTrue(value)` – checks that `value === true`
* `assert.isFalse(value)` – checks that `value === false`
* …the full list is in the [docs](http://chaijs.com/api/assert/)
