Vue.jsを1年触ったのでオススメパッケージを紹介

はじめに

こんにちは株式会社SCOUTERでエンジニアをしているhirokinishizawaです。 業務では、SARDINEという人材紹介会社向けの業務管理システムを開発しています。

前回プログラミングを初めてから1年が経ったので「1年間のプログラミングを通して得た3つの教訓」というブログを書きました。

1年間実務だけやってきて自分が得た教訓をプログラミングを始めたばかりの方や、これから始めてみたいと思っている方に共有できればという思いで書いた記事になっています。

techblog.scouter.co.jp

今回は1年間Vue.jsを触ってきて自分がおすすめだなと思ったパッケージについて紹介していきたいと思います。

紹介するパッケージ一覧

  • Vue.Draggable

github.com

  • vue-social-sharing

github.com

  • vue-carousel

github.com

  • vue-star

github.com

こちらの4つについて話していこうかと思います。

導入方法はすべて定番のyarn add [package-name]npm install [package-name]で導入できます。

Vue.Draggable

以前Vue.Draggableを業務で扱う機会があったのでその時にブログを書きました。ドラッグ&ドロップでリストを並び替えすることをお手軽に実装できるパッケージになっています。

techblog.scouter.co.jp

業務で扱った時は横の移動だけではなく縦の移動もあるため縦の順番もデータとして持っておく必要がありデータベースの設計を慎重に行わなければいけませんでした。Vue.Draggableでドラッグ&ドロップをした時にどのようなデータを受け取ることができるのか把握してから設計を行えば横移動も縦移動も問題なく動かすことが出来るかと思います。

vue-social-sharing

vue-social-sharingとfont-awesomeを導入することでソーシャルボタンを簡単に設置できます。

<template>
  <social-sharing
    url="https://twitter.com/hiroki_2438"
    title=" タイトル記載"
    description="説明文を記載してください"
    hashtags="Vue.js"
    twitter-user="hiroki_2438"
    inline-template
  >
    <div>
      <network network="facebook">
        <i class="fa fa-facebook-official"></i>
      </network>
      <network network="twitter">
        <i class="fa fa-twitter"></i>
      </network>
    </div>
  </social-sharing>
</template>

<script>
import SocialSharing from "vue-social-sharing";

export default {
  components: {
    SocialSharing
  }
}
</script>

f:id:hiroki-nishizawa:20190419033835p:plain

networkタグのnetwork属性を変更することよりソーシャルメディアを変更をすることができます。

今回facebooktwitterだけしか実装していませんが他にも

など。上記以外にもありますが、メインどころはこんなところでしょうか。

次にSocialSharingコンポーネントにpropsでtitleなどを渡していますが実際にコンポーネントで何を受け取っているのか調べてみました。

渡しているもの 説明 補足
url シェアしたいurlになります 何も指定しなければでソーシャルボタンが設置されているurlが指定されます
title タイトルになります 利用可能なのはemail,twitterになります
description 説明文です 利用可能なのはemail,LINE,SMS
quote facebook用のquoteになります facebookのみ使用可能
hashtags ハッシュタグです facebooktwitterのみ使用可能でfacebookは1つ目のみ使われます
twitter-user twitterのアカウントIDをいれてください twitterのみ使用可能

自分がよく使うのは基本的にfacebooktwitterなので十分だったのと、導入するだけで簡単に実装できるし以前aタグでやっていたときより簡潔で読みやすくなるのでおすすめです。

以前aタグでやっていた時

#facebook
<a class="fb btn" :href="`http://www.facebook.com/share.php?url=urlを記載`"
             onclick="window.open(this.href, 'FBwindow', 'width=650, height=450, menubar=no, toolbar=no, scrollbars=yes'); return false;">
    <img src="facebookのicon画像">
</a>

#twitter
<a :href="`https://twitter.com/intent/tweet?text=タイトル&url=urlを記載`" target="_blank">
    <img src="twitterのicon画像">
</a>

vue-carousel

vue-carouselはカルーセルを簡単に実装できレスポンシブにも対応していてとても使いやすいUIコンポーネントです。

<template>
  <Carousel
    :per-page="1"
    loop=true
    :navigation-enabled="true"
    navigation-prev-label="〈"
    navigation-next-label="〉"
    touchDrag=true
    :speed="1000"
  >
    <Slide v-for="(slide, key) in slides" :key="key">
      <span>{{slide}}</span>
    </Slide>
  </Carousel>
</template>

<script>
import { Carousel, Slide } from "vue-carousel";

export default {
  components: {
    Carousel,
    Slide
  },
  data() {
    return {
      slides: ["slide1", "slide2", "slide3", "slide4", "slide5", "slide6"]
    };
  }
};
</script>


f:id:hiroki-nishizawa:20190419053208p:plain

上記のCarouselコンポーネントにpropsで渡してしているのは

  • per-page
    • 各ページに表示されるスライドの最大数
  • loop
    • 最後に到達したときにカルーセルをループさせるためのもの
  • navigation-enabled
    • navigation-prev-labelとnavigation-next-labelを使用できるようにするもの
  • navigation-prev-label
    • ナビゲーションの前に戻るボタンのテキスト
  • navigation-next-label
    • ナビゲーションの次に進むボタンのテキスト
  • touchDrag
    • タッチドラッグを出来るようにするもの
  • speed
    • スライドする時のスピードを調節するもの

