20210129のvue.jsに関する記事は9件です。

【Nuxt.js】Nuxt.jsプロジェクトにプラグインを導入する

Nuxt.jsで外部プラグインを導入する際の手順をまとめておきます。

1 . 追加したいプラグインをインストール(今回はvuejs-dialogを例にします)

npm i -save vuejs-dialog

package.jsonに追加されていることを確認!

package.json
"vuejs-dialog": "^1.4.2"

2 . pluginsフォルダ配下にファイルを作成する(ファイル名は任意)

※MacのコマンドなのでWindowsの方は読み替えて下さい。

touch plugins/vuejs-dialog.js

3 . 下記のようにプラグインをVueで使うことを宣言する

plugins/vuejs-dialog.js
import Vue from 'vue'
import VueDialog from 'vuejs-dialog'

Vue.use(VueDialog)

4 . 作成したplugins/vuejs-dialog.jsnuxt.config.jsに登録する

nuxt.config.js
plugins: [
    { src: '~/plugins/vuejs-dialog'}
  ],

これで各Vueファイルからプラグインを使用できます!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いいね機能をLaravelとVue.jsで実装②

こちらの記事はパート2です。
前回の記事はこちらから↓↓
https://qiita.com/tanaka2020/items/8ec8ae0fdd3ad4409c74

実装

4.ルートの設定

routesディレクトリ内のweb.phpを編集

Route::get('/posts/{post?}/firstcheck', 'LikeController@firstcheck')->name('like.firstcheck');・・・1
Route::get('/posts/{post?}/check', 'LikeController@check')->name('like.check');・・・2

1.Vue読み込み時にLikeControllerのfirstCheck()アクション呼び出し
2.いいねボタンクリック時に記事id別にLikeControllerのCheck()アクション呼び出し

5.Vue Components作成

resouces
 |ーjs
  |ーcomponents
    |ーLikeComponent.vue

<template>
 <div>
  <button v-if="status == false" type="button" @click.prevent="like_check" class="btn btn-outline-warning">&#9825;</button><a v-if="status == false" href="#">{{count}}</a>
  <button v-else type="button" @click.prevent="like_check" class="btn btn-warning">&#9829;</button><a v-if="status == true" href="#">{{count}}</a>
 </div>
</template>

<script>
export default {
 props: ['post_id'],・・・1
 data() {
   return {
     status: false,・・・2
     count: 0,
   }
 },
 created() {
   this.first_check()・・・3
 },
 methods: {
   first_check() {
     const id = this.post_id
     const array = ["/posts/",id,"/firstcheck"];
     const path = array.join('')
     axios.get(path).then(res => {
       if(res.data[0] == 1) {
         console.log(res)
         this.status = true
         this.count = res.data[1]
       } else {
         console.log(res)
         this.status = false
         this.count = res.data[1]
       }
     }).catch(function(err) {
       console.log(err)
     })
   },
   like_check() {
     const id = this.post_id
     const array = ["/posts/",id,"/check"];
     const path = array.join('')
     axios.get(path).then(res => {
       if(res.data[0] == 1) {
         this.status = true
         this.count = res.data[1]
       } else {
         this.status = false
         this.count = res.data[1]
       }
     }).catch(function(err) {
       console.log(err)
     })
   },
 }
}
</script>

1.blade側はより投稿記事のidが渡されます。

<like-component :post_id="{{$post->id}}"></like-component>

2.statusがfalseだといいねされていない状態です。
3.Vue読み込み時にLikeControllerのfirstCheck()アクションが実行されます。

まとめ

status = falseスクリーンショット 2021-01-29 23.16.28.pngstaatus = trueスクリーンショット 2021-01-29 23.16.38.png

今回はAxiousで実装しており非同期でLikeContorollerのアクションを呼び出して、いいね機能を実装しています。もっと簡単で出来る方法や綺麗な記述ありましたらご教授お願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いいね機能をLaravelとVue.jsで実装①

投稿型のサイトを作成していて、
いいね機能を実装したいと思ったので作成しました。

環境

・PHP 7.4.9
・Composer version 1.10.13
・Laravel Framework 7.30.1
・vue/cli 4.3.1

流れ

1.投稿機能の実装(今回の記事では省略)
2.いいね機能用のdatabeseテーブルの作成
3.Vueコンポーネントの作成
4.Controllerの作成
5.ルート設定
6.view表示

実装

1.投稿機能の実装

 今回は省略!!

2.いいね機能用のdatabeseテーブルの作成

$ php artisan make:model Like -m

