Swift で MessagePack-RPC

ねこじゃらしの y-okubo と申します。

今回は、Swift で MessagePack-RPC を実現するライブラリの使い方を紹介します。

MessagePack-RPC については、こちらをご覧ください。

www.slideshare.net

ひとことで言えば、MessagePack (JSON) を使った RPC です。

各言語にライブラリが用意されており、異なる言語で作ったアプリ間でプロセス間通信をするのに便利です。

github.com

なぜか公式にはリストアップされていないのですが、Objective-C 版もあります(CocoaPods 対応がありがたい)。

github.com

今回は、コレを Swift から使ってみることにしました。

プロジェクトの作成

まず、Xcode で適当なプロジェクトを作成します。 今回は OS X - Application - Command Line Tool のプロジェクトを作成します。

f:id:nekojarashi-Inc:20160810182458p:plain

ターミナルでそのディレクトリまで移動して、以下のコマンドで CocoaPods をインストールします。

pod init

この後は、作成された Podfile に使用するライブラリを追加していきます。 Podfile を編集して、以下の内容を追加します。

pod 'MPMessagePack'

以下のコマンドを実行してライブラリをインストールします。

pod install

CocoaPods のライブラリを Xcode で使うには、先のコマンドで生成された .xcworkspace ファイルを開きます。

サンプルコード

簡単なコードを書いて動作確認をしてみます。

まずはサーバのリクエストハンドラを追加します。

RPC が呼び出されると必ずこのコードが実行されます。

メソッド名で分岐することで RPC 呼出しに対応したコードを記述できます。

今回は実験的なコードなので、そのような分岐はせず同じような戻り値を返しています。

func serverRequestHandler(messageId: NSNumber?, method: String?, params: [AnyObject]?, completion: (MPRequestCompletion)?) {
    if let method = method, let params = params {
        print("(Server) method:", method)
        print("(Server) params:", params)
    }
    
    completion!(nil, "Return from server(\(method))")
}

サーバを起動するコードを追加します。

リクエストハンドラを設定して、ポート 5001 番でサーバを起動してみます。

// Server

var server: MPMessagePackServer = MPMessagePackServer()

server.requestHandler = serverRequestHandler

do {
    try server.openWithPort(5001)
}
catch let error as NSError {
    print(error)
}

クライアントのコードを追加します。

こちらは非同期実行のコードになります。

var client: MPMessagePackClient = MPMessagePackClient()

client.openWithHost("127.0.0.1", port: 5001, completion: {error in
    client.sendRequestWithMethod("method_hoge", params: ["hoge", "42"], messageId: 1, completion: {error, result in
        if let error = error {
            print("Error:", error)
        }
    })
})

同期実行のコードは以下になります。 こちらは Exception を throw するので do ~ try ~ catch で例外ハンドリングをする必要があります。

client.openWithHost("127.0.0.1", port: 5001, completion: {error in
    do {
        try print(client.sendRequestWithMethod("method_fuga", params: [], messageId: 1, timeout: 2.0))
    } catch let error as NSError {
        print("Error:", error)
    }
})

注意点として、Command Line Tool でプロジェクトを作成している場合は、以下のコードも追加してください(このコードを追加しないと動きません)。

このコードで無限ループを回してクライアントからのリクエストを待ちます。

NSRunLoop.currentRunLoop().run()

では、コードを実行してみます。

2016-08-12 15:35:40.222 MsgpackRpcSwiftExample[11273:1429215] -[MPMessagePackServer connectionWithInputStream:outputStream:]:38: [Server] Client connected
(Server) method: method_fuga
(Server) params: []
(Server) method: method_hoge
(Server) params: [hoge, 42]

クライアントからの RPC 呼出しに対してサーバが値を受け取っているのがおわかりいただけるかと思います。

まとめ

MessagePack-RPC を Swift から使う方法について簡単に紹介いたしました。

他の言語についてもライブラリが存在するので、異なる言語間でプロセス間通信を行うような処理が必要になったとき、気軽に使えて便利です。

最近はマイクロサービス化によりコンポーネント疎結合化が加速しており、設計の都合やチームのスキルセットによって言語を使い分けるシーンも増えているのではないでしょうか。

そのようなときに、それらコンポーネントを繋ぐ方法として MessagePack-RPC は有力な選択肢になり得ると思います。

