づののしウ日記

学んだことを整理するために使います

ソフトウェアテスト

ソフトウェアテスト

https://service.shiftinc.jp/column/3621/

ソフトウェアをリリースする上で大切なことは、「不具合のない製品」を作ることです。不具合が多く使えない機能が多いことは、品質が悪い製品を意味します。このことから、ソフトウェアテストは不具合を発見し改善するために行います。

ソフトウェアテストの7原則

  • 不具合があることしか示せない
  • 全数テストは不可能
  • 初期テスト
  • 不具合の偏在
  • 殺虫剤のパラドックス
  • テストは条件次第
  • 不具合ゼロの落とし穴

ソフトウェアテストの分類方法

品質によるソフトウェアテストの分類

ソフトウェアの品質とは?

品質特性 概要
機能適合性 実装された機能がニーズを満たす度合い
性能効率性 システムの実行時の性能や資源効率の度合い
互換性 他製品やシステムと機能や情報を共有、変換できる度合い
使用性 効果的、効率的に利用できる度合い
信頼性 必要時に実行できる度合い
セキュリティ 不正に悪用されることがなく、情報やデータが保護される度合い
保守性 効果的、効率的に保守や修正ができる度合い
移植性 効果的、効率的に他のハードウェアや実行環境に移植できる度合い

品質を測るためのテストの具体例

  • 機能テスト
    • リクエスト通りに動作するかどうか
  • 性能テスト
    • 快適に使えるレスポンスの早さかどうか
  • 負荷テスト
    • 負荷の許容できる限界値を測る
  • ユーザビリティテスト
    • ユーザーが使いやすいかどうか
  • セキュリティテスト
    • 利用が安全かどうか

工程によるソフトウェアテストの分類

テスト計画

  1. テスト対象の決定
  2. テストのやり方の決定
  3. 役割分担の決定
  4. 準備対象の決定
  5. スケジュールの決定
  6. 管理方針の決定

テスト設計

  1. インプット情報の入手と確認
  2. テスト設計方針の決定
  3. テストケースの作成

テスト実行

  1. テスト環境の準備
  2. テストデータの準備
  3. 実行手順の確認
  4. ツールの準備
  5. 実行スケジュールの作成
  6. 実行

テストレベルによるソフトウェアテストの分類

ソフトウェアテストでは、まずそのソフトウェアの動作における最小単位でテストを行います。これを単体テスト、あるいはユニットテストと言います。

次に、複数の動作を組み合わせて結合テストを行います。結合テストでは連続する動作が正常に動くかどうかを確認します。

開発の終盤で行われるのが、ソフトウェアの全体の流れを確認するシステムテストです。システムテストでは、ソフトウェアだけでなくハードウェア部分も包括してテストを行います。

技法による分類

テスト技法で分類すると、ブラックボックステストホワイトボックステストに分けられます。

ブラックボックスは「中身が見えない」という意味で、ブラックボックステストではソフトウェアの中身を見ずに、入力した内容に対して期待した出力があるかを確認します。

一方、ホワイトボックスではソフトウェアの中身も確認し、入力から出力の間にどのような処理が行われているかも併せて確認します。

引用リスト

ソフトウェアテストとは?種類や目的、重要な7原則を紹介

ソフトウェアテスト

ソフトウェアテスト

https://service.shiftinc.jp/column/3621/

ソフトウェアをリリースする上で大切なことは、「不具合のない製品」を作ることです。不具合が多く使えない機能が多いことは、品質が悪い製品を意味します。このことから、ソフトウェアテストは不具合を発見し改善するために行います。

ソフトウェアテストの7原則

  • 不具合があることしか示せない
  • 全数テストは不可能
  • 初期テスト
  • 不具合の偏在
  • 殺虫剤のパラドックス
  • テストは条件次第
  • 不具合ゼロの落とし穴

ソフトウェアテストの分類方法

品質によるソフトウェアテストの分類

ソフトウェアの品質とは?

品質特性 概要
機能適合性 実装された機能がニーズを満たす度合い
性能効率性 システムの実行時の性能や資源効率の度合い
互換性 他製品やシステムと機能や情報を共有、変換できる度合い
使用性 効果的、効率的に利用できる度合い
信頼性 必要時に実行できる度合い
セキュリティ 不正に悪用されることがなく、情報やデータが保護される度合い
保守性 効果的、効率的に保守や修正ができる度合い
移植性 効果的、効率的に他のハードウェアや実行環境に移植できる度合い

品質を測るためのテストの具体例

  • 機能テスト
    • リクエスト通りに動作するかどうか
  • 性能テスト
    • 快適に使えるレスポンスの早さかどうか
  • 負荷テスト
    • 負荷の許容できる限界値を測る
  • ユーザビリティテスト
    • ユーザーが使いやすいかどうか
  • セキュリティテスト
    • 利用が安全かどうか