-mをつけることでモデルと一緒にマイグレーションファイルも作ってくれます。
※databaseの設定は省略します。設定はconfigフォルダdatabase.phpと.envを編集

作成したマイグレーションファイルの編集

public function up()
    {
        Schema::create('likes', function (Blueprint $table) {
            $table->id();
            $table->integer('posts_id');
            $table->integer('user_id')->unable();
            $table->integer('like')->default(0);
            $table->timestamps();
        });
    }

likeカラムの値が0か1かでいいねされているかを判断するように実装します

作成したモデルLike.phpの編集

class Like extends Model
{
     protected $fillable = [
          'posts_id','user_id','like'
     ];
}

"fillable"はホワイトリストです。$fillableで指定したカラムは値が代入可能です。

マイグレーションの実行

php artisan migrate

3.コントローラーの作成

php artisan make:controller LikeController

Vueのコンポーネントから呼び出されるアクションを書いていきます。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Post;//投稿機能のモデル
use App\Like;
use Auth;

class LikeController extends Controller
{
    //コンポーネント初期読み込み時(created)に呼び出される
    public function firstcheck($post) {
     $user = Auth::user();
     $likes = new Like();
     $like = Like::where('posts_id',$post)->where('user_id',$user->id)->first();
     if($like) {
          $count = $likes->where('posts_id',$post)->where('like',1)->count();
          return [$like->like,$count];
     } else {
          $like = $likes->create([
               'user_id' => $user->id,
               'posts_id' => $post,
               'like' => 0
          ]);
          $count = $likes->where('posts_id',$post)->where('like',1)->count();
          return [$like->like,$count];
     }
    }

    //いいねボタンを押した時に呼び出される
    public function check($post) {
     $user = Auth::user();
     $likes = new Like();
     $like = Like::where('posts_id',$post)->where('user_id',$user->id)->first();
     if($like->like == 1) {
          $like->like = 0;
          $like->save();
          $count = $likes->where('posts_id',$post)->where('like',1)->count();
          return [$like->like,$count];
     } else {
          $like->like = 1;
          $like->save();
          $count = $likes->where('posts_id',$post)->where('like',1)->count();
          return [$like->like,$count];
     };
    }
}

長くなってきたので次に続く。。。。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

firebase deploy で詰まった時の対処法

簡単な自己紹介

今回が初投稿になります!現在フロントエンド を学習している就活生です。
何事もアウトプットが重要だと思うので投稿してみようと思いました!かつ自分の備忘録として記録しようと思います。

今回vueCLIとfirebaseを使用してアプケーションを作成したのですが、最後の最後デプロイするところで壁にぶつかったので記録しておきます!

サイトが公開されていない

詳しいデプロイ方法は他の記事を参照してください。

$ firebase deploy

これでfirebase deployが成功した場合、最後にURLが発行されると思います。
そしてそのURLをクリックして無事公開できたと思ったらこの画面になりました、、、
Image from Gyazo

原因

おそらく人によって様々かとは思いますが私の場合はdistディレクトリの位置がおかしかったからでした。

$firebase init

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  C:\Users\username\
            ⬆︎ここがおかしい

上記のようになっている場合うまくいきません

C:\Users\username\desktop\app

このようにしておかなければなりませんでした。

解決策

私の場合すでに作成しまっていたdistディレクトリを削除し、また新しく作り直したのちに
正しいディレクトリの位置でfirebase init を行いました。

そしてもう一度firebase deploy を実行すると成功しました。

これまでターミナルなんて物を触ったこともなかったのでこんなこと当たり前だよ!と言われそうですが、
何故無知な者で。。
これからも精進していきます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vue-simple-suggestでサジェストが遅すぎて使い物にならなかった

目的

前回の記事でvue-simple-suggestの導入をしたんですが、サジェストが遅すぎて使い物にならなかったので(私の実装方法が悪い)改善しました。
前回の記事

原因

フォームに入力する度に、APIを叩いてサジェストの候補を呼び出していた。

遅すぎて使い物にならなかったコード

form.vue
<template>
    <div class="form-group">
        <vue-simple-suggest
            v-model="selected"
            :list="getSuggestionList"
            :filter-by-query="true">
            <input type="text" name="tag" id="tag" placeholder="タグを入力してください" autocomplete="off">    
        </vue-simple-suggest>
    </div>
</template>
<script>
import VueSimpleSuggest from "vue-simple-suggest";
import 'vue-simple-suggest/dist/styles.css';