MessagePack-RPC を使う上でこの記事がお役に立てば幸いです。

お知らせ

ねこじゃらしでは Swift に限らず RubyJavaScriptプログラマ、UI/UX デザイナを募集しております。

ご興味をお持ちいただけましたら、以下のリンクからお問い合わせください。

www.nekojarashi.com

Go の パッケージ管理ツールは glide がイイ感じ

ねこじゃらしの y-okubo と申します。

今回は、仕事で Go を書いたときに使ったパッケージ管理ツール glide について簡単にですが、紹介させていただきます。

そもそも Go にはパッケージ管理を行うための仕組みがありません。
パッケージ管理をし易くするための仕組みが、現行の 1.6 で導入された Vendoring です。

Vendoring の詳しい説明はググっていただくとして、実際にはパッケージ管理を行うためのツールが必要になります。

この手のいわゆるパッケージ管理ツールは gbgom が有名のようですが、今回は試してみて便利だった glide を採用することにしました。

glide 使い方

glide コマンド自体をインストールします。

$ go get github.com/Masterminds/glide
$ go install github.com/Masterminds/glide

glide コマンドで依存パッケージをインストールします。

$ glide create
$ glide install

これで、対象ディレクトリに glide.yaml と glide.lock ができます。
また、vendor ディレクトリも作成され、その中に依存するパッケージが格納されます。
glide.yaml と glide.lock だけをリポジトリに格納して、vendor ディレクトリは Git で Ignore 指定してください。

このリポジトリを初めて使う方は、glide コマンド自体をインストールした後に glide install をすれば、vendor ディレクトリに依存パッケージがインストールされます。

まとめ

私は普段 Ruby で開発をしていて Bundler を使っているのですが、 コマンド体系や構成ファイルがそれと似ており、違和感なく使うことができました。

もう一つの候補であった gom と比べると、コマンド体系や YAML 書式がスッキリしているのもイイ感じで、ドキュメントがよく整備されているのもあって、glide を正式導入することにしました。

逆に言えば、gom は Bundler によく似たコマンド体系や YAML 書式を持っているので、かなり複雑な設定もできそうです(Go 標準のツールがシンプルで強力なので複雑な設定自体が不要な気もしますが)。

glide や gom 以外のパッケージ管理を選択されるのも良いかと思いますが、現時点では glide が個人的に最もオススメできるパッケージ管理ツールです。

glide で快適な Go 開発を!

お知らせ

ねこじゃらしでは Go に限らず RubyJavaScriptプログラマ、UI/UX デザイナを募集しております。

ご興味をお持ちいただけましたら、以下のリンクからお問い合わせください。

www.nekojarashi.com

開発部LTを開催しました! (テーマ Rack について)

バックアップ開発部の r-fujiwara です。 ねこじゃらし開発部では、二週間に一度、メンバーの最近の興味を持っていることや、業務で行っていることなどを持ち回りで LT を行っています。

今回

今回は記事を書いている r-fujiwara の番でした。 主に Rack(Application / Server) の話をさせて頂きました。

分かったつもりになるかもしれないRack

speakerdeck.com

この発表をしようと考えた動機

  • (冒頭にもありますが、)教えている後輩に「Rack って何ですか?」と聞かれて答えに窮してしまったので、この機会にちょっと触れてみたかったから。

  • RailsDevelopment で開発していると、やはり「WEBrickって何よ?」「Puma って何よ?」って所に関して、多分自分自身もあまり自信を持って答えられ無さそうだったので、この機会で整理したかったから。

  • しかしながら、Rack のことをあまりにも普通に話しすぎてしまっても、それはそれで何かしらのドキュメントを読めばいい話であると考え、今回は「車で例える」というアプローチを取り、より噛み砕いた説明が出来ないか、と考えて今回の Rack の発表といたしました。

こういった背景があります。

個人的感想

  • 自分としては rack は非常に面白い仕組みであり、Rack Server や Rack Application のソースを読むだけでもかなり力になる、と考えております。

  • アプリケーションを作るだけでなく、その中身がどう動いているのか?とちゃんと理解して然るべき技術選定出来るエンジニアになっていきたいと私は考えておりますし、またそういった人材をねこじゃらしで育てていければ、と考えております。