それぞれこのような設定になっています。

今回使っているものの他に autoplay autoplayDirection autoplayHoverPause autoplayTimeoutのように時間で自動的にスライドを出来るようにするための設定だったり、スマホ用にスワイプで切り替わるようにするための設定だったりとカルーセルの実装をするために必要なものを簡単に実装できるのがとてもおすすめできるポイントだなと思います。

vue-star

twitterのいいねみたいなアニメーションを簡単に実装できるUIコンポーネントになっています。

<template>
  <vue-star color="#F05654">
    <i slot="icon" class="fa fa-heart"></i>
  </vue-star>
</template>

<script>
import VueStar from "vue-star";
export default {
  components: {
    VueStar
  }
};
</script>

f:id:hiroki-nishizawa:20190419195526g:plain

他にもcolorのカラーコードを変えて色を変更したり、animateという属性を使ってanimationのクラスを入れることによって違う動きができたりします。 自分は使っているのですがanimate.cssを入れることでより簡単にanimationの幅を広げることができます。

<template>
  <div class="vue-stars">
    <vue-star animate="animated wobble" color="#0000ff">
      <i slot="icon" class="fa fa-heart"></i>
    </vue-star>
  </div>
</template>

f:id:hiroki-nishizawa:20190419195233g:plain 自分がプログラミングを初めて半年とかの時に業務でお気に入り機能を実装することになったのですが、当時デザイナーの意向でやりたいという話になりcssで出来るっぽいってとこまでわかったのですが工数的にやらないという意思決定になりました。そのときにこれを知っていたら実装できたのではないか。と思っております。。。

このようなアニメーションが必要な時にほんと簡単に実装できるのでとてもおすすめです。

まとめ

今回は数あるパッケージの中から自分が実際に使い個人的にいいなと思ったパッケージを紹介しました。もちろん他にもたくさんのパッケージがあります。今回紹介したものと似たようなパッケージ、自分が知らない便利なパッケージ、絶対業務では扱わないけど面白そうなパッケージ。たくさんのVue.jsのパッケージがあり、パッケージをまとめてくれている方も多いので試しに使ってみてはいかがでしょうか。

自分の場合はドラッグ&ドロップをしたいだったり、カルーセルを実装したいだったりしたときにVue.jsを冒頭に付けて検索をした結果触ることになったり、まとめてくれているサイトで面白そうってだけで触ったりしました。

最後に

弊社では事業・サービスが成長していくにあたってメンバーを増やしていきたいと思っています。

興味のある方は下記からご応募いただくか、@hirokiにご連絡ください!!

www.wantedly.com

www.wantedly.com

www.wantedly.com

E2Eテストを導入して自分を救う (TestCafe編)

こんにちは、自意識過剰な正義のヒーローでお馴染みの株式会社SCOUTERの石岡 将明( @masaakikunsan )です。

先月のブログで、フロントにおける unit test についてブログを書いている人がいたので、今回は E2E テストについて書いていこうかなと思います。

E2E テストとは

E2E (End to End)テストとは Web ブラウザを通して一通りの処理をテストすることです。

今回のデモで作成するものを例として挙げます。

  1. ユーザー登録画面を表示
  2. 名前を入力
  3. bioを入力
  4. 送信ボタンをクリック
  5. ユーザー一覧ページに遷移
  6. ユーザー一覧に追加したユーザーが追加されている

なぜ E2E テストが必要なのか

みなさんはリリース前にテストをして最終確認をすると思います。 その際に全ページの UI の確認と挙動が想定どおりになるかどうかのテストを毎回していたらかなり時間の無駄です。

そこで、 E2E テストをすることで手を動かさずに全ページの UI と挙動のテストをすることができ、時間もコストもかなり下がり生産性があがるでしょう。

また、このような経験はありませんか?

実装した箇所以外のテストが漏れていてリリースしたらバグがあった。みたいな E2E テストを実行すれば基本的にこれは防げます。

全画面のテストを自動でやることで、影響範囲の漏れもなくなり品質担保にも繋がります。

つまり、 E2E テストはコストを下げる為だけでなく自分を救うことにも繋がります。

Nuxt.js に E2E テストを導入しよう

json-server を追加する

今回はテストに使う mock サーバーとして json-server を利用します。

さっそく導入していきます。

$ yarn add -D json-server

次に json を追加していきます。

db/db.json を追加して以下を記載します。

{
  "user": [
    {
      "id": 1,
      "name": "masaakikunsan",
      "bio": "自意識過剰な正義のヒーロー"
    }
  ]
}

最後に json-server を実行するために package.json に scripts を追加しましょう。

"server": "json-server --watch db/db.json --port 4000"

これで、 $ yarn server を実行し、 localhost:4000/user にアクセスすると db.json に記載したデータが表示されるでしょう。

f:id:masaakikunsan:20190416172936p:plain