export default {
    components: {
        VueSimpleSuggest
    },
    data() {
        return {
            selected: '',
            List:'',
        };
    },
    methods: {
        async getSuggestionList() {
            return await axios.get('/api/tagList')
            .then(res => this.List = res.data )
            .catch((error)=>{
                this.errorMsg = 'Error! Could not reach the API. ' + error
                console.log(this.errorMsg)
            })
        },
    }
}
</script>

解決方法

フォームに入力する度にAPIを呼び出すのではなくて、コンポーネントが呼び出された時に一度だけAPIを叩く。
createdで呼び出すことで、インスタンスを作成した後に一度だけ呼び出して配列に格納します。

form.vue
<template>
    <div class="form-group">
        <vue-simple-suggest
            v-model="selected"
            :list="List" <!--メソッドを呼び出すのではなく配列を呼び出す-->
            :filter-by-query="true">
            <input type="text" name="tag" id="tag" placeholder="タグを入力してください" autocomplete="off">    
        </vue-simple-suggest>
    </div>
</template>
<script>
import VueSimpleSuggest from "vue-simple-suggest";
import 'vue-simple-suggest/dist/styles.css';

export default {
    components: {
        VueSimpleSuggest
    },
    data() {
        return {
            selected: '',
            List:'',
        };
    },
    methods: {
        //async getSuggestionList() {
        //    return await axios.get('/api/tagList')
        //    .then(res => this.List = res.data )
        //    .catch((error)=>{
        //        this.errorMsg = 'Error! Could not reach the API. ' + error
        //        console.log(this.errorMsg)
        //    })
        //},
    },
    /*-----------以下のコードを追加-------------------*/
    created(){
        axios.get('/api/tagList')
        .then(res => this.List = res.data )
        .catch((error)=>{
            this.errorMsg = 'Error! Could not reach the API. ' + error
            console.log(this.errorMsg)
        })        
    }
}
</script>

失敗した方法

最初、createdではなくwindow:onloadを使って画面ロード時にAPIを呼び出そうとしたら配列が空のままでした。

form.vue
<template>
    <div class="form-group">
        <vue-simple-suggest
            v-model="selected"
            :list="List"
            :filter-by-query="true">
            <input type="text" name="tag" id="tag" placeholder="タグを入力してください" autocomplete="off">    
        </vue-simple-suggest>
    </div>
</template>
<script>
import VueSimpleSuggest from "vue-simple-suggest";
import 'vue-simple-suggest/dist/styles.css';

export default {
    components: {
        VueSimpleSuggest
    },
    data() {
        return {
            selected: '',
            List:'',
        };
    },
    methods: {
        window:onload = function() {
            axios.get('/api/tagList')
            .then(res => this.List = res.data )
            .catch((error)=>{
                this.errorMsg = 'Error! Could not reach the API. ' + error
                console.log(this.errorMsg)
            })
        },
    }
}
</script>

この方法だとインスタンスが作成される前に呼び出されてしまうため、ダメだったようです。
vueのライフサイクルしっかり学ばなきゃ。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フロントエンド( Vue )の単体テスト( Jest )のテストケース作成とテストコード作成

単体テストは奥が深い

テストコード書いてみたいな、ということで以下の作ったものを対象に、テストケースとテストコードを作成したため、そのプロセスを記事にする。単体テストは難しいし、奥が深い...

環境

単体テスト対象

単体テストは、Search.vue を対象に実施する。Search.vue に実装されているイベントを確認する。

  • 初期表示
    1.png

  • 入力ボックスに検索キーワードを入力
    2.png

  • 検索待ち
    3.png

  • 検索結果(検索結果あり)
    4.png

  • 検索結果(検索結果なし)
    5.png

これらは、Search.vue で処理されており、画面遷移はない。

単体テストの実施対象は Search.vue とする。Search.vue の中で呼び出されている別のコンポーネントは、Search.vue の単体テストでは単体テスト対象としない。

結果として、ざっくり Search.vue の単体テスト対象は青色の範囲で、赤色の範囲は別コンポーネントで実装しているため範囲外とする。
6.png

ざっくりとディレクトリ構成を以下に示し、単体テストの対象をファイル単位で確認する。

/src
  /views
    - Search.vue <- 単体テスト対象
  /components
    - Player.vue <- 検索結果を表示するコンポーネント。単体テスト対象外。
    - Loading.vue <- 検索待ちのローディングを表示するコンポーネント。単体テスト対象外。
  /api
    - api.ts <- 検索時に実行される Http リクエスト。単体テストではモックにする。

単体テストケースを作成する

単体テストのケース作成方針