Go の標準っぽいプレゼンを作る - 完全版

バックアップ開発部の y-okubo と申します。

今回は、以下の記事で書いた内容の(ほぼ)完全版を書きます。

qiita.com

概要

  • Go 界隈の方々がよく使っているプレゼンツール(フォーマット)です
    • とは言えど、このツールを使っていないプレゼンもいっぱいあります
  • 当然ですが Go で書かれています
  • シンタックスハイライトは美しいですが、フォーマットの指定方法が特殊です

インストール

GOPATH を設定した上でインストールします

$ go get golang.org/x/tools/cmd/present

サーバ起動

作成するスライドを格納するディレクトリに移動してから実行します

$ present

デフォルトでは http://127.0.0.1:3999 にアクセスすることでプレゼンが見られるようになります。

フォーマット

プレゼンのメタ情報

Title of document
Subtitle of document
15:04 2 Jan 2016
Tags: foo, bar, baz

Author Name
Job title, Company
joe@example.com
http://url/
@twitter_name

* Title of slide or section (must have asterisk)

最低限、この内容(アスタリスクの部分)までないと表示できません(以外とハマります、コレ)。

  • 一行目はプレゼンのタイトルです
  • 二行目はプレゼンのサブタイトルです
  • 三行目はプレゼンの発表日時?
  • 四行目はタグ?ですかね(どこで役に立つのかは不明)
  • 空行を挟んで個人プロフィール各種(名前・役職・会社名・メールアドレス等々)
  • さらに空行を挟んで次のスライドタイトルになります

箇条書きとセクション

- bullets 1
- more bullets 1
- a bullet with 1
* Slide title or section
** Sub section
*** More sub section

サブセクション以降はプレゼンの下から順に表示されます。

例えば

* Title of slide or section (must have asterisk)

** Subsection1

- bullets 2
- more bullets 2
- a bullet with 2

*** Subsubsection2

- bullets 1
- more bullets 1
- a bullet with 1

これは

f:id:nekojarashi-Inc:20160531123924p:plain

こんな感じで表示されます(正直ちょっと違和感があります)。

テキスト

* Text

Some More text

  Preformatted text
  is indented (however you like)

f:id:nekojarashi-Inc:20160530120910p:plain

書いた内容がそのまま反映されます。

インデントを行うとその部分が整形済みテキスト(pre タグ)になります。

フォント

* Font

_italic_
*bold*
`program`
_this_is_all_italic_
_Why_use_scoped__ptr_? Use plain ***ptr* instead.

上から順番に * イタリック * ボールド * プログラム? * イタリック * ボールド(先頭のアスタリスクもボールドにしたいのでエスケープしてる?)

こんな感じで表示されます。

f:id:nekojarashi-Inc:20160531124228p:plain

インラインリン

* Inline link

[[url][label]], or [[url]]

f:id:nekojarashi-Inc:20160530121337p:plain

ラベルを付けるか、そのままリンクを表示するかの違いですね。

リンク

* Link

.link http://golang.org golang.org

f:id:nekojarashi-Inc:20160530121355p:plain

Playground で直接実行可能な状態にする

* The Go Playground

.play demo.go

こんな UI が付きます。

f:id:nekojarashi-Inc:20160531124021p:plain

コード

コードは別ファイルの置く必要があります。

* Code

.code -numbers test.go /START OMIT/,/END OMIT/

/開始箇所/,/終了箇所/ で表示するコードを指定します。 なお、正規表現が使えます。

イメージ

* Images

.image gopher.png 408 300

高さと幅を指定できます。

f:id:nekojarashi-Inc:20160531124632p:plain

公開する

http://go-talks.appspot.com で 動的に表示できます。

http://go-talks.appspot.com/github.com/owner/project/file.ext

という形式で指定すれば、作成したプレゼンが表示されます。

GitHub URL 途中の blobmaster は 取り除いてください。

こちらは表示例です。

http://go-talks.appspot.com/github.com/y-okubo/go-present-example/sample.slide

まとめ

いかがでしょうか。

かなりクセのあるフォーマットで書きやすいとは言えないのですが、お、できるな!と思わせるプレゼンが作れるので、 Go のコミュニティで発表する予定の資料は present で、それ以外では普段お使いのプレゼンソフトを使われのが良いかと思います。