工程によるソフトウェアテストの分類

テスト計画

  1. テスト対象の決定
  2. テストのやり方の決定
  3. 役割分担の決定
  4. 準備対象の決定
  5. スケジュールの決定
  6. 管理方針の決定

テスト設計

  1. インプット情報の入手と確認
  2. テスト設計方針の決定
  3. テストケースの作成

テスト実行

  1. テスト環境の準備
  2. テストデータの準備
  3. 実行手順の確認
  4. ツールの準備
  5. 実行スケジュールの作成
  6. 実行

テストレベルによるソフトウェアテストの分類

ソフトウェアテストでは、まずそのソフトウェアの動作における最小単位でテストを行います。これを単体テスト、あるいはユニットテストと言います。

次に、複数の動作を組み合わせて結合テストを行います。結合テストでは連続する動作が正常に動くかどうかを確認します。

開発の終盤で行われるのが、ソフトウェアの全体の流れを確認するシステムテストです。システムテストでは、ソフトウェアだけでなくハードウェア部分も包括してテストを行います。

技法による分類

テスト技法で分類すると、ブラックボックステストホワイトボックステストに分けられます。

ブラックボックスは「中身が見えない」という意味で、ブラックボックステストではソフトウェアの中身を見ずに、入力した内容に対して期待した出力があるかを確認します。

一方、ホワイトボックスではソフトウェアの中身も確認し、入力から出力の間にどのような処理が行われているかも併せて確認します。

引用リスト

ソフトウェアテストとは?種類や目的、重要な7原則を紹介

【メモ】Railsでフォロー機能

Railsチュートリアル

https://railstutorial.jp/chapters/following_users?version=5.0

すなわち、CalvinはHobbesをフォローしていても、HobbesはCalvinをフォローしていないといった関係性が成り立つのです。このような左右非対称な関係性を見分けるために、それぞれを能動的関係 (Active Relationship)と受動的関係 (Passive Relationship)と呼ぶことにします。

relationshipテーブルを作る
$ rails generate model Relationship follower_id:integer followed_id:integer

class CreateRelationships < ActiveRecord::Migration[5.0]
  def change
    create_table :relationships do |t|
      t.integer :follower_id
      t.integer :followed_id

      t.timestamps
    end
    add_index :relationships, :follower_id
    add_index :relationships, :followed_id
    add_index :relationships, [:follower_id, :followed_id], unique: true
  end
end

indexを作るよう設定する
add_index :relationships, [:follower_id, :followed_id], unique: true
ここでは指定した2つのカラムの組み合わせが一意になるよう設定している
 →あるユーザーが同じユーザーを2回以上フォローするのを防ぐため

rails db:migrateでテーブルを作成

能動的関係に対して1対多 (has_many) の関連付けを実装する

class User < ApplicationRecord
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
end

:active_relationshipsは仮の名前、メソッド名になる
class_name: "Relationship" 実態はRelationshipだよって明示してる。じゃないと存在しないactive_relationshipsテーブルを探しに行ってしまう。
foreign_key: "follower_id" 外部キーを指定。さもないとactive_relationships_idを探しに行ってしまう
dependent: :destroy あるユーザーが削除されたら、そのユーザーの関連も消す。relationshipsテーブルのレコードを消す。

リレーションシップ/フォロワーに対してbelongs_toの関連付けを追加する

class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
  validates :follower_id, presence: true
  validates :followed_id, presence: true
end

validates presence: trueで保存するとき空だとエラーでるようにする

ここまでで使えるようになったメソッド

メソッド 用途
active_relationship.follower フォロワーを返します
active_relationship.followed フォローしているユーザーを返します
user.active_relationships.create(followed_id: other_user.id) userと紐付けて能動的関係を作成/登録する
user.active_relationships.create!(followed_id: other_user.id) userを紐付けて能動的関係を作成/登録する (失敗時にエラーを出力)
user.active_relationships.build(followed_id: other_user.id) userと紐付けた新しいRelationshipオブジェクトを返す

followingの関連付け user.followingsになるようfollowingsにする。:sourceを使って

「following配列の元はfollowed idの集合である」ということを明示的にRailsに伝えます。

class User < ApplicationRecord
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  has_many :followings, through: :active_relationships, source: :followed
end

こんなことができるようになる 特定のユーザーがフォローしてるユーザーリストを配列のように使える

user.followings.include?(other_user) 居る?
user.followings.find(other_user) 取得
user.followings << other_user 追加
user.followings.delete(other_user) 削除