私のような SIer 所属の人間は、単体テストとなると、コードベースのC1(分岐網羅)でテストケースを作成する、としたくなるが、vue-test-utils にそれはしないほうがいいよ、ということが書かれている。

何をテストするかを知る

UI コンポーネントでは、コンポーネントの内部実装の詳細に集中しすぎて脆弱なテストが発生する可能性があるため、完全なラインベースのカバレッジを目指すことはお勧めしません。
代わりに、コンポーネントのパブリックインターフェイスを検証するテストを作成し、内部をブラックボックスとして扱うことをお勧めします。単一のテストケースでは、コンポーネントに提供された入力(ユーザーのやり取りやプロパティの変更)によって、期待される出力(結果の描画またはカスタムイベントの出力)が行われることが示されます。

https://vue-test-utils.vuejs.org/ja/guides/#%E4%B8%80%E8%88%AC%E7%9A%84%E3%81%AA%E3%83%92%E3%83%B3%E3%83%88

上記を読み替えると、コンポーネントに提供された入力をテストケースとし、期待される出力をテストケース結果として検証していく、形になる。
コンポーネントに提供された入力って何?となるが、vue-mastery に以下のものがコンポーネントに提供される入力であると書かれている。

  • Component Data (コンポーネントに定義されたデータが変更される)
  • Component Props (親コンポーネントからプロップが渡される)
  • User Interaction (ユーザーが画面を操作する)
    Ex: user clicks a button
  • Lifecycle Methods (Vue ライフサイクルが変わる)
    mounted(), created(), etc.
  • Vuex Store (Vuex にストアされているデータが変更される)
  • Route Params (URL のパラメータによってデータが渡される)

次に期待される出力って何?となるが、同じく vue-mastery に以下のものが期待される出力となると書かれている。

  • What is rendered to the DOM (表示内容が変わる)
  • External function calls (外部関数の実行される)
  • Events emitted by the component (親コンポーネントのイベントを発火させる)
  • Route Changes (画面が遷移する)
  • Updates to the Vuex Store (Vuex にストアしているデータを変更する)
  • Connection with children (子コンポーネントを呼び出す) i.e. changes in child components

コンポーネントに提供された入力の種類
期待される出力の種類
https://www.vuemastery.com/courses/unit-testing/what-to-test/

単体テストのケース作成方針は、テストケースで確認することは、コンポーネントに提供された入力であり、テストケース結果は期待される出力である。この方針でテストケースを作成していると、例えば、開発した人がテストケースを作成すると陥りがちなラインベースでのテストケースになる、という事象は結構防げる。

Search.vue にコンポーネントに提供される入力と期待される出力を洗い出す

以下のフォーマットで書く。

  • コンポーネントに提供される入力 : 期待される出力

以下が洗い出した結果である。

  • input にフォーカスする : タイトルが変更される
  • input からフォーカスを外す : タイトルが変更される
  • input に文字を入力する : 検索ボタンが有効になる(押せるようになる)
  • input に文字を入力して検索ボタンを押す : current page が表示される
  • input に文字を入力して検索ボタンを押す : previous ボタンが表示される
  • input に文字を入力して検索ボタンを押す : このイベントの期待される出力多いのであとは省略
  • previous ボタンを押す : 次のページが表示される
  • next ボタンを押す : 次のページが表示される

洗い出し終わったら、これら分類していく。例えば、コンポーネントに提供される入力の場合は、「input にフォーカスする」はユーザーインタラクションであり、その期待される出力は「タイトルが変更される」であるため、表示内容が変わる、であるという感じ。

このときに悪い洗い出し方の例として、期待される出力が「loadingFlag が true になる」となっていた場合、期待される出力の分類のどれにも該当しないため、適切な期待される出力ではない、となる。

拙い英語で恐縮だが、私はソースコードに以下のような形でコンポーネントに提供される入力と期待される出力を、分類も含めて書くようにしている。もうちょっと量が増えてきて、かつ、レビュー対象になると見やすいEXCELさんに書く。

// Press the search button (User Interaction) -  "current_page" is rendered (What is rendered to the DOM)
// Press the search button (User Interaction) -  "total page" is rendered (What is rendered to the DOM)
// Press the search button (User Interaction) -  previous button is rendered (What is rendered to the DOM)
// Press the search button (User Interaction) -  next button is rendered (What is rendered to the DOM)

テストコード作成

全てはテストコードとして載せきれないため、サンプルとしてテストコードの作成対象は以下のテストケースにする。

  • input に文字を入力して検索ボタンを押す : current page が表示される