さらに詳細なフォーマットについては present の GoDoc をご覧ください。

godoc.org

お知らせ

ねこじゃらしでは Go に限らず RubyJavaScriptプログラマ、UI/UX デザイナを募集しております。

ご興味をお持ちいただけましたら、以下のリンクからお問い合わせください。

www.nekojarashi.com

GlusterFS + Go の開発環境を Docker で構築

バックアップ開発部の y-okubo と申します。

今回は「gogfapi を使った簡単な API Server」を作るにあたり、Docker を使って Go の開発環境を構築した手順を紹介させていただきます。

gogfapi を使った〜」は弊社が毎年行っている開発合宿のお題として選んだ物で(社内的にはもうちょっとクールなお題にしています)、そちらは別の機会に紹介したいと思います。

構成

開発環境なのでデータの永続化は考慮していません。

f:id:nekojarashi-Inc:20160523170935p:plain

サーバ

当初は DockerHub でイメージを探したのですが、イメージを生成すると GlusterFS のインストールで失敗する事が多いので自作することにしました。

Dockerfile はこんな感じです(途中の root:password はとても意識が低いので適宜書き換えてご利用ください)。

FROM centos

ENV container docker

# Install require packages
RUN yum --setopt=tsflags=nodocs -y update
RUN yum --setopt=tsflags=nodocs -y install wget nfs-utils openssh-server vim
RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs

# Added to enable systemd. Reference: http://developerblog.redhat.com/2014/05/05/running-systemd-within-docker-container/
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

# Add epel repository
RUN wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-6.noarch.rpm
RUN rpm -ivh epel-release-7-6.noarch.rpm

# Install GlusterFS packages
RUN wget http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/glusterfs-epel.repo -O /etc/yum.repos.d/glusterfs-epel.repo
RUN yum -y update
RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install glusterfs glusterfs-server glusterfs-fuse glusterfs-geo-replication glusterfs-cli glusterfs-api glusterfs-api-devel glusterfs-devel
RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install attr iputils iproute
RUN yum clean all

RUN echo 'root:password' | chpasswd
VOLUME [ “/sys/fs/cgroup” ]

EXPOSE 111 245 443 24007 2049 8080 6010 6011 6012 38465 38466 38468 38469 49152 49153 49154 49156 49157 49158 49159 49160 49161 49162

RUN systemctl disable nfs-server.service
RUN systemctl enable rpcbind.service
RUN systemctl enable sshd.service
RUN systemctl enable glusterd.service
CMD ["/usr/sbin/init"]

COPY ./startup.sh /var/startup.sh
RUN chmod +x /var/startup.sh
RUN echo /var/startup.sh >> /root/.bashrc

ポイントは

  • EPEL レポジトリを追加
  • startup.sh を実行

しているところでしょうか。

GlusterFS のインストールに失敗していたのが yum install あたりだったので、自分で EPEL レポジトリを追加しています。

startup.sh の内容についてはこの後に説明します。

その他の内容は GlusterFS official docker image を参考にしています。

startup.sh の内容は以下になります。

#!/bin/bash

# Create volume
mkdir /var/gfs_srv_vol
gluster peer probe $HOSTNAME
gluster volume create gfs_volume $HOSTNAME:/var/gfs_srv_vol force
gluster volume start gfs_volume
gluster volume quota gfs_volume enable

# Mount volume
mkdir /var/gfs_cli_vol
mount -t glusterfs -o acl $HOSTNAME:/gfs_volume /var/gfs_cli_vol

こちらでは GlusterFS のボリューム作成とローカルマウントを行っています。

Dockerfile の中の RUN で実行したかったのですが、$HOSTNAME 環境変数を使いたかったのでシェルスクリプトで実行するようにしてあります。

クライアント側

サーバ側とほぼ一緒の Dockerfile になります(こちらも途中の root:password はとても意識が低いので適宜書き換えてご利用ください)。

FROM centos

ENV container docker

# Install require packages
RUN yum --setopt=tsflags=nodocs -y update
RUN yum --setopt=tsflags=nodocs -y install wget nfs-utils openssh-server vim
RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs

# Added to enable systemd. Reference: http://developerblog.redhat.com/2014/05/05/running-systemd-within-docker-container/
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

