アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

【Mac】スマートフォンアプリ作りのための環境構築(Vue + Cordova + Jest)

はじめに

エンジニアの平山です。
本記事ではvueのデフォルトアプリをandroidとiosのスマートフォンで動かし、jestを使ってデフォルトアプリの単体テストを実施する方法をまとめています。
なお、単体テストの書き方は割愛します。

必要な機器

・Macのpc
・androidかiosのスマートフォン
・pcとスマートフォンをつなぐケーブル

PCへのインストールが必要なもの。(カッコ内は今回使ったバージョン)

・cordova(9.0.0)
・node(10.15.3)
・npm(6.9.0)※yarnでも良い。
・vue(3.7.0)

iosのスマートフォン端末にアプリをインストールする場合のみ必要なもの。

・apple id
・ios-deploy(1.9.4)
・xcode(10.1)

①Cordovaでスマートフォンアプリ用の環境をディレクトリ内に作成する。

cordova create [任意のディレクトリ名]

②Vue cliを使って①で作ったディレクトリにVueの環境を組み込む。

①で作成したディレクトリ内で構築する。

cd [①で作成したディレクトリ名]
vue create .
Vue CLI v3.7.0
? Generate project in current directory? (Y/n) y

マニュアルで環境構築。

Vue CLI v3.7.0
? Please pick a preset: 
  n (babel, eslint, unit-mocha) 
  default (babel, eslint)
❯ Manually select features 

任意の項目を選択してインストール。

Vue CLI v3.7.0
? Please pick a preset: Manually select features
? Check the features needed for your project: 
 ◉ Babel
 ◯ TypeScript
 ◯ Progressive Web App (PWA) Support
 ◯ Router
 ◉ Vuex
 ◉ CSS Pre-processors
 ◉ Linter / Formatter
❯◉ Unit Testing
 ◯ E2E Testing

単体テストはjestを選択。

Vue CLI v3.7.0
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Vuex, CSS Pre-processors, Linter, Unit
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Prettier
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Pick a unit testing solution: 
  Mocha + Chai 
❯ Jest

jestを含めた色々なツールの設定は、それぞれ別の設定ファイル内で定義させる。

Vue CLI v3.7.0
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Vuex, CSS Pre-processors, Linter, Unit
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Prettier
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? (Use arrow keys)
❯ In dedicated config files 
  In package.json 

③インストールするスマートフォン端末のOSに合わせてディレクトリを作成。今回はandroidとiosにインストールするのでそれぞれ作成。

cordova platforms add android ios

④ビルドの設定ファイルを作成する。

vueファイルのビルド結果の出力先(デフォルトはdist)をcordovaのビルド結果出力先(www)に変更する。
また、公式ガイドに書かれているように、cordovaアプリ用にpublicPathの設定を変更する。

これらの設定のためにvueのオプション設定ファイル(vue.config.js)を①で作ったディレクトリ直下に作成する。

設定ファイル内容は以下の通り。

module.exports = {
  publicPath:'./',
  outputDir:'www'
}

⑤アプリの識別子を変更する。

①で作成したディレクトリ直下にある「config.xml」のwidgetタグのid要素を変更する。
このidはアプリの識別子で重複不可のものであり、重複している場合はandroidアプリだとスマートフォン端末へのインストールはできるが、iosアプリの場合はインストールができなくなる。
idの付け方についてはネットに色々と記事があるので、それらを参照。
以下config.xmlの例。

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.asial.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>HelloCordova</name>
    <description>
        A sample Apache Cordova application that responds to the deviceready event.
    </description>
    <author email="dev@cordova.apache.org" href="http://cordova.io">
        Apache Cordova Team
    </author>
    <content src="index.html" />
    <plugin name="cordova-plugin-whitelist" spec="1" />
    <access origin="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />
    <platform name="android">
        <allow-intent href="market:*" />
    </platform>
    <platform name="ios">
        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
    </platform>
</widget>

⑥スマートフォンへのインストール準備。

pcとスマートフォン端末をケーブルで接続しておく。(ケーブル次第でpcがスマートフォンを認識しない場合が多々あるので、その場合は色々調べて解消する)

アプリのビルドを行う。

npm run build

インストール準備。

cordova prepare

⑦androidのスマートフォンへインストールする場合。

cordova run android

スマートフォン端末に以下の画面が表示されればインストール成功。

f:id:tatatakaka:20190513193236p:plain
アプリ画面

⑧iosのスマートフォンへインストールする場合。

アプリとapple idの紐付け