テストコードを作成する上で私が気にしているポイントとしては以下。

  • shallowMount
    Search.vue で呼び出しているコンポーネントはスタブで扱う。呼び出しているコンポーネントは単体テストの対象ではないため。
  • axios はモック化する
    検索ボタンを押したときに Http リクエストが発行され、そのレスポンスに検索結果があるのだが、これはモック化する。Search.vue の表示などの単体テストをしたいので、Http リクエストの発行などは確認対象外である。モック化して受け取るレスポンスは、response.js に定義しており、そこにテストケースが消化できるようなデータを用意しておく。
import axios from 'axios'
import { shallowMount } from "@vue/test-utils";
import flushPromises from 'flush-promises'

import Search from "@/views/Search.vue";
import {
  firstResponse200,
} from './response.js'

jest.mock('axios')

describe("Search.vue", () => {
  it('Press the search button (User Interaction) -  "current_page" is rendered (What is rendered to the DOM)', async () => {

    // モックが返すレスポンスを定義
    axios.get.mockResolvedValueOnce(firstResponse200)

    // shallowMount で Search.vue が呼び出しているコンポーネントはスタブにする
    const wrapper = shallowMount(Search)

    // input に test という文字列を入力する
    const searchInput = wrapper.find('[data-testid="search-input"]')
    searchInput.setValue('test')

    // 画面の更新処理
    wrapper.vm.$nextTick()
    await flushPromises()

    // 検索の実行
    wrapper.find('[data-testid="search-button"]').trigger('click')

    // 画面の更新処理
    wrapper.vm.$nextTick()
    await flushPromises()

    // 期待する出力の検証
    expect(wrapper.find('[data-testid="current-page-paragraph"]').html()).toMatch('<p class="title" data-testid="current-page-paragraph">1</p>')
  })
})

余談 & まとめ

これは余談なんですが、テストケース作成したからと言って、全てをテストコード化するわけではないです。テストコードを作成せずに手で画面を操作して確認して終わりにすることもあります。当然のことですが、テストコードの作成はそれなりに時間がかかります。感覚ベースですが、2 時間で開発したコードのテストコード作成は 2 時間ほどかかります。たまにですが、コードを書き終わったはいいが、テストコードにしづらいため、コードを書き直すこともあります。とにかくテストコード化はそれなりに大変であるということ。書かなくていいなら、書かないほうがいい、と思っています。書く書かないは開発物の対象の性質に依存するので、それぞれの現場で判断するべき内容になるので、なかなか一概には言えないところです。少なくとも私のように趣味で書いてあるコードに対してテストコードを書くのは過剰であると、そう思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フロントエンド( Vue )の単体テストのテストケース作成とテストコード作成

単体テストは奥が深い

テストコード書いてみたいな、ということで以下の作ったものを対象に、テストケースとテストコードを作成したため、そのプロセスを記事にする。単体テストは難しいし、奥が深い...

環境

単体テスト対象

単体テストは、Search.vue を対象に実施する。Search.vue に実装されているイベントを確認する。

  • 初期表示
    1.png

  • 入力ボックスに検索キーワードを入力
    2.png

  • 検索待ち
    3.png

  • 検索結果(検索結果あり)
    4.png

  • 検索結果(検索結果なし)
    5.png

これらは、Search.vue で処理されており、画面遷移はない。

単体テストの実施対象は Search.vue とする。Search.vue の中で呼び出されている別のコンポーネントは、Search.vue の単体テストでは単体テスト対象としない。

結果として、ざっくり Search.vue の単体テスト対象は青色の範囲で、赤色の範囲は別コンポーネントで実装しているため範囲外とする。
6.png

ざっくりとディレクトリ構成を以下に示し、単体テストの対象をファイル単位で確認する。

/src
  /views
    - Search.vue <- 単体テスト対象
  /components
    - Player.vue <- 検索結果を表示するコンポーネント。単体テスト対象外。
    - Loading.vue <- 検索待ちのローディングを表示するコンポーネント。単体テスト対象外。
  /api
    - api.ts <- 検索時に実行される Http リクエスト。単体テストではモックにする。

単体テストケースを作成する

単体テストのケース作成方針

私のような SIer 所属の人間は、単体テストとなると、コードベースのC1(分岐網羅)でテストケースを作成する、としたくなるが、vue-test-utils にそれはしないほうがいいよ、ということが書かれている。

何をテストするかを知る

UI コンポーネントでは、コンポーネントの内部実装の詳細に集中しすぎて脆弱なテストが発生する可能性があるため、完全なラインベースのカバレッジを目指すことはお勧めしません。
代わりに、コンポーネントのパブリックインターフェイスを検証するテストを作成し、内部をブラックボックスとして扱うことをお勧めします。単一のテストケースでは、コンポーネントに提供された入力(ユーザーのやり取りやプロパティの変更)によって、期待される出力(結果の描画またはカスタムイベントの出力)が行われることが示されます。

