Vim PluginでもUnit testを回したい！

# Vim PluginでもUnit testを回したい！

こんにちは、IKです。
ところで皆さんは毎日Vim scriptを書き、Vim Pluginを公開していると思いますが、
**[Unit test](https://ja.wikipedia.org/wiki/%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88)**、したくないですか...?
したいと思ったそこの貴方が今回の対象です!!!

この記事を書く際に、@kamyknさんの[spelunker.vim](https://github.com/kamykn/spelunker.vim)を参考にしています。

## 用意するもの

以下の3点です

- [Vim](https://github.com/vim/vim)（必須）
- [CircleCI](https://circleci.com/) （お好みで[travis](https://travis-ci.org/)、[ジェンキンスおじさん](https://jenkins.io/)などでも良いでしょう）
- [重い腰](https://www.weblio.jp/content/%E9%87%8D%E3%81%84%E8%85%B0)を上げる勇気

**（今回はCircieCIを使った方法を書きます）**

## テスト導入

今回は自分の作成したPluginにUnit testを導入したので、自分のPluginをベースにUnit testの導入フローを紹介したいと思います。

以下のような、Vimで天気をみるためのPluginのためのUnit testです。

> https://github.com/kazukazuinaina/Weather.vim

![Untitled.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278618/c691342c-1b37-36a7-3f5d-fab8fe3d30e4.gif)

### ディレクトリ構造

```
.
├── .circleci
│   ├── .vimrc
│   └── config.yml
├── .github
│   └── ISSUE_TEMPLATE
│       ├── ISSUE_TEMPLATE.md
│       └── bug_report.md
├── .gitignore
├── LICENCE
├── README.md
├── Weather.gif
├── autoload
│   ├── Weather
│   │   ├── city
│   │   │   ├── Hiroshima.vim
│   │   │   ├── Hukuoka.vim
│   │   │   ├── Kyoto.vim
│   │   │   ├── Nagoya.vim
│   │   │   ├── Osaka.vim
│   │   │   ├── Sapporo.vim
│   │   │   ├── Sendai.vim
│   │   │   ├── Shiga.vim
│   │   │   └── Tokyo.vim
│   │   ├── returncity.vim
│   │   ├── test
│   │   │   ├── test_popup.vim
│   │   │   └── test_status.vim
│   │   └── test.vim
│   └── Weather.vim
├── doc
│   └── Weather.txt
└── plugin
    └── Weather.vim
```

テストコードに関するスクリプトは、`test` ディレクトリ内にある`test_status.vim`と
`Weather`ディレクトリ内にある`test.vim`です。

簡単にこのスクリプトについて紹介しておきます。

- test.vim

これは、testディレクトリに置かれた単体テスト用のスクリプト群をまとめて実行するためのファイルです。

- test_status.vim

これは、apiを叩いた際にhttp statusが正しく返って来ているかをtestするためのスクリプトファイルです。

### testコードカキコ

じゃあ実際にtestを書いていきましょう。

#### test_status.vim

```vim
" test_status.vim

let s:dir = expand('<sfile>:h:h') . '/city'
let s:city_data = map(glob(expand(s:dir) . '/*.vim', 1, 1), 'fnamemodify(v:val, ":t:r")')

function! Weather#test#test_status#run()

for i in s:city_data
  let l:id = Weather#returncity#return(i)
  let l:res = webapi#http#get('http://weather.livedoor.com/forecast/webservice/json/v1?city='.id)
  echo i . " status:" .  l:res.status
  " l:res.statusは文字列だから10進数に変換
  call assert_equal(200, str2nr(l:res.status, 10))
endfor

endfunction
```

test_status.vimは、cityディレクトリにある都市のファイル名を抜き出し、その都市に対応するidを指定してapiをgetで叩き、それを`assert_equal`で評価するためのスクリプトになります。
今回のケースだと、`call assert_equal(200, str2nr(l:res.status, 10))`の部分がそれに当たり、これは`webapi#http#get('http://weather.livedoor.com/forecast/webservice/json/v1?city='.id)`で返ってきたhttpレスポンスが`200`なら[ヨシ！](https://dic.nicovideo.jp/a/%E7%8F%BE%E5%A0%B4%E7%8C%AB)、そうでないならダメと言う感じになります。

`assert_equal`についてはこちらに記事がが参考になると思います!

> https://thinca.hatenablog.com/entry/20151203/1449076256

#### test.vim

```vim

" test.vim

scriptencoding utf-8

let s:save_cpo = &cpo
set cpo&vim

function! Weather#test#run()
  " v:errorsを初期化する
  let v:errors = []
  call Weather#test#test_status#run()
  if len(v:errors) >= 1
    echo v:errors
    " error exit
    cquit!
  endif
  echo 'test success'
  qall!
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo
```
test.vimは、test_status.vimで実行したassert_equalの結果を返し、Vimを終了させるようなスクリプトになっています。

もし、assert_equalを実行し、問題なければ`:qa!`でVimを終了、問題があれば`:cq!`でVimを終了させるスクリプトになっています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">補足すると、CLIのコマンドの終了ステータスがCIで拾われるので、Vimでもcq!などで正常以外のステータスを送ることでCIの結果を失敗にしたりできます！ってのを使ってます😌 <a href="https://t.co/4D3YzM7Otx">https://t.co/4D3YzM7Otx</a></p>&mdash; かみけん (@_kamykn_) <a href="https://twitter.com/_kamykn_/status/1201534556286803969?ref_src=twsrc%5Etfw">December 2, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## circle ciの設定

testが書けたらあとはcircle ciに丸投げしちゃいましょう！！

```yaml
version: 2
jobs:
  build:
    docker:
      - image: centos:7

    working_directory: ~/project

    steps:
      - checkout

      - run:
          name: ls
          command: ls -la

      - run:
          name: yum
          command: yum update -y && yum install make gcc ncurses-devel ssh git -y

      - run:
          name: Build latest Vim
          command: git clone https://github.com/vim/vim.git && (cd vim && make && make install)

      - run:
          name: Vim version
          command: vim --version

      - run:
          name: Install webapi-vim
          command: git clone https://github.com/mattn/webapi-vim

      - run:
          name: write .vimrc
          command: mv ~/project/.circleci/.vimrc ~/.vimrc

      - run:
          name: check path
          command: ls -la && pwd

      - run:
          name: Unit test
          command: vim +":call Weather#test#run()"
```

これで、circle ciくんが自動でVimを起動し、テストを動かし、Vimを終了するところまで自動で回してくれます！
自動化って素晴らしい！！

やっていることとしては、

1. yumをupdate
2. 必要なpackage群をインストール
3. Vimをビルド
4. pluginをインストール
5. vimrcをホームディレクトリに配置（もっといい方法あると思います）
6. Vimを起動し、上記のtest用の関数を呼び出す


**これがtest用のvimrcです**

```vim
scriptencoding utf-8
set encoding=utf-8
syntax enable
set runtimepath+=~/project
set runtimepath+=~/project/webapi-vim
```

## スクリーンショット

### テストが成功した図

<img width="446" alt="スクリーンショット 2019-11-22 16.45.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278618/1cca41b3-9401-ad11-2722-304e7923c758.png">

### テストが失敗して絶望している図

<img width="751" alt="スクリーンショット 2019-11-26 9.34.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278618/97c9bf5d-8389-0cfd-2c3c-7c969cc7b2b0.png">


## 最後に

どうでしたか？
これでより豊かなVim lifeが送れることでしょう！
**Happy Vimming!!**

次の@aiya000さんの記事にも期待です!!



## 参考文献

> https://thinca.hatenablog.com/entry/20151203/1449076256
> https://github.com/kamykn/spelunker.vim