次に、ユーザー一覧ページとユーザー作成ページを作っていきます。

ユーザー一覧とユーザー作成ページ

下記のようなページを作成しました。

f:id:masaakikunsan:20190416185539p:plain

f:id:masaakikunsan:20190416185657p:plain

コードはURLを載せて置くのでみてください。 github.com

TestCafe の導入

E2E テストですと、Puppeteer がかなり主流になってきていますが今回は TestCafe を使います。 理由としては、ブラウザを指定することでそのブラウザでのテストが可能だからです。

あと Puppeteer よりドキュメントが読みやすくて個人的に好きというのもありますw

まぁ他にも理由はあるので自分で調べてみてください。

devexpress.github.io

それではさっそく導入していきましょう。

$ yarn add -D testcafe

次に TestCafe を実行するために package.json に scripts を追加しましょう。

"e2e": "testcafe chrome test/e2e/*.js --speed 0.5"

speed を指定することで E2E の実行スピードを指定できます。

それでは、テストコードを書いていきましょう。

test/e2e/test.js を作成し下記コードを記載していきます。

import { Selector } from 'testcafe'

fixture('ユーザー追加').page('http://localhost:3000/create')

const customDataAttribute = name => `[data-test='${name}']`

test('必要項目を入力後、送信して遷移先を確認', async t => {
  const userName = await Selector(customDataAttribute('name'))
  const userBio = await Selector(customDataAttribute('bio'))
  const submitButton = await Selector(customDataAttribute('submit-button'))

  await t
    .setNativeDialogHandler(() => true)
    .typeText(userName, 'test')
    .typeText(userBio, 'bioだよ〜')
    .click(submitButton)

  const tableUserName = await Selector(customDataAttribute('user-name')).nth(-1)
    .innerText

  await t.expect(tableUserName).eql('test')
})

Selector は、テスト内の要素を識別するものです。 ここでは、data属性を定義しそれを元に要素を識別しテストを実装していきます。

それでは実行していきましょう。

テストで正しく挙動していることが確認できました。

scripts の testcafe の横の chromesafari にすれば safari が起動しますし、 firefox にしたら firefox が起動します。 これでブラウザテストの時間もかなり削減できます。

まとめ

E2E テストでの実装例を紹介していきました。いかがだったでしょうか?

E2E を実際のプロダクトにいきなり導入するのはコスト的にも難しいでしょう。 ただ、E2Eテストを絶対にバグっててはいけないところだけ導入するだけでもかなりテストの工数も下がり良いでしょう。

ずっと、言っていますが自分を助けることになります。

ちなみに、僕はフリーランスのときに実装した箇所がクレカ決済の箇所に影響が及んでいてクレカ決済が死ぬっていうバグを起こして以来テストはまじ大事自分を助けるって気持ちになっています。

現在、back check チームでは、エンジニアの募集をしております。 僕と一緒にフロントエンドを朝まで語り尽くしたいフロントエンドエンジニアやレベルの高いフロントエンドチームと最高のプロダクトを作って行きたいサーバーサイドエンジニアは是非ご応募お願いいたいします!

www.wantedly.com

www.wantedly.com

[Nuxt.js] middleware で外部リソース取得処理を共通化

こんにちは!

SCOUTER社でエンジニアをしている匠平@SCOUTER(@show60)です。

開発を進めるにつれ page component も増えていきますが、複数の page component で同じ外部リソースが必要なことがあります。

同じ処理を行っている場合は処理をまとめちゃいたいですね。

そんなときのために、 middleware を使った処理の共通化についてお話しします。

fetch で外部リソースからデータを取得する

page component 内で使われるデータを外部リソースから取得したいとき、 Nuxt.js では page component 内で fetch を使うことでこの処理を行うことができます。

また、この fetch は、取得したデータを Vuex store に格納することを前提としています。

これにより、その page component 配下にある component でも Vuex store に格納されたデータを扱うことができるようになります。

下図内で、 ...fetches data before rendering page と記述してあるとおり、 page component で記述した fetch はその page component が読み込まれる度、そのレンダリングの前に発動します。

複数の page で使われている処理を共通化

複数の page compoent において、fetch で同じデータを取得しているとき共通化を考えます。 どこからでも呼び出せる処理の実装を考えたときに、手段として mixins と middleware があります。 fetch は page component でしか動作しないため、 mixins と middleware 上では、他の方法を考える必要があります。

mixins

下記の mixins を page component で呼び出すと page component 内で sample という data を使用することができます。 created のタイミングでデータ取得処理を発動させ、それを data に挿入しています。

// mixins/sampleMixins.js
export default {
  data: () => {
    return {
      sample: []
    }
  },
  created() {
    this.getSamples()
  },
  methods: {
    async getSamples() {
      this.sample = await this.$axios.$get('/api/sample')
    }
  }
}

ただし、 page component 内ですでに sample という名前で data が定義されている場合、そちらが優先されてしまいますので注意してください。

参考: Option Merging

外部リソースからデータを取得する処理自体は共通化することができましたが、この page component 配下で使用するためには props でデータを渡していく必要があります。

middleware