https://vue-test-utils.vuejs.org/ja/guides/#%E4%B8%80%E8%88%AC%E7%9A%84%E3%81%AA%E3%83%92%E3%83%B3%E3%83%88

上記を読み替えると、コンポーネントに提供された入力をテストケースとし、期待される出力をテストケース結果として検証していく、形になる。
コンポーネントに提供された入力って何?となるが、vue-mastery に以下のものがコンポーネントに提供される入力であると書かれている。

  • Component Data (コンポーネントに定義されたデータが変更される)
  • Component Props (親コンポーネントからプロップが渡される)
  • User Interaction (ユーザーが画面を操作する)
    Ex: user clicks a button
  • Lifecycle Methods (Vue ライフサイクルが変わる)
    mounted(), created(), etc.
  • Vuex Store (Vuex にストアされているデータが変更される)
  • Route Params (URL のパラメータによってデータが渡される)

次に期待される出力って何?となるが、同じく vue-mastery に以下のものが期待される出力となると書かれている。

  • What is rendered to the DOM (表示内容が変わる)
  • External function calls (外部関数の実行される)
  • Events emitted by the component (親コンポーネントのイベントを発火させる)
  • Route Changes (画面が遷移する)
  • Updates to the Vuex Store (Vuex にストアしているデータを変更する)
  • Connection with children (子コンポーネントを呼び出す) i.e. changes in child components

コンポーネントに提供された入力の種類
期待される出力の種類
https://www.vuemastery.com/courses/unit-testing/what-to-test/

単体テストのケース作成方針は、テストケースで確認することは、コンポーネントに提供された入力であり、テストケース結果は期待される出力である。この方針でテストケースを作成していると、例えば、開発した人がテストケースを作成すると陥りがちなラインベースでのテストケースになる、という事象は結構防げる。

Search.vue にコンポーネントに提供される入力と期待される出力を洗い出す

以下のフォーマットで書く。

  • コンポーネントに提供される入力 : 期待される出力

以下が洗い出した結果である。

  • input にフォーカスする : タイトルが変更される
  • input からフォーカスを外す : タイトルが変更される
  • input に文字を入力する : 検索ボタンが有効になる(押せるようになる)
  • input に文字を入力して検索ボタンを押す : current page が表示される
  • input に文字を入力して検索ボタンを押す : previous ボタンが表示される
  • input に文字を入力して検索ボタンを押す : このイベントの期待される出力多いのであとは省略
  • previous ボタンを押す : 次のページが表示される
  • next ボタンを押す : 次のページが表示される

洗い出し終わったら、これら分類していく。例えば、コンポーネントに提供される入力の場合は、「input にフォーカスする」はユーザーインタラクションであり、その期待される出力は「タイトルが変更される」であるため、表示内容が変わる、であるという感じ。

このときに悪い洗い出し方の例として、期待される出力が「loadingFlag が true になる」となっていた場合、期待される出力の分類のどれにも該当しないため、適切な期待される出力ではない、となる。

拙い英語で恐縮だが、私はソースコードに以下のような形でコンポーネントに提供される入力と期待される出力を、分類も含めて書くようにしている。もうちょっと量が増えてきて、かつ、レビュー対象になると見やすいEXCELさんに書く。

// Press the search button (User Interaction) -  "current_page" is rendered (What is rendered to the DOM)
// Press the search button (User Interaction) -  "total page" is rendered (What is rendered to the DOM)
// Press the search button (User Interaction) -  previous button is rendered (What is rendered to the DOM)
// Press the search button (User Interaction) -  next button is rendered (What is rendered to the DOM)

テストコード作成

全てはテストコードとして載せきれないため、サンプルとしてテストコードの作成対象は以下のテストケースにする。

  • input に文字を入力して検索ボタンを押す : current page が表示される

テストコードを作成する上で私が気にしているポイントとしては以下。

  • shallowMount
    Search.vue で呼び出しているコンポーネントはスタブで扱う。呼び出しているコンポーネントは単体テストの対象ではないため。
  • axios はモック化する
    検索ボタンを押したときに Http リクエストが発行され、そのレスポンスに検索結果があるのだが、これはモック化する。Search.vue の表示などの単体テストをしたいので、Http リクエストの発行などは確認対象外である。モック化して受け取るレスポンスは、response.js に定義しており、そこにテストケースが消化できるようなデータを用意しておく。