followingsで取得した集合からフォロー・アンフォローのメソッドを作る

# ユーザーをフォローする
  def follow(other_user)
    active_relationships.create(followed_id: other_user.id)
  end

# ユーザーをフォロー解除する
  def unfollow(other_user)
    active_relationships.find_by(followed_id: other_user.id).destroy
  end

# 現在のユーザーがフォローしてたらtrueを返す
  def following?(other_user)
    followings.include?(other_user)
  end

受動的関係を使ってuser.followersを実装する user.followersメソッドを追加する。フォロワーの集合を取得する。user.followingsの逆

class User < ApplicationRecord
  has_many :active_relationships,  class_name:  "Relationship",
                                   foreign_key: "follower_id",
                                   dependent:   :destroy
  has_many :passive_relationships, class_name:  "Relationship",
                                   foreign_key: "followed_id",
                                   dependent:   :destroy
  has_many :followings, through: :active_relationships,  source: :followed
  has_many :followers, through: :passive_relationships, source: :follower
end

この場合なら、has_many :followersを単数形にして自動的に外部キーfollower_idを探してくれるからsourceを省略しても動く。比較の為あえて書いてる。

ルーティングを設定する config/routes.rb

resources :users do
    member do
      get :followings, :followers
    end
  end

これで/users/1/following/users/1/followersを表してる

HTTPリクエス URL アクション 名前付きルート
GET /users/1/followings followings followings_user_path(1)
GET /users/1/followers followers followers_user_path(1)
resources :users do
  collection do
    get :tigers
  end
end

collectionを使うとidを含まなくなり、この例では/users/tigersのようになる

フォロワーの統計情報を表示するパーシャル app/views/shared/_stats.html.erb

<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.following.count %>
    </strong>
    following
  </a>
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
    followers
  </a>
</div>

@ u s e r がnilであればcurrent_userを代入してる

<%= render 'shared/stats' %>

レンダー

フォロー・フォロー解除のパーシャル app/views/users/_follow_form.html.erb

<% unless current_user?(@user) %>
  <div id="follow_form">
  <% if current_user.following?(@user) %>
    <%= render 'unfollow' %>
  <% else %>
    <%= render 'follow' %>
  <% end %>
  </div>
<% end %>

ユーザーをフォローするフォーム リレーションシップを作成 app/views/users/_follow.html.erb

<%= form_for(current_user.active_relationships.build) do |f| %>
  <div><%= hidden_field_tag :followed_id, @user.id %></div>
  <%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>

<%= hidden_field_tag :followed_id, @user.id %> hidden_field_tagで <input id="followed_id" name="followed_id" type="hidden" value="3" />を生成している

隠しフィールドのinputタグを使うことで、ブラウザ上に表示させずに適切な情報を含めることができます。

ユーザーをフォロー解除するフォーム リレーションシップを削除 app/views/users/_unfollow.html.erb

<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
             html: { method: :delete }) do |f| %>
  <%= f.submit "Unfollow", class: "btn" %>
<% end %>
<%= render 'follow_form' if logged_in? %>

適切な場所でレンダーする

followingアクションとfollowersアクション ルーティングに従って2つのアクションを作る renderで同じビューファイルを指定する app/controllers/users_controller.rb

class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
                                        :following, :followers]

  def following
    @title = "Following"
    @user  = User.find(params[:id])
    @users = @user.followings.paginate(page: params[:page])
    render 'show_follow'
  end

  def followers
    @title = "Followers"
    @user  = User.find(params[:id])
    @users = @user.followers.paginate(page: params[:page])
    render 'show_follow'
  end
end

フォローしているユーザーとフォロワーの両方を表示するshow_followビュー app/views/users/show_follow.html.erb

<% provide(:title, @title) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <%= gravatar_for @user %>
      <h1><%= @user.name %></h1>
      <span><%= link_to "view my profile", @user %></span>
      <span><b>Microposts:</b> <%= @user.microposts.count %></span>
    </section>
    <section class="stats">
      <%= render 'shared/stats' %>
      <% if @users.any? %>
        <div class="user_avatars">
          <% @users.each do |user| %>
            <%= link_to gravatar_for(user, size: 30), user %>
          <% end %>
        </div>
      <% end %>
    </section>
  </aside>
  <div class="col-md-8">
    <h3><%= @title %></h3>
    <% if @users.any? %>
      <ul class="users follow">
        <%= render @users %>
      </ul>
      <%= will_paginate %>
    <% end %>
  </div>
</div>

