n8nでJavaScriptコードをデバッグしたりテストする

# はじめに
**n8n**というワークフローのサービスがあるのですが、日本ではほとんど話題になっていません。SaaSもあるけど、自分で建てることもできて、それだと月数百円でできるので、是非広まってほしい。自分で建てる方法は下記をご覧ください。

https://qiita.com/yo16/items/4c1b81c40db932e2bf8d

ちなみに、Qiitaで大変バズっていた『しらせ君』、これはn8nで動いていると書かれています。

https://qiita.com/hirokidaichi/items/ff638d2574955f7c6787

わたしも真似をして、Slackの通知ボット『Tsutchy』を作り、かなり活躍しています。

## 本題
n8nでは、**Notionからデータを取得**したり、**Google Driveにファイルを格納**したり、**Xでツイート**したり、APIさえ公開されていれば、いろいろなことができます。でもその組み合わせだけだとあまり問題解決には至らず、途中で加工します。
単純なところだと、Notionから取得した情報をもとにツイートする文面を作るとか、AというデータソースとBというデータソースを合わせて何かするとか、まぁ切りがないわけですが。

そういう**痒い所には、JavaScriptを書くモジュール**があります。これはエンジニアにとっては結構大きいです。（Pythonでも書けます）
ループさせたいとか、フィルタしたいとか、Group Byしたいとか思っても、それ用のモジュールの入力方法がわからないとかできない、ストレスがたまるサービスもあるのです。外部のJavaScriptを実行するサービスに登録して、それを呼び出すとか。まどろっこしすぎる！

そんな便利なn8nですが、課題があって、今回の記事。**デバッグしづらい問題**。
JavaScriptのモジュールで、コードを書く欄は👇この画面。大きな入力欄があるだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/271a417d-7342-4d04-b96c-74d2ae4ebd89.png)

公式掲示板でも[デバッグはどうやるの](https://community.n8n.io/t/how-to-debug-javascript-in-code-node/22394)って話題に出ていて、`console.log()`しかないよ😭という会話がありました。

そうは言ってもそれはちょっと・・・ということで

# 解決案：VSCodeで書く
普通ー・・・。すみません。

メリットは３つあります。

1️⃣ テストコードをしっかり書いて、実行しておくことで、インプットとアウトプットの想定が可視化されます。

2️⃣ n8nでは、前のワークフローを何度も実行できないことがあるのですが、外でテストができれば、何度も実行できます。何度も実行できないとは、例えばXへポストし、コードでその結果を使うとか。

3️⃣ 細かく関数化することで、細かくテストできます。



## メイン処理を関数化
Codeモジュールの大きな入力欄１つに対して、１つのファイル・１つの関数で定義して、jestでその関数をテストするだけです。はい、普通。
ちょっとした工夫としては、関数にするっていうことくらいでしょうか。

```JavaScript:code1.js
// 新しいフィールドを作って1を設定する
function code1() {
  for (const item of $input.all()) {
    item.json.myNewField = 1;
  }
  
  return $input.all();
}
// return oneCode();
```
こんな感じで、`Code1.js`は関数定義で、テスト後、それをn8nへコピペすればOK。

VSCodeならテスト💯もできるし、デバッグ🪲もできる。

## $オブジェクトの定義💰
n8nでは、$オブジェクト（jQueryではありません）で、ワークフローの前に実行した結果を参照できます。`$('node1').all()`で、`node1`の結果配列要素を取得。

デフォルトで入っている`$input`は、直前の処理のアウトプットだと思いますが、順序を入れ替えたりすると"直前の処理"というのが変わって嫌なので、`$('node1')`とノード名を指定するようにしています。

コード内では、それを書くので、テスト用に作りました。👇
私は、今のところ`$('～～').all()`と`$('～～').first()`しか使わないので

```JavaScript:n8n_object.js
/*
    n8nのcodeモジュールで使用する"$"関数をモック
*/
const mockResults = {};

const $ = function (nodeName) {
    return {
        // 前処理の結果データを登録する
        registerMockData: (objectArray) => {
            console.assert(
                Array.isArray(objectArray),
                'objectArray must be an array'
            );

            mockResults[nodeName] = objectArray.map((obj) => ({json: obj}));
        },

        // n8nで使用する関数
        all: () => mockResults[nodeName],
        first: () => mockResults[nodeName][0],
    };
};

module.exports = $;
```

これをテストで使うときには、👇こんな感じで。
```JavaScript:Code1.test.js
const $ = require('../n8n_object');
const code1 = require('../code1');

describe('code1', () => {
    it('code1_simple_test1', () => {
        // テストデータの準備
        $('node0').registerMockData([
            {
                id: 1,
                val: "abc",
            },
            {
                id: 2,
                val: "def",
            },
        ]);

        // 実行
        result = code1();

        // 検証
        expect(result.length).toBe(2);
        expect(result[0].id).toBe(1);
        expect(result[0].val).toBe("abc");
        expect(result[0].myNewField).toBe(1);
        expect(result[1].id).toBe(2);
        expect(result[1].val).toBe("def");
        expect(result[1].myNewField).toBe(2);
    });
});
```

jestのテストケースを、VSCodeのデバッグ実行ができるので、デバッグもできます。この設定は「vscode jest デバッグ」あたりでググれば出てきます。

# おわりに
n8nはとても便利なのですが、コーディングがメインではないので、手厚くはないです。なので自前で外に環境を作って、外でテストをして、n8nへ移行するという形でした。

Qiitaを見てn8nを使うような人は、Codeモジュールは必ず使うと思います。これを作っておくことで安心感・安定感が増すので、n8nを使う人はやってみてください。

ではよきワークフローライフを！