import axios from 'axios'
import { shallowMount } from "@vue/test-utils";
import flushPromises from 'flush-promises'

import Search from "@/views/Search.vue";
import {
  firstResponse200,
} from './response.js'

jest.mock('axios')

describe("Search.vue", () => {
  it('Press the search button (User Interaction) -  "current_page" is rendered (What is rendered to the DOM)', async () => {

    // モックが返すレスポンスを定義
    axios.get.mockResolvedValueOnce(firstResponse200)

    // shallowMount で Search.vue が呼び出しているコンポーネントはスタブにする
    const wrapper = shallowMount(Search)

    // input に test という文字列を入力する
    const searchInput = wrapper.find('[data-testid="search-input"]')
    searchInput.setValue('test')

    // 画面の更新処理
    wrapper.vm.$nextTick()
    await flushPromises()

    // 検索の実行
    wrapper.find('[data-testid="search-button"]').trigger('click')

    // 画面の更新処理
    wrapper.vm.$nextTick()
    await flushPromises()

    // 期待する出力の検証
    expect(wrapper.find('[data-testid="current-page-paragraph"]').html()).toMatch('<p class="title" data-testid="current-page-paragraph">1</p>')
  })
})

余談 & まとめ

これは余談なんですが、テストケース作成したからと言って、全てをテストコード化するわけではないです。テストコードを作成せずに手で画面を操作して確認して終わりにすることもあります。当然のことですが、テストコードの作成はそれなりに時間がかかります。感覚ベースですが、2 時間で開発したコードのテストコード作成は 2 時間ほどかかります。たまにですが、コードを書き終わったはいいが、テストコードにしづらいため、コードを書き直すこともあります。とにかくテストコード化はそれなりに大変であるということ。書かなくていいなら、書かないほうがいい、と思っています。書く書かないは開発物の対象の性質に依存するので、それぞれの現場で判断するべき内容になるので、なかなか一概には言えないところです。少なくとも私のように趣味で書いてあるコードに対してテストコードを書くのは過剰であると、そう思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【プログラミング未経験者のための】Vue.jsとは

はじめに

この記事は、Vue.jsの本格的な学習を始める前に、その大まかなイメージを把握してもらうためのものです。よって、実際のコードは登場しません。また、理解しやすいように説明も簡潔にしてあります。ご了承ください。

また、記事の対象は
●本当に正真正銘のプログラミング初心者(「HTML?…あー聞いたことあるわ」みたいな人)
●HTML、CSSをちょっとかじった人、とりあえずPythonの本買ったけど全然開いてない人(私)
●JavaScriptのチュートリアル動画見終わったぜ!な人
らへんの、そもそもプログラミング初心者な人向けです。

かくいう私も同じ道をたどってきた(今も)初心者です。半年ぐらい前にVueでプログラミングを始めました。学習するうえで私がつまずいてきたところを抑えつつ、始める前に知っておきたかったなぁと思うことを書いていきます。参考になれば幸いです。では早速。

Vue.jsってなんだ

JavaScriptのフレームワークのひとつです。

って検索すれば出てきますが、私は最初いまひとつ理解できませんでした。

理解するにあたって、まずはHTML, CSS, そして JavaScriptが何かを知っておきましょう。

●HTML, CSS, JavaScript

3つとも、コンピューター君と会話するための言語です。それぞれの言語で書き方が異なります。

HTMLとは、文字や画像、ボタンなどを表示させるものです。それだけです。

でも、パーツ的には同じ文字、画像、ボタンなのにAmazonと楽天だと全然サイトの見た目が違いませんか?

そういった見た目を整えるために、CSSを使います。文字を大きくしたり、ボタンの色を変えたりとかできます。

さあ、Amazonのページが完成しました!でも、商品をクリックしても何も反応しません。なぜでしょう?

見た目しか作ってないからです。実際のアクションは、JavaScriptで書けます。いいねを押すといいね数を+1する、とか。

●フロントエンド、バックエンド

今まで紹介してきたものはすべて、フロントエンドと呼ばれる部分の開発です。

フロントエンドとは、文字通り前側(フロントエンド)、つまりユーザーが見ることのできる部分のことです。

ちなみにこの反対には、バックエンドと呼ばれる部分があります。

バックエンドは先ほどの逆、つまりユーザーが見ることのできない部分、例えばデータを保管したりする部分です。

●フレームワークとは

すごく簡単に言うと、その言語をより簡単に、便利に使うための追加パックみたいな感じです。