provideメソッドでパラメータを引き渡し
<% provide(:title, "Home") %>
yieldメソッドで受け取る
<title><%= yield(:title) %></title>
gravatar_for gravatarっていうオンラインにプロフィールを置くサービスがあるらしい
userインスタンスを使ってgravatarから対応するプロフィール引っ張ってくるようにgravatar_forヘルパーメソッドを定義するっぽい。
gravatarで実際に登録とかしたら詳しくわかりそう。

フォロー・フォロー解除ボタンを動作させる リレーションシップの作成と削除なので、relationshipsコントローラが必要

[Follow] / [Unfollow] ボタンを動作させるためには、フォームから送信されたパラメータを使って、followed_idに対応するユーザーを見つけてくる必要があります。その後、見つけてきたユーザーに対して適切にfollow/unfollowメソッドを使います。

$ rails generate controller Relationships

Relationshipsコントローラ app/controllers/relationships_controller.rb

class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
    user = User.find(params[:followed_id])
    current_user.follow(user)
    redirect_to user
  end

  def destroy
    user = Relationship.find(params[:id]).followed
    current_user.unfollow(user)
    redirect_to user
  end
end

メモ

ルーティングのmember

https://railsguides.jp/routing.html#%E3%83%A1%E3%83%B3%E3%83%90%E3%83%BC%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B
こう書いたら
get '/photos/123/preview', to: 'photos#preview'ができるような感じ。
params[:id]を渡す。
URLヘルパーも作られるpreview_photo_urlpreview_photo_path

resources :photos do
  member do
    get 'preview'
  end
end

GETリクエストとそれに伴う'/photos/1/preview'を認識し、リクエストをPhotosコントローラのpreviewアクションにルーティングし、リソースid値をparams[:id]に渡します。
同時に、preview_photo_urlヘルパーとpreview_photo_pathヘルパーも作成されます。
patchputpostdelete必要であればその都度書く。

なんで関連付けするの?

https://railsguides.jp/association_basics.html

アソシエーション(関連付け)すると

class Author < ApplicationRecord
  has_many :books, dependent: :destroy
end

class Book < ApplicationRecord
  belongs_to :author
end
本を1つ作る
@book = Book.create(published_at: Time.now, author_id: @author.id)

作者を削除、書いた本も削除する
@books = Book.where(author_id: @author.id)
@books.each do |book|
  book.destroy
end

これが、こうなる。シンプルになる。

本を1つ作る
@book = @author.books.create(published_at: Time.now)

作者と書いた本を削除
@author.destroy

has_many :throughの関連付け

多対多の時に、中間テーブルを使う。その時に使う。次の例ではAppointmentが中間テーブル

class Physician < ApplicationRecord
  has_many :appointments
  has_many :patients, through: :appointments
end

class Appointment < ApplicationRecord
  belongs_to :physician
  belongs_to :patient
end

class Patient < ApplicationRecord
  has_many :appointments
  has_many :physicians, through: :appointments
end

直接相手のモデルから情報取りに行こうとすると多対多になるから:throughで中間テーブルを経由して~って言ってる。
Railsチュートリアルのフォロー機能実装の場合はさらに自己参照だからややこしいことに・・・
https://railsguides.jp/association_basics.html#%E8%87%AA%E5%B7%B1%E7%B5%90%E5%90%88
自己結合のとこも参考になる。

双方向関連付け

a = Author.first
b = a.books.first
a.first_name == b.author.first_name # => true
a.first_name = 'David'
a.first_name == b.author.first_name # => true

Active Recordは関連付けの設定から、これら2つのモデルが双方向の関連を共有していることを自動的に認識します。以下に示すとおり、Active RecordはAuthorオブジェクトのコピーを1つだけ読み出し、アプリケーションをより効率的かつ一貫性のあるデータに仕上げます。

しかし、:through, :foreign_keyを使った場合は自動的に認識されない

a = Author.first
b = a.books.first
a.first_name == b.writer.first_name # => true
a.first_name = 'David'
a.first_name == b.writer.first_name # => false

Active Recordは:inverse_ofオプションを提供していて、これを使うと双方向の関連付けを明示的に宣言できます。

class Author < ApplicationRecord
  has_many :books, inverse_of: 'writer'
end

class Book < ApplicationRecord
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

:foreign_key

Railsの慣例では、相手のモデルを指す外部キーを保持しているjoinテーブル上のカラム名については、そのモデル名にサフィックス_idを追加した関連付け名が使われることを前提とします。:foreign_keyオプションを使えば、外部キーの名前を直接指定できます。

:inverse_of

:inverse_ofオプションは、その関連付けの逆関連付けとなるhas_many関連付けまたはhas_one関連付けの名前を指定します。

class Author < ApplicationRecord
  has_many :books, inverse_of: :author
end

class Book < ApplicationRecord
  belongs_to :author, inverse_of: :books
end