# Add epel repository
RUN wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-6.noarch.rpm
RUN rpm -ivh epel-release-7-6.noarch.rpm

# Install GlusterFS packages
RUN wget http://download.gluster.org/pub/gluster/glusterfs/3.7/LATEST/EPEL.repo/glusterfs-epel.repo -O /etc/yum.repos.d/glusterfs-epel.repo
RUN yum -y update
RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install glusterfs glusterfs-fuse glusterfs-cli glusterfs-api glusterfs-api-devel glusterfs-devel
RUN yum --setopt=tsflags=nodocs --enablerepo=epel -y install attr iputils iproute
RUN yum clean all

RUN echo 'root:password' | chpasswd
VOLUME [ “/sys/fs/cgroup” ]

EXPOSE 111 245 443 24007 2049 8080 6010 6011 6012 38465 38466 38468 38469 49152 49153 49154 49156 49157 49158 49159 49160 49161 49162

RUN systemctl disable nfs-server.service
RUN systemctl enable rpcbind.service
RUN systemctl enable sshd.service
CMD ["/usr/sbin/init"]

RUN yum --setopt=tsflags=nodocs -y groupinstall "Development Tools"
RUN wget https://storage.googleapis.com/golang/go1.6.2.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.6.2.linux-amd64.tar.gz

COPY ./hello.c /root/hello.c

RUN mkdir -p /root/go/bin \
  && echo 'export GOROOT=/usr/local/go' >> /root/.bashrc \
  && echo 'export GOPATH=/go/path' >> /root/.bashrc \
  && echo 'export GOBIN=/go/bin' >> /root/.bashrc \
  && echo 'export PATH=$PATH:$GOROOT/bin:$GOBIN' >> /root/.bashrc

こちらは最後の方で Go 開発環境のインストールと設定を行っています。

Go のソースコードはローカルマシン側に置いて、コンテナ起動時にマウントして参照できるようにしています。

イメージ生成

サーバ側

$ docker build -t glusterfs-server server

クライアント側

$ docker build -t glusterfs-client client

コンテナ起動

サーバ側

$ docker run --privileged -tid -p 20022:22 --hostname gfserver --name gfserver glusterfs-server

--privileged で権限を与えて SELinux 等の影響を受けないようにしています。

クライアント側

$ docker run --privileged -tid -p 20023:22 -p 8080:8080 -v /Users/y-okubo:/go/path --link gfserver:server --hostname gfclient --name gfclient glusterfs-client

--link で先ほど起動したコンテナを参照できるようにしてあります。

コンテナへ SSH ログイン

サーバ側

$ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 20022 root@localhost

クライアント側

$ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 20023 root@localhost

セキュリティ上よろしくないのですが、毎回 OpenSSH の警告が出るのを抑止しています。

クライアント→サーバの疎通確認(libgfapi 経由でのアクセス)

クライアント側に SSH ログインして、イメージ作成時に用意(Dockerfile にて指定)した hello.c をビルド・実行します。

hello.c のソースは以下になります。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <glusterfs/api/glfs.h>

int main (int argc, char** argv) {
    const char *gfserver = "server";
    const char *gfvol = "gfs_volume";

    int ret;
    glfs_t *fs;
    glfs_fd_t *fd;

    fs = glfs_new(gfvol);   // Virtual filesystem type struct
    glfs_set_volfile_server (fs, "tcp", gfserver, 0);
    ret = glfs_init (fs);
    if (ret) {
        printf( "Failed to connect server/volume: %s/%s\n", gfserver, gfvol );
        exit(ret);
    }

    char *greet = "Hello, Gluster!\n";
    fd = glfs_creat(fs, "greeting.txt", O_RDWR, 0644);
    glfs_write(fd, greet, strlen(greet), 0);
    glfs_close(fd);
    return 0;
}
$ gcc hello.c -lgfapi
$ ./a.out

まとめ

今回は GlusterFS の Go での開発環境を Docker で構築した際の流れを駆け足でご紹介しました。

今回はサーバ側でローカルマウントするようにしていますが、クライアント側でサーバ側のボリュームを FUSE マウントすれば別の用途でも使えるのではないかと考えています。

GlusterFS + Go という組み合わせにどれくらいの需要があるかは分かりませんが、何かのお役に立てれば幸いです。

お知らせ