xcodeでiosのプロジェクトファイルを開く。
ファイル名のHelloCordovaはデフォルトのアプリ名。
アプリ名を変更した場合には「アプリ名.xcodeproj」を開く。

open platforms/ios/HelloCordova.xcodeproj

xcodeの左側のアイコン群の一番左を選択したときに表示されるプロジェクトを選択。
Signing → Team欄からアプリと紐づけるapple idを選択する。
このとき、「Automatically manages signing」のチェックを入れておくこと。
Team欄にapple idが表示されない場合には「add an account」を選択してapple idを追加する。

f:id:tatatakaka:20190515125900p:plain

以下のようにStatus欄にエラーが発生した場合は、アプリの識別子(Bundle Identifier)が重複している可能性が高いので、⑤の手順から再実施して別の識別子に変更する。

f:id:tatatakaka:20190515161917p:plain
エラー発生画面

端末へインストール。
cordova run ios

androidの場合と同じように、アプリ画面が表示されれば成功。

以下のようなエラーが表示された場合は、スマートフォンに信頼されていないapple idによってアプリ起動が拒否されている可能性があるので、apple idを信頼するようにスマートフォンの設定を行う。

error: process launch failed: Security
(lldb)     safequit

Application has not been launched

ios-deploy: Command failed with exit code 1

⑨単体テストの実施

npm run test:unit

テストファイル(tests/unit配下の.spec.jsファイル)が実行され、結果が表示される。

 PASS  tests/unit/example.spec.js
  HelloWorld.vue
    ✓ renders props.msg when passed (18ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.574s
Ran all test suites.

⑩単体テストのcoverage設定(おまけ)

coverageを設定することで、単体テストの実施状況が表で確認できる。
f:id:tatatakaka:20190514171201p:plain

以下の赤ハイライトはテスト未実施箇所
f:id:tatatakaka:20190514171443p:plain

①で作ったディレクトリ直下にあるjest設定ファイル(jest.config.js)に以下を追記する。
collectCoverageはcoverageの有効・無効設定で、collectCoverageFromはcoverageの集計対象ファイル。
集計対象のファイルはsrcディレクトリ配下に存在する全てのjsファイルとvueファイルとしている。

・"collectCoverage": true
・"collectCoverageFrom": ["src/**/*.{js,vue}"] 

※②の最後に「In dedicated config files 」を選択した場合には、設定ファイル(jest.config.js)が作成されるが、「In package.json 」を選択した場合には設定ファイルは作成されずに、package.json内にjestの設定が定義される。

ファイル内容は以下の通り。追記した下2行以外はデフォルトで設定されているもの。

module.exports = {
  moduleFileExtensions: ["js", "jsx", "json", "vue"],
  transform: {
    "^.+\\.vue$": "vue-jest",
    ".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$":
      "jest-transform-stub",
    "^.+\\.jsx?$": "babel-jest"
  },
  transformIgnorePatterns: ["/node_modules/"],
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1"
  },
  snapshotSerializers: ["jest-serializer-vue"],
  testMatch: [
    "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"
  ],
  testURL: "http://localhost/",
  watchPlugins: [
    "jest-watch-typeahead/filename",
    "jest-watch-typeahead/testname"
  ],
  "collectCoverage": true,
  "collectCoverageFrom": ["src/**/*.{js,vue}"]
};

設定後に再度テストを実施すると、テスト結果にcoverageが加わっていることがわかる。

> app@0.1.0 test:unit /Users/takashi/work/blog/vue/app
> vue-cli-service test:unit

 PASS  tests/unit/example.spec.js
  HelloWorld.vue
    ✓ renders props.msg when passed (18ms)

-----------------|----------|----------|----------|----------|-------------------|
File             |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------------|----------|----------|----------|----------|-------------------|
All files        |     9.09 |      100 |        0 |     9.09 |                   |
 src             |        0 |      100 |        0 |        0 |                   |
  App.vue        |        0 |      100 |      100 |        0 |                 9 |
  main.js        |        0 |      100 |        0 |        0 |       1,2,3,5,7,9 |
  store.js       |        0 |      100 |      100 |        0 |             1,2,4 |
 src/components  |      100 |      100 |      100 |      100 |                   |
  HelloWorld.vue |      100 |      100 |      100 |      100 |                   |
-----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.138s
Ran all test suites.

coverage/lcov-report配下にあるindex.htmlをブラウザで開くと、⑩冒頭の添付画像のように表やコードハイライトでのテスト実施状況が確認できる。

以上