自動テスト - Rspec
SOULs フレームワークでは テスト
と共に開発を進めます。ここでは souls コマンド及び、Rspec を使用します。
テストフレームワーク - RSpec
SOULs API/Worker では RSpec をテストフレームワークとして使っています。
RSpec
https://github.com/rspec/rspec
SOULs フレームワークでは souls g
コマンドにより、
自動で CRUD のテストが生成されます。
テストディレクトリは ./spec/
以下に主に、
factories
, models
, queries
, mutations
, resolvers
, policies
の 6 つに分類されます。
それぞれの役割をみてみましょう。
RSpec - Queries
queries
内のディレクトリでは GraphQL Query に対するテストを定義します。
SOULs フレームワークにはデフォルトで以下の user_spec.rb
ファイルが含まれています。
RSpec.describe("User Query テスト") do
describe "User データを取得する" do
let!(:user) { FactoryBot.create(:user) }
let(:query) do
data_id = SOULsApiSchema.to_global_id("User", user.id.to_s)
%(query {
user(id: \"#{data_id}\") {
id
uid
username
screenName
lastName
firstName
lastNameKanji
firstNameKanji
lastNameKana
firstNameKana
email
tel
iconUrl
birthday
}
}
)
end
subject(:result) do
SOULsApiSchema.execute(query).as_json
end
it "return User Data" do
begin
a1 = result.dig("data", "user")
raise unless a1.present?
rescue StandardError
raise(StandardError, result)
end
expect(a1).to(
include(
"id" => be_a(String),
"uid" => be_a(String),
"username" => be_a(String),
"screenName" => be_a(String),
"lastName" => be_a(String),
"firstName" => be_a(String),
"lastNameKanji" => be_a(String),
"firstNameKanji" => be_a(String),
"lastNameKana" => be_a(String),
"firstNameKana" => be_a(String),
"email" => be_a(String),
"tel" => be_a(String),
"iconUrl" => be_a(String),
"birthday" => be_a(String)
)
)
end
end
end
この Query Spec は以下の GraphQL Query に対応しています。
SOULs フレームワークではデータベースの ID を直接表示せずに、暗号化した UUID を使って情報を管理します。
global_id_field
について初めてのかたはこちらの記事を参考にしてください。
そのため、
_, data_id = SOULsApiSchema.from_global_id(args[:id])
のように ID を変換しています。
RSpec - Mutations
mutations
内のディレクトリでは GraphQL Mutation に対するテストを定義します。
SOULs フレームワークにはデフォルトで以下の user_spec.rb
ファイルが含まれています。
RSpec.describe("User Mutation テスト") do
describe "User データを登録する" do
let(:user) { FactoryBot.attributes_for(:user) }
let(:mutation) do
%(mutation {
createUser(input: {
uid: "#{user[:uid]}"
username: "#{user[:username]}"
screenName: "#{user[:screen_name]}"
lastName: "#{user[:last_name]}"
firstName: "#{user[:first_name]}"
lastNameKanji: "#{user[:last_name_kanji]}"
firstNameKanji: "#{user[:first_name_kanji]}"
lastNameKana: "#{user[:last_name_kana]}"
firstNameKana: "#{user[:first_name_kana]}"
email: "#{user[:email]}"
tel: "#{user[:tel]}"
iconUrl: "#{user[:icon_url]}"
birthday: "#{user[:birthday]}"
}) {
userEdge {
node {
id
uid
username
screenName
lastName
firstName
lastNameKanji
firstNameKanji
lastNameKana
firstNameKana
email
tel
iconUrl
birthday
}
}
}
}
)
end
subject(:result) do
SOULsApiSchema.execute(mutation).as_json
end
it "return User Data" do
begin
a1 = result.dig("data", "createUser", "userEdge", "node")
raise unless a1.present?
rescue StandardError
raise(StandardError, result)
end
expect(a1).to(
include(
"id" => be_a(String),
"uid" => be_a(String),
"username" => be_a(String),
"screenName" => be_a(String),
"lastName" => be_a(String),
"firstName" => be_a(String),
"lastNameKanji" => be_a(String),
"firstNameKanji" => be_a(String),
"lastNameKana" => be_a(String),
"firstNameKana" => be_a(String),
"email" => be_a(String),
"tel" => be_a(String),
"iconUrl" => be_a(String),
"birthday" => be_a(String)
)
)
end
end
end
この Query Spec は以下の GraphQL Query に対応しています。
./app/grahpql/mutations/base/user/create_user.rb
RSpec - Resolvers
resolvers
内のディレクトリでは GraphQL Resolver に対するテストを定義します。
SOULs フレームワークにはデフォルトで以下の user_search_spec.rb
ファイルが含まれています。
RSpec.describe("UserSearch Resolver テスト") do
describe "削除フラグ false の User を返却する" do
let!(:user) { FactoryBot.create(:user) }
let(:query) do
%(query {
userSearch(filter: {
isDeleted: false
}) {
edges {
cursor
node {
id
uid
username
screenName
lastName
firstName
lastNameKanji
firstNameKanji
lastNameKana
firstNameKana
email
tel
iconUrl
birthday
}
}
nodes {
id
}
pageInfo {
endCursor
hasNextPage
startCursor
hasPreviousPage
}
}
}
)
end
subject(:result) do
SOULsApiSchema.execute(query).as_json
end
it "return User Data" do
begin
a1 = result.dig("data", "userSearch", "edges")[0]["node"]
raise unless a1.present?
rescue StandardError
raise(StandardError, result)
end
expect(a1).to(
include(
"id" => be_a(String),
"uid" => be_a(String),
"username" => be_a(String),
"screenName" => be_a(String),
"lastName" => be_a(String),
"firstName" => be_a(String),
"lastNameKanji" => be_a(String),
"firstNameKanji" => be_a(String),
"lastNameKana" => be_a(String),
"firstNameKana" => be_a(String),
"email" => be_a(String),
"tel" => be_a(String),
"iconUrl" => be_a(String),
"birthday" => be_a(String)
)
)
end
end
end
主要なオブジェクトに対してソートや検索など、 データの操作が必要な場合は Resolver 内に定義します。
RSpec - Factories
factories
内のディレクトリではデータベースの Model のテストデータを定義します。
SOULs API/Worker では Faker, FactoryBot, Gimei を使用してユーザーのテストデータを生成しています。
Faker
https://github.com/faker-ruby/faker
FactoryBot
https://github.com/thoughtbot/factory_bot
Gimei
https://github.com/willnet/gimei
実際にアプリをクライアントと共有する際にはできるだけ、 人間に見やすいデータに変換してあげましょう。
User
Model とリレーションにある Article
Model のファクトリーは
FactoryBot.define do
factory :article do
association :user, factory: :user
title { Faker::Book.unique.title }
body { Faker::Quote.matz }
thumnail_url { Faker::Internet.url }
public_date { Time.now }
association :article_category, factory: :article_category
is_public { false }
just_created { false }
slag { Faker::Internet.password(min_length: 16) }
tags { %w[tag1 tag2 tag3] }
is_deleted { false }
created_at { Time.now }
updated_at { Time.now }
end
end
association :user, factory: :user
という一行で User
Model とリレーションにあることを定義しています。
これにより、テストデータをユーザー Model から作成することなく即時に Article
Model を作成することができます。
SOULs コンソールを使って確認してみましょう。
$ souls c
FactoryBot を使って Article
Model のデータを作成してみます。
irb(main):001:0> FactoryBot.create(:article)
D, [2021-07-21T10:48:55.758530 2678] DEBUG -- : TRANSACTION (0.8ms) BEGIN
D, [2021-07-21T10:48:55.761804 2678] DEBUG -- : User Exists? (3.1ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "daniele@harber.net"], ["LIMIT", 1]]
D, [2021-07-21T10:48:55.765162 2678] DEBUG -- : User Create (2.0ms) INSERT INTO "users" ("uid", "username", "screen_name", "last_name", "first_name", "last_name_kanji", "first_name_kanji", "last_name_kana", "first_name_kana", "email", "tel", "icon_url", "birthday", "gender", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING "id" [["uid", "EuDrZrPvV8sNi"], ["username", "窪田 政伸"], ["screen_name", "walter"], ["last_name", "まつの"], ["first_name", "はると"], ["last_name_kanji", "佐伯"], ["first_name_kanji", "正之"], ["last_name_kana", "ホリ"], ["first_name_kana", "ミズノ"], ["email", "daniele@harber.net"], ["tel", "6761514902"], ["icon_url", "https://picsum.photos/200"], ["birthday", "1994-03-01"], ["gender", "金田 義弘"], ["created_at", "2021-07-21 10:48:55.762563"], ["updated_at", "2021-07-21 10:48:55.762563"]]
D, [2021-07-21T10:48:55.769609 2678] DEBUG -- : TRANSACTION (4.2ms) COMMIT
D, [2021-07-21T10:48:55.779854 2678] DEBUG -- : TRANSACTION (0.5ms) BEGIN
D, [2021-07-21T10:48:55.781527 2678] DEBUG -- : ArticleCategory Create (1.5ms) INSERT INTO "article_categories" ("name", "tags", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["name", "General Systems Vehicle"], ["tags", "{tag1,tag2,tag3}"], ["created_at", "2021-07-21 10:48:55.779023"], ["updated_at", "2021-07-21 10:48:55.779028"]]
D, [2021-07-21T10:48:55.784177 2678] DEBUG -- : TRANSACTION (2.5ms) COMMIT
D, [2021-07-21T10:48:55.785612 2678] DEBUG -- : TRANSACTION (0.5ms) BEGIN
D, [2021-07-21T10:48:55.788462 2678] DEBUG -- : Article Create (2.6ms) INSERT INTO "articles" ("user_id", "title", "body", "thumnail_url", "public_date", "article_category_id", "just_created", "slag", "tags", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING "id" [["user_id", 11], ["title", "I Sing the Body Electric"], ["body", "Because of the Turing completeness theory, everything one Turing-complete language can do can theoretically be done by another Turing-complete language, but at a different cost. You can do everything in assembler, but no one wants to program in assembler anymore."], ["thumnail_url", "http://nader.biz/hai"], ["public_date", "2021-07-21 10:48:55.772391"], ["article_category_id", 6], ["just_created", false], ["slag", "EeMnG7jCz9JnTtPc"], ["tags", "{tag1,tag2,tag3}"], ["created_at", "2021-07-21 10:48:55.784725"], ["updated_at", "2021-07-21 10:48:55.784731"]]
D, [2021-07-21T10:48:55.791332 2678] DEBUG -- : TRANSACTION (2.6ms) COMMIT
=>
#<Article:0x0000563d5614f7c8
無事作成することができました。
RSpec - Models
models
内のディレクトリではデータベースの Model のテストを定義します。
SOULs フレームワークにはデフォルトで以下の user_spec.rb
ファイルが含まれています。
RSpec.describe("User Model テスト", type: :model) do
describe "User データを書き込む" do
it "valid User Model" do
expect(FactoryBot.build(:user)).to(be_valid)
end
end
end
先程作成した、FactoryBot を使ってテストを進めます。 必要に応じて、Model にテストを追加していきましょう。