めんどくさい事を全部裏でやってくれます。また、何かをするやり方を自分で考えずに、やり方を指定してくれます(ルールを決めてくれる)。逆に言えば、自由度が下がります。

字を書く、という例えでいえばフレームワークなしは「自分で普通に書く」、フレームワークありは「印刷されたお手本をなぞる」感じです。

Vue.js(ビュージェーエス)はそういったフレームワークのひとつで、JavaScriptという言語のためのものです。

ちなみに、フレームワークと似たものでライブラリというものもあります。それはただ毎回コードを書かなくてもいいように、再利用できるまとまりを提供してくれるものです。WordやPowerPointのテンプレートみたいな感じ。



学習プロセス


1, HTML, CSS, JavaScriptの基礎を学ぶ


上記のフレームワークの説明のとおり、Vue.jsはJavaScriptをより使いやすくしてくれるものなので、


まずはそのベースとなる言語たちを知っておかなければいけません。


何を使って学習するかですが、一通り基礎的なことが網羅されているものであれば本やサイト、動画など本当に何でも構いません。


いくつか挙げておきます。



このステップでの大事なポイントは、全てを学習することにこだわらないことです。ましてや覚えようとなんてしなくて大丈夫です。


これをやってしまうとVueにたどり着く前に挫折コースまっしぐらです。


いくら覚えても使わない限り絶対に忘れます。なのでむやみに覚えるより、必要な時にその都度調べる方が賢明です。


ただ、そもそも何ができるのか、どういったものが使えるのかなどが分からないと「何が分からないのかが分からない」状態になるので、一通りの基礎をさらっと試してみる必要はあります。


「あーあれどう書くんだっけ」とか、調べてみて「あーこれ前やったなそういえば」ってなるくらいが理想。読めるけど書けない漢字みたいな(?)


2, Vue をはじめてみる


本当に最初は、基礎が網羅されている本やサイト、動画などに完全に従うのが吉。


知らない概念が出てくるたびググるとなお良し。無理に理解しなくても「へーこういうのがあるんだ」ぐらいでいいです。



  • 公式ドキュメント (https://v3.ja.vuejs.org/)

    なんと日本語であります。一番正確かつ網羅的です。どれだけドキュメントを見るのが嫌いでも見る時が来ることでしょう。


    私も「ドキュメントはややこしいし難しい」という理由から、分からないところを何時間もかけて動画や質問サイトで探し、結局公式ドキュメントに普通に書いてあった、という事が多々あります。



  • Udemyコース (https://www.udemy.com/course/vuejs-2-the-complete-guide/)

    英語です。が、字幕はあります。英語アレルギーのない方には強くお勧めします。実際私はこれに沿って学習しましたが。本当に説明が分かりやすく、至れり尽くせりな内容です。


    実際に学んだ知識を実践できるページ制作チャレンジもあります。確実に力が付きますよ!


    ちなみに、日本語のものだとこういうコース(https://www.udemy.com/course/vue-js-complete-guide/)もありました。





  • 以上、また随時追加していこうと思います。少しでも参考になれば幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】v-ifとv-showによる表示、非表示の切り替えの違い

sample.vue
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>

<div id="app">
  <button @click="changeShow">ボタン</button>
  <p v-if="isActive">if</p>
  <p v-show="isActive">show</p>
</div>

<script>
  new Vue({
    el: '#app',
    data: {
      isActive: true
    },
    methods: {
      changeShow() {
        this.isActive = !this.isActive
      }
    }
  })
</script>

上記の様な、ボタンをクリックすると、「if」の文字と「show」の文字の表示・非表示が切り替わるプログラムがある。

v-ifの場合は、条件がfalseの場合、DOM自体

<p v-if="isActive">if</p>

が消滅する。

v-showの場合は、条件がfalseの場合、DOMに display:none が追加されて非表示となる。

<p style="display:none">show</p>

v-showのデメリット

・非表示時の挙動は、ページ読み込み時に一旦、全てのDOMを読み込んでからdisplay:noneで非表示にするため、DOMそのものを読み込まないv-ifに比べて初期読み込み時の動作が重くなる。

・v-elseのように、falseの場合に別処理を実行することができない。

・タグには使用できない。(templeteタグはそもそもhtmlに表示されないので、display:noneのつけようがない)

v-ifのデメリット

表示・非表示の切り替え時に、DOMを直接操作するため、display:noneを与えるだけのv-showに比べて、動作が重くなる。

適材適所は?

頻繁に表示・非表示を切り替える必要がある場合は、"v-show"。それ以外は、"v-if"を使うのがベター。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む