ねこじゃらしでは Go に限らず RubyJavaScriptプログラマ、UI/UX デザイナを募集しております。

ご興味をお持ちいただけましたら、以下のリンクからお問い合わせください。

www.nekojarashi.com

既存のiOSプロジェクトにUI自動テストを実装したの話

実装したアプリはWebAPIを使用したストレージアプリで、取得してきたデータをTableView(またはCollectionView)で表示する画面が複数あります。

開発人数は2名で別に品質管理チームがおり、品質管理チームにテストして貰う前に、UI自動テストを行うという目的です。

環境

  • Xcode version 7.x
  • KIF version 3.3.0

kif-framework/KIF: Keep It Functional - An iOS Functional Testing Framework

どこまでUIテストを行うか?

自動UIテストの範囲は広くどこまでやるかが明確ではないため、最終的に組み込まれたものと、そうではないものに分けて記載します。

自動テストしたこと

  • シェルスクリプトを起動するだけすべてのUIテストが完了するようにする
  • デバイスごと動作することを確認する(iPhoneiPad)
  • バージョンごとに動作することを確認する(iOS9とiOS8)
  • ユーザー名、パスワードを入力してログインできる
  • すべてのボタンが表示されていることを確認する
  • すべてのボタンが動作されていることを確認する
  • 検索が動作されていることを確認する
  • アラートを表示は確認する
  • TableViewに表示されている文字を確認する
  • テスト中にスクリーンショットを保存する

複数デバイスで起動するためはxcodebuildシェルスクリプトで起動するようにする必要がありました。

UIテストは開発者視認できないほど早く動作し目視の確認が難しいため、テスト時にスクリーンショットを保存するのは有効です。 またスクリーンショットをフォルダ別にデバイス名、iOSバージョン、アプリバージョンを記載することで、バージョンごとの画面ログとして効果があります。

すべてのUIテストはファイルごと独立して動くようになっています、これはKIFでUIテストの順番を指定できないためです。 例えばLoginUITest.swiftLogoutUITest.swiftがあるときにLoginUITest.swiftが先にテストされるとは限りません。 そのためLoginUITest.swiftのはじめにログアウト処理、LogoutUITest.swiftがのはじめにログイン処理を入れてあります

自動テストしなかったこと

  • ネットワークエラー時
    • 画面数xネットワークエラー数は数が多くなりすぎる上にエラーテストとUIテストを同時にやるのは難しい
  • ログイン時のユーザー名、パスワード入力のエラーケース
    • 上記と同じくエラーケースが多いので、ほとんどやらなかった
  • ユーザーが開発者が想定外の操作をするケース
    • ボタンに対して連続タップをする、高速でスワイプをするなどのケース
      • 操作後に正常であることを示すテストが難しかったため

例外処理時のテストを実装するにはかなり時間がかかり、ほとんど実装はしませんでした。

KIFで発生した問題

  • 日本語のキーボードは無効にしておく

    • 試験をしているとシミュレーターの入力モードが日本語になってしまうことがあったので、日本語入力にならないように設定を変更しました。
    • シミュレーターのiOSのSettings > General > Keyboard > で Japanese-Kana を削除
  • 連絡帳へのアクセスを許可しますか?などのシステムアラートは検知できないことがある

    • tester.acknowledgeSystemAlert()が動作しないことがあります
    • 解決法が無いですが、予めシステムの許可を設定するか、目視でタップする用にしていました
  • UIテストの起動に失敗する

  • エラーログも出ないことがあるxcodeを再起動することで、起動できたりもします

  • 表示されているUIが検知できないでテストが失敗する

  • XCTestでも発生する、現状で有効な回避方法はないと思います

実装後の感想

  • 今のところスマホのUIテストにベストプラクティはないので、手探りでやるしかない
  • 状態遷移が多いアプリはテスト作成に非常に時間がかかる
  • Unitテストと違い、UIテストを必須にすることは非常に難しい

  • メリット

小ネタ

  • Xcode を立ち上げて Command + 5 で表示されるテストビューで個別に実行できます
  • Xcode を立ち上げて Command + Option + U ですべてのテストを実行できます
  • シミュレーターに表示されている Accessibility Inspector を非表示にしたいときは、シミュレーター内の Setting > General > Accessibility > Accessibility Inspector から非表示にできます