middleware 内では下記のようにすることで、外部リソースへ接続し、 Vuex store に値を格納することができました。

// middleware/index.js
export default ({ store }) => {
  // すでに store にデータが格納されている場合は再取得を行わない
  if (store.state.sample) {
    return
  }
  store.dispatch('getSamples')
}

middleware はページ遷移する度に呼び出されるため、必要なデータがすでに Vuex store に存在しているのであれば再取得の処理を行わないよう記述してあげるといいですね。

middleware で共通化する上で注意すること

middleware を介して Vuex store にデータを格納できたのですが、適さないデータというのもあります。

Nuxt.js の SPA ではページ遷移しても Vuex store のデータをそのまま保持しています。そのため、利用中に書き換わる頻度が高いデータにおいては正しいデータを表示してくれない事態が起きます。

例えば頻繁な更新がないと思われるユーザー情報や、部署名と部署 ID の対応表といったようなデータの取得に使用するほうがいいでしょう。 ユーザー認証されているかを全 page に渡って確認したい場合もこのように処理します。

最後に

SCOUTER は先日、新サービス back check を正式リリースいたしました!

これからたくさんのお客様に最高のプロダクトを届けていくため、一緒に開発を進めていくエンジニアの募集をしております。

ぜひご興味を持ったかたはご連絡ください!

www.wantedly.com

www.wantedly.com

AWS SystemManagerを使って、簡単にサーバー監視やIDS等を導入する

こんにちは kotamat です。 最近は社内セキュリティの向上に取り組んでおり、その一環でAWS System Managerを導入してみたら非常に簡単にいろいろなものがセットアップできたので、そちらの紹介をさせていただきます。

EC2へのソフトウェアインストールの課題感

複数のEC2インスタンスが必要かつ、どのインスタンスにもインストールするソフトウェアがある時、皆さんはどうしているでしょうか? 予めそのサーバーに必要なソフトウェアがわかっている場合はインストールした状態でAMIに固め、それをAutoScalingのAMI設定にしたり、EC2立ち上げ時にそのAMIベースに起動することによってすべてのインスタンスに反映することができるかと思います。

ただ、すでにインスタンスが立ち上がっている場合、それぞれのインスタンスにわざわざsshで入ってインストールしたり、AnsibleやChefを使って一括インストールをしていたかと思いますが、前者はインスタンス数が増えてきたときに対応が難しく、後者に関してはインフラ構成にもよりますが、踏み台の設定が必要だったり、構成管理ツール用に書き換える手間が発生します。

そういった中でAWSのSystemManagerは上記を解決するツールとして有用だったため、今回使用してみることにしました。

SystemManagerって何?

AWS Systems Manager は、Amazon EC2 インスタンス、オンプレミスサーバーと仮想マシンや他の AWS リソースを大規模に設定および管理する機能のコレクションです。Systems Manager には、AWS リソース間で運用データを簡単に一元化し、タスクを自動化できる統一されたインターフェイスが含まれています。Systems Manager はインフラストラクチャで運用上の問題を検出して解決するための時間を短縮します。Systems Manager は、インフラストラクチャのパフォーマンスと設定についての詳細を提供し、リソースとアプリケーションの管理を簡素化することで大規模なインフラストラクチャの運用と管理を簡単にします。

先日の発表で注目されたSessionManagerを始め、たくさんの機能を内包している物となっており、EC2やオンプレサーバーの一括管理に向いている物となっています。

今回はその中でもRun Commandを使ったツールのインストール方法について紹介させてもらいます。

RunCommand

ユーザ任意のターゲットインスタンスに対して、一括でコマンドを実行できるものです。

Dockerのコマンドを一括実行する AWS-RunDockerAction や、AmazonInspectorのエージェントを一括でインストールする AmazonInspector-ManageAWSAgent など便利コマンドももちろんありますが、 AWS-RunShellScript といった、シェルスクリプトを実行してくれるコマンドもあります。 こちらはsudo権限を付与できる任意のコマンドを打つことができるため、今回の背景のような外部ツールの一括インストールには非常に向いています。

導入方法

EC2のロールにIAMポリシーを付与する。

まず、AWS SystemManagerが管理対象としてEC2インスタンスを認識できるようにします。 AmazonLinux系であれば、最初からデーモンプロセスがインストールされている状態で起動しますので、 EC2ロールに対して arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM を付与するだけで大丈夫です。

EC2にタグをつける

SystemManagerのRunCommandでは、インスタンスのタグを指定して一括実行が可能なので、 タグをつけて運用していない場合は予めタグを付けておきましょう。 Env:production などのタグを予め指定しておく形をおすすめします。

RunCommandを設定する。

AWS SystemManager > Run Commandからコマンドの実行を選択します。

f:id:kotamat:20190401075406p:plain

コマンドの中から AWS-RunShellScript を選択します。

f:id:kotamat:20190401075503p:plain

コマンドのパラメータのところでは、任意のシェルスクリプトを記述します。 SaaS型のサーバー監視ツールやIDSでは公式でスクリプトを提供していることが多いと思いますので、そちらを記載します。

f:id:kotamat:20190401075611p:plain

