Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@takuya0301

Sencha Architect と Sencha Test の開発環境に REST API モックサーバーを追加する

More than 1 year has passed since last update.

Sencha Architect と Sencha Test の開発環境に REST API モックサーバーを追加する手順をメモっとく。

この記事では、Ext.ux.ajax.SimManagerExt.ux.ajax.Simlet を用いて REST API のモックサーバーを Sencha Architect と Sencha Test の開発環境に追加する。そうすることでコンポーネントごとにサーバーとの通信が発生するテストを書けるようにしている。

Sencha Architect と Sencha Test で開発環境を構築するの続き。

前提条件

この記事では下記バージョンの Sencha ツール群を使用している。

Tool Version
Sencha Architect 4.2.7
Sencha Ext JS 7.1.0.48
Sencha Cmd 7.1.0.16
Sencha Test 2.3.0.328

REST API モックサーバーを追加する

app.json にある依存パッケージの設定に "ux" を追加する。Ext.ux.ajax.SimManagerExt.ux.ajax.Simlet を使用するために必要な設定になる。

$ sed -i "" 's|"font-awesome"$|"font-awesome", "ux"|' test/app.json

RestSimlet.js と Server.js を app ディレクトリに追加する。

test/app/RestSimlet.js
Ext.define('RestSimlet', {
    extend: 'Ext.ux.ajax.JsonSimlet',
    alias: 'simlet.rest',

    doGet: function (ctx) {
        ctx.matches = this.getMatches(ctx);
        return this.callParent(arguments);
    },

    doPut: function (ctx) {
        return this.doGet(ctx);
    },

    doDelete: function (ctx) {
        return this.doGet(ctx);
    },

    privates: {
        getMatches: function (ctx) {
            var url = ctx.url.split(/\?|#/)[0],
                pattern = ctx.xhr.simlet.url;
            return url.match(pattern);
        }
    }
});
test/app/Server.js
Ext.define('Server', {
    requires: [
        'RestSimlet'
    ],

    singleton: true,

    start: resources => {
        Ext.ux.ajax.SimManager.init({
            defaultType: 'rest',
            delay: 0
        }).register([{
            url: RegExp('^/(\\w+)(/(\\d+))?$'),
            data: ctx => {
                var method = ctx.method.toUpperCase(),
                    resource = ctx.matches[1],
                    id = Number(ctx.matches[3]);

                resources = resources || {};
                resources[resource] = resources[resource] || [];

                var records = resources[resource];

                switch (method) {
                    case 'GET':
                        if (id) {
                            var index = records.findIndex(record => record.id === id);
                            if (index < 0) {
                                this.status = 404;
                                this.statusText = 'Not Found';
                                return;
                            } else {
                                this.status = 200;
                                this.statusText = 'OK';
                                return records[index];
                            }
                        } else {
                            this.status = 200;
                            this.statusText = 'OK';
                            return records;
                        }

                    case 'POST':
                        if (id) {
                            this.status = 405;
                            this.statusText = 'Method Not Allowed';
                            return;
                        } else {
                            var record = Ext.decode(ctx.xhr.body);
                            if (record) {
                                record.id = records.reduce((maximum, record) => Math.max(maximum, record.id), 0) + 1;
                                records.push(record);

                                this.status = 201;
                                this.statusText = 'Created';
                                return record;
                            } else {
                                this.status = 400;
                                this.statusText = 'Bad Request';
                                return;
                            }
                        }

                    case 'PUT':
                        if (id) {
                            var record = Ext.decode(ctx.xhr.body);
                            if (record) {
                                record.id = id;

                                var index = records.findIndex(record => record.id === id);
                                if (index < 0) {
                                    records.push(record);

                                    this.status = 201;
                                    this.statusText = 'Created';
                                    return record;
                                } else {
                                    records[index] = record;

                                    this.status = 200;
                                    this.statusText = 'OK';
                                    return record;
                                }
                            } else {
                                this.status = 400;
                                this.statusText = 'Bad Request';
                                return;
                            }
                        } else {
                            this.status = 405;
                            this.statusText = 'Method Not Allowed';
                            return;
                        }

                    case 'DELETE':
                        if (id) {
                            var index = records.findIndex(record => record.id === id);
                            if (index < 0) {
                                this.status = 404;
                                this.statusText = 'Not Found';
                                return;
                            } else {
                                records.splice(index, 1);

                                this.status = 204;
                                this.statusText = 'No Content';
                                return;
                            }
                        } else {
                            this.status = 405;
                            this.statusText = 'Method Not Allowed';
                            return;
                        }
                }
            }
        }]);
    },

    stop: () => Ext.ux.ajax.SimManager.simlets = []
});

app.js に依存クラスとして Server を追加する。

test/app.js
/*
 * This call registers your application to be launched when the browser is ready.
 */
Ext.application({
    requires: [
        'Ext.Panel',
        'Server'
    ],

    name: 'ExampleTest',

    launch: () => {
        var config = { floated: true, right: 0, bottom: 0 };
        if (Ext.isChrome) Ext.merge(config, { iconCls: 'x-fab fa-chrome',  title: 'Chrome'  });
        if (Ext.isEdge  ) Ext.merge(config, { iconCls: 'x-fab fa-edge',    title: 'Edge'    });
        if (Ext.isGecko ) Ext.merge(config, { iconCls: 'x-fab fa-firefox', title: 'Firefox' });
        if (Ext.isOpera ) Ext.merge(config, { iconCls: 'x-fab fa-opera',   title: 'Opera'   });
        if (Ext.isSafari) Ext.merge(config, { iconCls: 'x-fab fa-safari',  title: 'Safari'  });
        Ext.create('Ext.Panel', config).show();
    }
});
$ git add .
$ git commit -m "Add REST API mock server"

REST API モックサーバーの動作を確認する

Sencha Architect を開く。

User モデルを作成する。

  • userClassName: User
  • fields:
    • id
    • name
  • proxy:
    • type: rest
    • url: users

User model

Users ストアを作成する。

  • userClassName: Users
  • storeId: Users
  • autoLoad: true
  • model: Example.model.User
  • data:
[
    { id: 1, name: "Alice"   },
    { id: 2, name: "Bob"     },
    { id: 3, name: "Charlie" },
    { id: 4, name: "Dave"    }
]

Users store

Users グリッドを作成する。デザインビューにレコードが表示されていれば動作している。

  • userClassName: Books
  • store: Users
  • columns:
    • Column
      • xtype: gridcolumn
      • flex: auto
      • width: (none)
      • dataIndex: id
      • text: id
    • Column
      • xtype: gridcolumn
      • flex: 1
      • width: (none)
      • dataIndex: name
      • text: name

Code view of Users grid

Design view of Users grid

Sencha Architect のプロジェクトを保存する。そして、プレビューをクリックし、下記のように表示されていれば実装がきちんとできている。

Users grid in main application

Sencha Studio で Jasmine テストスイートを UsersTest という名前で作成する。

Create Jasmine test suite

Users グリッドに自動ロードで5件のユーザーが読み込まれることを確認するテストを記述する。

ここで beforeEach メソッドで、REST API モックサーバーを開始させ、テスト対象の Users ストアと Users コンポーネントを生成し、テストアプリケーションのビューポートにコンポーネントを追加している。テストではそのグリッドを取得し、グリッドが参照するストアのレコード数が5であることを検証している。afterEach メソッドでテストのために生成したストアとコンポーネントを破棄し、REST API モックサーバーを停止させ、ひとつのテストケースが完了する。

test/test/component/UsersTest.js
describe("UsersTest", function () {
    let store;
    let component;

    beforeEach(function () {
        Server.start({
            users: [
                { id: 1, name: "Alice"   },
                { id: 2, name: "Bob"     },
                { id: 3, name: "Charlie" },
                { id: 4, name: "Dave"    },
                { id: 5, name: "Eve"     }
            ]
        });
        store = Ext.create("Example.store.Users");
        component = Ext.create("Example.view.Users");
        Ext.getApplication().viewport.add(component);
    });

    afterEach(function () {
        component.destroy();
        store.destroy();
        Server.stop();
    });

    it("loads 5 records", function () {
        ST.grid("users")
            .and(grid => {
                var users = grid.getStore();
                expect(users.count()).toBe(5);
            });
    });
});

このテストが無事に完了すれば開発環境に REST API モックサーバーを追加できたことになる。


この記事では Sencha Architect と Sencha Test の開発環境に REST API モックサーバーを追加する方法について説明した。SPA のコンポーネントテストも高速に実現できる環境なので、興味のある人はトライアル版で試してみてほしい。

1
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
takuya0301
Mikatus 株式会社の VP Engineering 兼デザイングループグループリーダー。最近は Event Storming と Lagom に興味がある。
mikatus
税理士・会計事務所向けクラウドシステムの企画、開発、提供事業を行っております。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?