ターゲットは手動での指定かタグでの指定ができます。 手動で指定を行ってもいいのですが、本番環境のみインストールしたいというパターンも多いかと思いますので、 Env:production などのタグを予め指定しておいて、そこにインストールする形をおすすめします。

f:id:kotamat:20190401080150p:plain

出力オプションでは、コマンドの結果の出力先を指定できます。 コマンドの出力を2500文字以上出力したい場合はS3のバケット指定を、CloudWatchLogsで確認できるようにしたい場合は、CloudWatch出力にチェックし、設定を行います。 CloudWatchに関しては、デフォルトで /aws/ssm/AWS-RunShellScript に書き込まれるので、それで問題なければロググループ名を空白にします。

f:id:kotamat:20190401080240p:plain

SNS通知では、コマンド完了等のステータスに関する通知を受け取る設定ができます。

のいずれかのステータスを受信したい場合は設定しましょう。

f:id:kotamat:20190401080635p:plain

Run Commandの実行

あとは実行ボタンを押すだけです。 実行後はコマンド履歴の方に反映されます。

f:id:kotamat:20190401081136p:plain

コマンドの詳細では、実行したインスタンスの一覧が表示されます。

f:id:kotamat:20190401081301p:plain

そして、インスタンスを選択し 出力の表示 を押すと、その実行されたコマンドの標準出力を確認することができます。

f:id:kotamat:20190401081500p:plain

これでコマンドの実行は完了です。

まとめ

今回はSystemManagerの中でもRunCommandに焦点をあてて紹介させてもらいました。 RunCommandを用いることで、単発コマンドを複数のインスタンスに一括で実行することが簡単になるため、 すでに立ち上がっているインスタンスへ監視ツールを導入したいときや、IDSなどのセキュリティツールを導入したいときなどに活用することができるかと思います。

最後に

SCOUTERではエンジニア、デザイナーともに募集しております! 新規事業、絶賛グロース中の事業ともにLaravel, Vue.jsで開発しておりますので、 興味のある方はお声がけください!

体験副業もはじめました!

www.wantedly.com

www.wantedly.com

www.wantedly.com

PHPerKaigi 2019 に参加・登壇してきました。

こんにちはみなさん ゴツメ装備エンジニアこと @niisan-tokyo です。
3/30, 31 でPHPerKaigiに参加・登壇してきましたので、その報告となります。
(3/29の前夜祭は会社の締め会で出れなかったのです。)

PHPerKaigi 2019

PHPerKaigiはPHPer同士の相互交流を主なテーマとし、去年から始まりました。

phperkaigi.jp

このイベントは相互交流に重きをおいているだけあって、トークにおいては必ず質疑の時間がタイムテーブルに盛り込まれていたり、
気軽に聞けるCIやフレームワークの相談会があったり、突発的に喋りたくなった人のためのアンカンファレンススペースがあったりしまして、
様々な楽しみ方ができるようになっています。

また、このイベントの大きな特徴としては運営が非常に親切かつ丁寧というところにあります。
去年、私が「ビールしかないんかぁ」(お酒飲めない人) とつぶやいた直後に烏龍茶がデプロイされたりして、少しの不満も迅速に解消しようという不断の努力を感じます。

なお、LTの時間にビールがデプロイされる予定だったらしいですが、手違いで届いていなかったようです。 これに一番ショックを受けていたのが、当の実行委員長だったというね。

FOOD

私のいつものスタイルとして、カンファレンスで出てきた食事を紹介しています。
今回は朝昼晩と全部あったので、それぞれ載せてみました。

f:id:niikura23:20190401082405j:plain
朝ごはん

朝ごはんはドーナッツですね。
朝から糖分を補給し、頭の回転を良くしようという考えではないだろうかと推測したりしなかったり。

f:id:niikura23:20190401083100j:plain
ランチ

ランチにデプロイされたお弁当ですね。
メルカリさんのランチセッションを聞く前に食べ尽くしてしまいました。

f:id:niikura23:20190401083406j:plain
懇親会ディナー

懇親会で出てきたディナーです。
ちゃんと麦茶もあったりしたので、満足できました。

トーク

たくさんのトークがあったのですが、私が登壇で頭が一杯になってたりするので、印象に残っているのだけ紹介します。

speakerdeck.com

以前から講演をしてもらったり、講演を聞いたりしている郡山先生ですが、とにかく見ている世界が違うのかと思うほどの内容で、これを聞けただけでも参加した意味があったなって思ったほどです。
とりあえず、無心になって資料を見るなり公演を聴くなりして、「REST」ってなんなのかって言うことを、見つめ直すべきだなって思います。

speakerdeck.com

LTのネタで川島さんが話した内容ですが、これ、レギュラートークで聞きたかったなぁって思います。 クラスの依存関係を可視化できる、できたてのツールでして、私も後で使ってみようかなって思います。

登壇

今回は幸運にもCFP通ったので、私も登壇してきました。

f:id:niikura23:20190401085442p:plain
登壇時のワタシ

緊張しいたわけではないですが、下むいちゃってますね。 これは反省点ですな。。。

発表資料はこちらです。

speakerdeck.com

内容は見てもらえばわかると思いますが、言及されていない内容に非同期のときのAuthの挙動の説明がないので、補足しておきます。

  1. 一郎くんがログイン => Auth::user() == 一郎くんになるがクリーナーによってリセット
  2. 次郎くんがログイン => Auth::user() == 次郎くんになるがクリーナーによってリセット
  3. 一郎くんが自身の名前を参照 => Auth::user() == 一郎くんになり、viewの描画が始まる前に5秒待つ
  4. 次郎くんが自身の名前を参照 => Auth::user() == 次郎くんになり、viewの描画が始まる前に5秒待つ
  5. 一郎くんが待ち時間後に自身の名前を参照 => Auth::user() == 次郎くんの状態でviewが描画され、クリーナーによってリセット
  6. 次郎くんが待ち時間後に自身の名前を参照 => Auth::user()はクリーナーによりリセットされており、ログアウト状態になった

という、面倒な経緯でした。

まとめ

というわけで、PHPerKaigi 2019 に参加してきたレポートでした。
来年は弊社のメンバーがもっと参加するといいな!

さいごに

現在、株式会社SCOUTERでは、エンジニア、デザイナーの募集をしております。

興味のある方は、是非下記からご応募お願い致します!

www.wantedly.com

www.wantedly.com

www.wantedly.com

Vueのコンポーネント単体テストを始めてみよう

こんにちは!
株式会社SCOUTER開発部フロントエンドエンジニアの佐藤(@r_sato1201)です

最近、社内でLaravelのテストについての会話が活発に交わされるようになりました。 その会話を聞いていて、Vueのテストはないのだろうか?と興味を持ったのでテストの導入方法と、簡単なテストを動かしてみたことについて書いてみました。

なぜテストをするのか?

Vueの公式ドキュメントにはテストの利点についてこう記述してあります。

コンポーネント単体テストにはたくさんの利点があります:

コンポーネントがどう動作すべきかのドキュメントを提供します
・過度な手動テストの時間を節約します
・新しい機能におけるバグを減らします
・設計を改良します
リファクタリングを容易にします

自動テストは大規模な開発チームが複雑なコードベースを保つことを可能にします。

テストを書くことで、コンポーネントがどういう振る舞いをするべきかや、 コンポーネントの設計に問題があることに気づけるメリットがあるようです。

テストの導入方法

Vueにはvue-test-utilsというVueコンポーネント単体テストのための公式ライブラリが用意されています。上記に加え、テストフレームワークJestを使うことで簡単にテストを導入することが出来ます。

新しくプロジェクトを作成する際は vue-clivue createコマンドを用いればvue-test-utilsJestを簡単にインストールすることができます。

f:id:ryonnsui1201:20190327034808p:plain

Unit Testingを選択

f:id:ryonnsui1201:20190327034823p:plain

Jestを選択

既存のプロジェクトに導入する際は、devDependencies にvue-test-utils、Jestをインストールしましょう。

yarn add -D @vue/test-utils babel-jest jest vue-jest

※babelを使っていない場合、babel-jestは不要です。

Jestカバレッジも簡単に取ることが出来ます。
package.jsoncollectCoverageオプションを加えることでカバレッジを取ることができるようになり、collectCoverageFromオプションにカバレッジ収集対象のファイルを配列で定義することができます。

"collectCoverage": true,
"collectCoverageFrom": ["**/*.{js,vue}", "!**/node_modules/**"],

f:id:ryonnsui1201:20190329102122p:plain

更に詳しいオプションの詳細については、 Jest configuration documentationも参考にしてみてください。

テストを実践してみよう

テストの書き方を掴むために、propsによる描画のテストを行ってみます。

以下のようなpropsを持つ簡単なコンポーネントを作成します。

RemoveUser.vue

<template>
  <div class="User">
    <span>{{name}}</span>
    <template v-if="isAdmin">
      <button @click="removeUser()">削除</button>
    </template>
  </div>
</template>

<script>
  export default {
    props: {
      id: Number,
      name: String,
      isAdmin: Boolean
    },
    methods: {
      removeUser() {
        this.$emit('removeUser', {id: this.id})
      }
    }
  }
</script>

それでは、実際にテストを行ってみましょう。
プロジェクトのtests/unit内にテストファイルを作成します。
テストは.spec.js.test.jsというファイルを作成すれば自動で検知してくれます。

yarn test:unitでテストを実行します。

また、--watchを指定することで、テストコードに変更を検知して、変更があるたびにテスト実行することができます。

RemoveUser.spec.js

import {mount} from '@vue/test-utils'
import RemoveUser from '@/components/RemoveUser.vue'

describe("user", () => {
  const adminPropsData = {
    id: 1,
    name: 'ユーザー1',
    isAdmin: true,
  }
  const userPropsData = {
    id: 1,
    name: 'ユーザー1',
    isAdmin: false,
  }
  test('emitされるか', () => {
    const wrapper = mount(RemoveUser, {
      propsData: adminPropsData
    })
    wrapper.find('button').trigger('click')
    // イベント発火してるかどうか
    expect(wrapper.emitted('removeUser')).toBeTruthy()
    // emit時にidが渡されてくるかどうか
    expect(wrapper.emitted('removeUser')[0]).toEqual([{id: 1}])
  })
  test('nameが正しく表示されているか', () => {
    const wrapper = mount(RemoveUser, {
      propsData: adminPropsData
    })
    expect(wrapper.find('span').text()).toBe(adminPropsData.name)
  })
  test('isAdminの値によって削除ボタンが表示されてるか', () => {
    const adminWrapper = mount(RemoveUser, {
      propsData: adminPropsData
    })
    const userWrapper = mount(RemoveUser, {
      propsData: userPropsData
    })
    expect(adminWrapper.find('button').exists()).toBeTruthy()
    expect(userWrapper.find('button').exists()).toBeFalsy()
  })
})

テストコードを箇所に分けて、説明していきます。

test('emitされるか', () => {
    const wrapper = mount(RemoveUser, {
      propsData: adminPropsData
    })
    wrapper.find('button').trigger('click')
    // イベント発火してるかどうか
    expect(wrapper.emitted('removeUser')).toBeTruthy()
    // emit時にidが渡されてくるかどうか
    expect(wrapper.emitted('removeUser')[0]).toEqual([{id: 1}])
  })

ここでは、コンポーネント内でクリックイベントが発火したときに 正しくemitされているかを確認しています。

ここで

・ボタンをクリックしたときにemitが動いているか
・emit時に、propsで渡したidが返ってくるか
が分かります。

f:id:ryonnsui1201:20190328210955p:plain

test('nameが正しく表示されているか', () => {
        const wrapper = mount(RemoveUser, {
            propsData: adminPropsData
        })
        expect(wrapper.find('span').text()).toBe(adminPropsData.name)
    })

ここでは、propsで渡したnameが正しく描画されているかを確認しています。

f:id:ryonnsui1201:20190328211618p:plain

test('isAdminの値によって削除ボタンが表示されてるか', () => {
  const adminWrapper = mount(RemoveUser, {
    propsData: adminPropsData
  })
  const userWrapper = mount(RemoveUser, {
    propsData: userPropsData
  })
  expect(adminWrapper.find('button').exists()).toBeTruthy()
  expect(userWrapper.find('button').exists()).toBeFalsy()
})

ここでは、権限によって削除ボタンが表示の出し分けをしているかを確認しています。

ここで

・isAdmin = true(管理者) の場合、削除ボタンが表示されている
・isAdmin = false(一般) の場合、削除ボタンが表示されていない

ということが分かります。

f:id:ryonnsui1201:20190328212226p:plain

ちなみに、開発をする上では
① 実装する機能の要件を元にテストコードを書く
② テストコードのに表現されている要件を満たすコードを書く
③ テストが成功する状態を維持しつつリファクタリングしていく

という順番で行うと良いと思います。

さいごに

フロントのテストはどんなものなんだろうと、軽い気持ちでテストについて調べてみました。 導入自体は簡単で手軽に始められますが、何をテストするべきか、しないべきかの切り分けが非常に難しく重要であると感じました。 今回は、Vueコンポーネント単体テストの導入方法、シンプルなテストに留めましたが、 今後、Vuex周りのテストやE2Eテストについても調べ学習していきたいと思います。

現在、株式会社SCOUTERでは、エンジニア、デザイナーの募集をしております。

興味のある方は、是非下記からご応募お願い致します!

www.wantedly.com

www.wantedly.com

www.wantedly.com

参考資料

1年間のプログラミングを通して得た3つの教訓

はじめに

こんにちは株式会社SCOUTERでエンジニアをしているhirokinishizawaです。 業務では、SARDINEという人材紹介会社向けの業務管理システムを開発しています。

2019年2月1日でプログラミングというものに出会ってから1年が経ち楽しい時期やつらい時期もありましたが、1年間実務だけやってきた自分が得た教訓をプログラミングを始めたばかりの方やこれから始めてみたいと思っている方に共有をし、少しでもお役に立てればいいなぁと思います。

自分の経歴

入社する前

自分は日大付属の高校に通っていて特にやりたいこともなく大学受験をせずに卒業しました。(決して、決して行けなかったわけではないです!!多分。。。)

卒業をした後も特にやりたいことはなく主に軌道工事、塗装工事、屋上防水工事などいわゆる建築現場職人の仕事をしたり、土日などは父親の仕事の手伝いで3D図面を書いていたりしました。

そんな何の目標もないまま月日が流れ2018年1月に兄の繋がりで弊社CEO中嶋に出会いエンジニアという職業を知りその場で2月から入社をさせていただくことになりました。

入社してから

入社してからの流れは下記のようになります

ProgateでHTML&CSS、JavaScript、PHPを勉強
↓
オウンドメディア作成(jsはほぼやってもらった)
↓
サービスLP作成
↓
NuxtMeetUpのLT登壇
↓
サービス開発

このような流れでサービス開発に合流しました。

ありきたりかも知れませんが自分の得た教訓をこれから話していこうかと思います。

1.遠慮するのは時間の無駄

これはエンジニアに限った話ではないですが経験者、未経験者問わず入社してからわからないことは多々あるかと思います。

  • みんな他の仕事があるし質問するのは悪いのではないか。
  • 質問の回答が自分の求めていた回答ではなく、その時に別の聞き方ができずもう一度同じ質問するのが気まずい。
  • そもそも何で詰まってるのかがわからないから質問が出来ない。

質問したけど質問した内容の意味が分からない時に苦い顔するので再び質問しに行きにくいだったり、皆忙しいから質問しずらいだったり。いろいろな思いがあり質問しに行きずらいことがあるかと思いますが全て時間の無駄使いです、気にせず質問しましょう。

もちろん時間を割いて教えてくれるのですから最低限考えて質問しなければいけないとは思いますが、わからないものは一人で考えても分からないので、とにかく一つ一つ確認しましょう。

実際に自分は結構周りの目を気にしてしまうタイプで半年近くもの期間を無駄にしたなぁと今でも後悔しています。ですが困ったことがあったら質問する。再び分からなくなったら質問するを繰り返した結果自分の成長を実感できたのも事実です。

  • 自分が質問する時に気をつけていたこと
    • コードベースで話が出来ないぐらいわからない時はタスクベースでなにをしたいのかを伝えてまずは調べ方を教えてもらう
    • 調べてもわからない時はどのように検索したのかを先に伝えてその上で相手だったらどういうような対応をするのかを聞く
    • 言っている(回答の)意味が分からない時はちょっと止めてもらい出てきた単語だったりのメモを取り後ほど検索する

など意識していました。ただ自分は開発の質問をテキストでするのが苦手で口頭で聞きたいタイプなのですが、そうではない方もいるのでそこは臨機応変な対応ができる人になりましょう。

2.アサインされたら見積もりや設計を先にしよう

アサインされたのですぐにコードを書き始めるのは一見早いように思えますが、レビューしてもらう人に自分の設計の意図が伝わらず(そもそも設計が出来てないなど)手戻りが発生し結果遅くなることがあります。

現在弊社ではスクラムを導入しておりプランニングの時間をとってチームで見積もりや設計を行っております。フロントエンドが得意な人やサーバーサイドが得意な人、どちらも卒なくこなせる人などいろいろな人がいる中で見積もりを行うわけですから、導入してすぐの頃は見積もりがずれ当初決めていたリリース予定には間に合わなかったり、アサインされた人が設計をやったりしていたので分からない時もありコミュニケーションコストだったり手戻りが起きてしまったりした時にすごく時間がかかってしまうことがありました。

最近では認識のズレは改善されてきて見積もりも大幅にずれることはなくなり、プランニングで時間をかけて見積もりや設計を行うことによってコミュニケーションコストや手戻りが起こることがほぼ無くなってきました。

仮に個人で開発をするときでも自分だけが分かればいいのではありません。もし設計が苦手だとしても癖を付けるために他の人も触るという前提でコンポーネント設計など心がけるのがいいと思います。

3.一つ一つ順番に終わらせていく

タスクAのレビューをお願いしてその間にタスクBに着手しました。タスクBをやっている最中にタスクAのレビューが終わり修正が必要。そんな時にあなたはどちらを先にやりますか?「タスクAのレビュー対応がそこまで大変ではないので後回し」、「タスクBはまだまだ掛かりそうだから先にタスクAのレビュー対応しておこう」2つだけだったらなんとかなるかと思います。

ですがレビューをお願いしている人にも他の仕事がありレビューが遅れることもあります。タスクA、タスクBまで終わりレビューのお願いをしていてレビューが遅れてしまいタスクCを着手している時にレビューが返ってきてタスクA、タスクBともに膨大な手戻りが必要になった時。そこからさらに緊急度の高いバグ報告が来たら。他にも緊急度高い修正依頼が入ってきたら。焦ってしまいがキャパシティが小さい自分にとってはパンク寸前です。

エンジニアだけの話ではないと思いますがそれぞれタスク毎に何をやるべきなのか、その中で優先度が高いのはどれか。パンク寸前でも時間は待ってくれませんし、その時間に追われてどんどん焦ってくることがあります。ですが一度立ち止まって整理し一つ一つ終わらせていくしかありません。

実際に自分は一度にタスクが増えて焦ってしまい全部に手を出そうとして結果何も進まなかったということを何度かやってしまっています。今でもマルチタスクは苦手ですがメモを使い、やることを全て書き出してから優先度をつけてやっています。

まとめ

  • 成長をするためなら自分より出来る人に質問するしかない(ある程度限度はあります)
  • 見積もりや設計に時間を取られたとしてもしっかり行えばリターンはある
  • 焦らずにやるべきことリストを作って優先度をつけてパンクしないようにする

もちろん自分の性格など関係するとは思いますが、この3つのことで悩んでいる方も少なくはないのではと思います。そんな方に少しでもお役に立ててたら幸いです!

最後に

弊社では事業・サービスが成長していくにあたってメンバーを増やしていきたいと思っています。

興味のある方は下記からご応募いただくか、@hirokiにご連絡ください!!

www.wantedly.com

www.wantedly.com

www.wantedly.com