Galapagos Tech Blog

株式会社ガラパゴスのメンバーによる技術ブログです。

「マインドマップを使ったテスト分析設計勉強会」参加レポート〜後編〜

こんにちは、この前ニチアサの録画に失敗したテストチームの文豪です。

今回は「マインドマップを使ったテスト分析設計勉強会」参加レポートの後編です。

gtech.hatenablog.com

マインドマップを使ったテスト分析設計勉強会」

■日時
2018/05/16 19:30~21:30
■場所
株式会社ガラパゴス
■登壇者
セッション1:とののさん@tonono2587
セッション2:鈴木三紀夫さん(@mkoszk
Twitterハッシュタグ
#NaITE_mm

nagasaki-it-engineers.connpass.com

後輩あべべと一緒に参加してきました。
後編では、セッション2「テスト要求分析を語る夕べ(出張編)」についてレポートしていきたいと思います。

なお、あべべと同じくテスト設計の経験はほぼゼロです。最近始めたところです。
ご承知おきください。

その前に

文豪もマインドマップ書いたので、ちらっとレポートします。
お題は前回記事を参照ください。

書いてみた

文豪は数年前に『マインドマップから始めるソフトウェアテスト』を読んでいるので、おぼろげな記憶を頼りにとりあえず書いてみました。

f:id:glpgsinc:20180521150714j:plain
メインブランチはボタン3つと、画面表示です。
(フォームと書いてあるのはそのとき「画面表示」という言葉がぱっと出なかったからです)
・これはできるべき
・こんなバグありそう
・ユーザーのしそうな操作
の3点を考えながら書きました。
お題の数取器はWEBアプリなのですが、普段WEBアプリのテストをしないのでその辺は難しかったです。

共有後に、目から鱗だったことは以下です。
・十進法かを確認する
・+ボタンを押して+1されるのはいつか:ボタンを押したとき?放したとき?
・ボタンのどこを押しても反応するか

感想

・文豪の中には「メインブランチはボタンのような画面を構成するもので出す」という発想しかありませんでした。なので品質特性などでメインブランチを出す、というのはよい気付きでした。
・参加者の方から「論理的に文章で考えられない人にマインドマップは向いているのでは」というようなお話がありました。文豪はわりと(論理的かはともかく)文章でざくざく書くタイプなので、もしかしたらマインドマップは得意でないかも。でもテスト分析には活用できそうなので、今後使っていけたらと思いました。
マインドマップの略はMM

それでは

本題の「テスト要求分析を語る夕べ(出張編)」のレポートです

話し手は鈴木三紀夫さん、セッション1で使用した『マインドマップから始めるソフトウェアテスト』の共著者のお一人です。
テーマはそのものずばり「テスト要求分析」でした。
が、文豪はテスト設計初心者なので、MUZUKASHIと思いながら聞いていました。

主に以下のお話をしていただきました。
・テスト分析
・テストプロセス
・テスト要求分析プロセスのアウトプット
ステークホルダーの分析
実際の資料はまだまだ続きがあったのですが、時間内に終わりませんでした。
6月26日頃に続きを発表してくださるかも、とのことでした。
参加せねば(((((((((((っ・ω・)っ ブーン

というわけでお聞きしたお話の中で、気になったものを列挙していきます。ざっくりです。

*「分析」という言葉の定義について

辞書では「分けること」を指している
→本当に「分ける」だけが「分析」か
→論理学の本では「分けて」「再構成すること」
→分析は「分ける」だけでなく「再構成すること」も必要と考える

*「テスト分析」の指すもの

以下の3種類を指している
①テスト結果の分析のこと
②第三者検証会社が仕事を始める前に行う分析のこと
③テスト設計の前のアクティビティ

①との混合を避けるため、③のことは「テスト要求分析」と呼んでいるそうです。
(今回のお話の主題は③になります)

*「プロセス」という言葉の定義について

JSTQBの用語集では「相互関係のある活動のセット」
JSTQB認定テスト技術者資格-シラバス(学習事項)・用語集-

辞書では「物事を進める手順、変化するときの経過や過程」
鈴木さんは「入力から出力に変換する諸活動の連鎖」と説明しているそうです。

*テストプロセスとは

JSTQBの用語集では、「テスト計画、コントロール…によって構成される」とあり、プロセスの構成要素を列挙して定義づけている
・テストプロセスは時代とともに変わっていく
初めの頃は「実行」「報告」という2つのアクティビティしかなかったが、今は「計画」「要求分析」〜「評価」まで増えた。

*テスト要求分析についての解説は多くない


*テスト要求分析のアウトプット

・アウトプットはテスト条件あるいはテスト観点
・テスト条件とは「◇◇な場合、〇〇すると、■■となる」と書けるもの
 ◇:条件、状態、前提
 ◯:トリガー、イベント、操作
 ■:期待結果
・テスト観点とは「テストしたいと思うモノやコト」

*テスト分析を始める前に

ステークホルダーを把握し、重要度・影響度でステークホルダーを分析する
・表よりオニオンモデルの方が出てきやすい
ステークホルダーからユーザー要求を獲得する
→ユーザー要求が多くなってきたらリッチピクチャーの出番
ユーザーの「思い」は、1つだけではない (1/2) - ITmedia エンタープライズ

感想

・2003年頃にはまだ「テスト設計」という言葉は一般的ではなかったらしく、ある会社が「テスト設計」という言葉を使い始めたら、テストケースの書き方が変わり欠陥が激減したそうです。言葉1つで、ここまで変わるんだなと思いました。
・バックワードチェイニングなる人材育成の方法論があるそうです。よさげです。
・リッチピクチャーすごい
・発表資料がスライドでなくドキュメントでした。とってもよいと思ったので、機会があれば文豪もドキュメントで資料作ろうと思いました。
・MUZUKASHIとは思いましたが、楽しかったです。

マインドマップ、テスト要求分析についてお話をお聞きし、いくつか学びがありました。テスト設計のお勉強を始めたところなので、これから活かせていけたらと思います。

閲覧いただきありがとうございました!


ところで

ガラパゴスではエンジニアを募集しています。興味を持たれた方はぜひ弊社の採用ページをご覧ください。

www.glpgs.com

「マインドマップを使ったテスト分析設計勉強会」参加レポート〜前編〜

こんにちは、テストチームのあべです。
テストエンジニア歴はもうすぐ1年のひよっこです。

今回は、発表者が先輩のとののさん(@tonono2587)、会場が自社ということもあり、「マインドマップを使ったテスト分析設計勉強会」に参加してきました!

マインドマップを使ったテスト分析設計勉強会」

■日時
2018/05/16 19:30~21:30
■場所
株式会社ガラパゴス
■登壇者
セッション1:とののさん(@tonono2587)
セッション2:鈴木三紀夫さん

nagasaki-it-engineers.connpass.com

NaITE(長崎IT技術者会)のマインドマップ本読書会SIGメンバーさんの活動報告会を兼ねた勉強会、という形でした。

今回の勉強会はセッションが2つありました。ので、一緒に参加した先輩の文豪さんと2回に分けてレポートします!

今回は、セッション1「【使ってみた】テストにマインドマップ」です(^^)
主な内容はこちら

ちなみに

あべは、テスト設計、分析、テストケース作成の経験がありません。
1年間、テスト実施者として経験を積んできました。

また、「マインドマップから始めるソフトウェアテスト」は未読で参加しています。
マインドマップについても、勉強会当日にネットで20分ほど学習しただけの、全くの素人です。もちろん、書いたこともありませんでした。

そんな、ソフトウェアテストの経験もマインドマップの知識も浅い、
初心者のレポートであることをご承知おきくださいm( )m

SIG活動報告

まずはSIGメンバーの活動報告がありました。

メンバーの参加動機

■テスト業務の改善

  • 1個の改修でどこに影響があるのかすぐにわからない
  • テスト技法自体はなんとなくわかってきたが、設計にもっと活かしたい
  • 要求分析的な部分も考えたい
  • バグ分析もできたらいいな

活動内容

マインドマップから始めるソフトウェアテスト」の読破&実践を通して、
日々の活動内容の改善を目指す

マインドマップを描いているうちにわかったこと

  • 内容を俯瞰してみられる
  • 自分の考える癖が見える
  • 考えられることが増える

マインドマップからテストケースに落としてみてわかったこと

マインドマップを描いてみる

描いてみましょう!ということで、以下のお題でマインドマップを描いてみることになりました。

■お題

数取器( 数取器|ブラウザ・スマホですぐ使えるWEBアプリ
使い方
「+」ボタン:クリックする度に数字が+1される
「-」ボタン:クリックする度に数字が-1される
「Clear」ボタン:クリックすると数字が0にもどる

■作成時間:10分

参加者のみなさんがもくもくと書き進めて行くなか、私はいきなりつまずきました。

・・・なにをかいていいかわからない!

テーマはもちろん「数取器」です。
しかしその先に伸ばすべきメインブランチに何を書けばいいかわかりません。
前述のとおり、私はテスト分析も設計も、テストケース作成の経験もありません。
メインブランチにはなにをかくべき?ボタン?品質特性?技法の名前??なにが正解なんだ???
完全に、パニックです。
ですが、さすがに何も描かないのは良くないので、とりあえずメインブランチをボタンにして書いてみました。

そうなるまでのあべの頭の中の流れは、
品質特性では書ける気がしない。。
→「表示」「機能」みたいな要素でも多分うまくまとめられないな〜
→とりあえずボタンごとにどんなテストがしたいか書き出してみよう!
という感じでした。

それがこちら↓ f:id:glpgsinc:20180517150925j:plain 最初の5分くらいは完全にパニックになっていたので、ブランチもかなり少ないしまとまっていません(笑)

マインドマップを共有する

各自で書いた後、周りの人と各々のマインドマップを共有しました。
そのとき、参加者の方の意見で「なるほど!」と思ったのは、

  • 「+」ボタンの表示範囲が大きかった→ボタンのどこでも反応する?
  • 「-」ボタンを押し続けるとマイナスになるのか
  • 更新をかけたときは値がリセットされるのか
  • ボタンを長押しするとどうなるのか

またメインブランチも、私のようにボタンだったり、「機能」「表示」などの要素だったり、人によってかなり違いがありました。

マインドマップをテストに使うコツ

作成→共有後、とののさんマインドマップをテストに使うときに、感じたこと、それを解決するコツを紹介してくださいました。

■メインブランチを出すのが難しい
  • メインブランチをいろんな方法で出してみる。
    メインブランチの出し方
    • 品質特性
    • 利用者の立場:実装/ユーザー
    • 意地悪漢字
    • 業種/職種/経験
    • モデルを基に発想:入力>変換>出力
■ブランチを広げるのが難しい
  • 気にしすぎないで思いついたことはどんどん描く(階層/構造/見た目/内容...)
  • ブランチのそばにメモを書く
■「手描き」自体難しい(後でメインブランチを入れ替えたくなる/書ききれない)
  • ツールをつかってみる (WEB/アプリ/付箋)
  • 描ききれないところはメインブランチごと別紙へ

まとめ

  • マインドマップを書くのにはコツがいる
    メインブランチの出し方、マインドマップ全体の整理の仕方
  • 始めやすい、楽しくできる
  • 繰り返し描くうちに、マインドマップの使い方自体上手くなる、慣れてくる
  • テスト観点が多くげられるようになってくる
    描きながらどう整理するか、考える順番をどうするか
  • 視覚的にまとまるので、他人と共有しやすい 共有することで、別の考えも取り入れられる

感想など

  • はじめやすい
    マインドマップを描いたことがなくても、なんとかそれっぽい形に描くことができました。
    必要なのは紙とペンだけなので、思いついたときにすぐはじめられていいなと思いました。
  • 活用できそう
    ソフトウェアテストの分析以外にも、自己分析や旅の計画など、いろいろなことに活用できそうだなと思いました。
  • 自分がどんなふうに考えているかわかる
    頭のなかでぐるぐる考えるより、自分がどんな順番で考えてるかが目に見えてわかると、自分の考え方や癖(とののさんも仰っていましたね)が見えてくる感じがありました。自分の考え方の良いところ、悪いところを直すきっかけにもなりそうです。
  • 知識がある程度必要
    これはマインドマップを使わない場合でも当然なのですが、ソフトウェアテストの知識はある程度必要だなと感じました。知識がないと、今回の私のように、前に進めないままパニックということになりかねません。 ソフトウェアテスト以外でマインドマップを使うときも、テーマについてはある程度知っておいたほうがいいなと感じました。

さいごに

マインドマップ」とはなにか、テスト分析に使うとどうなるのか、を学ぶことができました。
今回の勉強会をきっかけに、マインドマップについて、テスト分析についてを学びます! ゆくゆくはマインドマップを使ってテスト分析できるよう頑張ります(^^)

ここまで読んでいただき、ありがとうございました!

文豪さんの後編をお楽しみに!

ところで

ガラパゴスではエンジニアを募集しています。興味を持たれた方はぜひ弊社の採用ページをご覧ください。

www.glpgs.com

サーバーレスなAtomリーダーを作ってみた

こんにちは。iOSチームの高橋です。好きな天気記号は煙霧です。

最近仕事の空き時間に簡単なAtomフィードのリーダーを作ったので、そのお話をします。

構成

図にするとこういう感じです:

f:id:glpgsinc:20180516150140p:plain

サーバーレスということで、フィードを保存するストレージにはDynamoDBを使っています。 DynamoDBに登録されたフィードを定期的にLambdaでクロールして解析したエントリ情報をDynamoDBに保存するようにしています。 保存されたエントリはAPI Gateway経由で取得できるようにして、S3に置かれたhtmlからajaxで叩いてVue.jsで描画する形です。

筆者はDynamoDBやらVue.jsやらの経験がなかったので、この記事はそのあたりの奮闘記になります。

DynamoDBとの闘い

DynamoDBはいわゆるNoSQLなので、検索の柔軟性をいささか犠牲にしている部分があります。 具体的にはあらかじめ指定されたキーでしかフィルタやソートができません。

筆者はこのあたりで心が折れかけたのですが(RDBの世界に帰りたい!)、とりあえずテーブルを二つ作って、一方をフィード格納用、もう一方をエントリ格納用としました。

キーの設定

フィード格納用テーブルはシンプルにフィードのIDとURL(とタイトルなどのメタデータ)を保存するものです。 こちらは特にフィルタしないのでscanで全件取得する方針にしました。なのでIDをハッシュキーにしておきます。

問題はエントリ格納用テーブルです。フィードIDでフィルタして、更新時刻でソートしたいので、 フィードIDをハッシュキーに、更新時刻をレンジキーにしたいのですが、 フィードIDと更新時刻の組が一意になるとは限らない(レアケースではあると思いますが)ので、これらをキーとして使うことはできません。

かわりに、エントリIDをレンジキーにして、ローカルセカンダリインデックスとして更新時刻をソートキーにすることで解決しました。

Pythonからの呼び出し

こうしてできたDBへはAPI GatewayからLambdaでアクセスします。言語はPython3.6を選んだので、boto3を使ってこういう風にクエリできます:

dynamodb = boto3.resource('dynamodb')
entries_table = dynamodb.Table(os.environ['ENTRIES_TABLE_NAME'])
entries = entries_table.query(
    IndexName='LSI', # ローカルセカンダリインデックス
    ScanIndexForward=False, # 降順
    Limit=10,
    KeyConditionExpression=Key('feed_id').eq(feed_id))

ペジネーション

ここまで作ったところでふたたびNoSQLの壁が立ちはだかります。DynamoDBではSQLのようにOFFSETを使うことができず、 かわりに「前回のクエリで返ってきた最後の値」を引数に渡すことでそこから先のエントリだけを取得するようになっています。 今回のように順番にペジネーションする場合はフロントエンドで値を持ち回る必要があるだけですが、途中のページに飛びたい場合などには問題になるでしょう。

Serverless Framework

テーブル設計とLambdaに載せるコードが準備できたところで、Serverless Frameworkを使ってズドン!と作ってしまいます。 この子はYAMLにLambda関数の設定や作りたいリソースを書いてやると自動でLambda関数やAPI Gatewayのエンドポイントやその他リソースを作ってくれるいい子です。

今回の設定ファイルはこういう感じになります(だいぶ端折っていますが):

service: feed-reader
provider:
  name: aws
  runtime: python3.6
  region: ap-northeast-1
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource:
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.FEEDS_TABLE_NAME}"
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.ENTRIES_TABLE_NAME}"
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.ENTRIES_TABLE_NAME}/*"
  environment:
    FEEDS_TABLE_NAME: ${self:service}-feeds-${opt:stage, self:provider.stage}
    ENTRIES_TABLE_NAME: ${self:service}-entries-${opt:stage, self:provider.stage}
functions:
  crawler:
    handler: handler.crawl
    events:
      - schedule: rate(6 hours)
  createFeed:
    handler: handler.create_feed
    events:
      - http:
          path: feeds/create
          method: post
          cors: true
resources:
  Resources:
    feedsTable:
      Type: "AWS::DynamoDB::Table"
      Properties:
        AttributeDefinitions:
          -
            AttributeName: id
            AttributeType: S
        KeySchema:
          -
            AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:provider.environment.FEEDS_TABLE_NAME}

functionseventsのところでscheduleを指定するとCloudWatchから定期的にLambda関数を起動することができます。 また、httpを指定するとAPI Gatewayのエンドポイントを作ってくれます。便利!

pipでパッケージを入れたい場合

ところで、Lambdaで実行する関数の中でサードパーティのパッケージを使いたいときがあります。 こういう場合、パッケージをダウンロードしてきてzipに含めて一緒にアップロードするなどの手間が発生するのですが(しかも場合によってはLambdaと同等の環境を再現する必要があったりもする)、 Serverlessにはそれを解決する便利なプラグインがあります。

GitHub - UnitedIncome/serverless-python-requirements: ⚡️🐍📦 Serverless plugin to bundle Python packages

このプラグインを入れて、YAMLファイルにちょっと追記して、requirements.txtに必要なパッケージを書いておくと自動でLambda関数に含めてくれます。 また、今回はネイティブコンパイルの必要なパッケージは使っていないのですが、必要ならばDockerコンテナを立ち上げて環境を用意してくれるようです。便利!

Vue.jsとなかよくなる

さて、バックエンドの準備が整ったので、フロントエンドの作成に入ります。 APIを叩くのにはaxiosを使うことにしたので、たとえばこういう感じでフィード一覧を取得することができます:

axios.get('/feeds').then(function (response) {
  console.log(response.data.feeds);
});

こうして得られたフィードやエントリをVue.jsを使って描画していきます。

Vue.jsというのは、コンポーネントシステムやデータバインディングを用いてシンプルな記述から動的にWebページを生成するフレームワークです。 公式のチュートリアルがとても充実していて特にハマったところもないので特筆すべきことはないのですが、たとえば、

<div id="app">
  <ul>
    <li v-for="feed in feeds">{{ feed.title }}</li>
  </ul>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    feeds: []
  }
});

axios.get('/feeds').then(function (response) {
  vm.feeds = response.data.feeds;
});

のように記述すると、GET /feeds APIから返ってきたフィード情報がリスト表示されるようになっています。 ここではvm.feedsがHTMLのfeedsにバインドされており、変更が検知されるとDOMを書き替えて表示を更新します。 v-forというのはVue.jsの独自属性で、その名の通り配列のfor文のように要素を繰り返すことを表しています。

さらにコンポーネントを活用することで、ひとまとまりのHTML片を独自タグに変換し、HTMLの記述をシンプルにすることができます。たとえば、

components: {
  'entry': {
    props: ['entry'],
    template: '<article><h1>{{ entry.title }}</h1><div v-html="entry.content"></div></article>'
  }
}

のようにコンポーネントを定義しておくと、HTML側では

<entry :entry="entry"></entry>

と書くだけで、templateで指定したHTMLを生成してくれます。いい子ですね。

S3にアップロード

あとはこうしてできたWebページ一式をS3の適当なバケットに上げて、Webホスティングを有効にすれば完成です。簡単ですね!

認証をかける

こうしてできたAtomリーダーですが、自分以外からのアクセスを制限したいと思います。 このとき可能な方法はいくつかあると思うのですが、今回はAPI GatewayAPIキーを設定する方法をとりました。 そういう場合もServerless Frameworkに任せることができて、YAMLファイルに

provider:
  # ...
  apiKeys:
    - feedReaderAPIKey
# ...
functions:
  # ...
  createFeed:
    # ...
    events:
      - http:
          # ...
          private: true

と書くだけで自動的にAPIキーを生成してAPI Gatewayに設定してくれます。

生成されたAPIキーはデプロイ時にコンソールに表示されるので、axiosのほうで

axios.defaults.headers = { 'x-api-key': 'XXXXXXXXX' };

などと書いてやればaxiosからのリクエストに自動的にAPIキーが付加されます(実際には直書きするとまずいので、ローカルストレージに格納したものを読み出しています)。 便利ですね。

ハマったことなど

基本的には便利なフレームワークに助けられてスッと作ることができたのですが、いくらかハマったところがあったので書いておきます:

DynamoDB

上にも書きましたが、DynamoDBのキー概念に馴染むのにけっこう手間が掛かりました。いまでもこれが正解なのかよくわかっていません(でもこれ以外にどうすれば?)。 結局詳細はよくわからなかったので、限りなくシンプルなテーブル構造を保つためにいくつかの機能(未読件数表示とか)を落とすことになりました。今後の課題です。

XHTMLとHTML

Atomフィードのエントリのcontent属性が'xhtml'の場合(そんなことをしている例は筆者自身のblog以外に見つからなかったのですが)、その部分はXMLとして解釈され、XHTMLとして独自の名前空間を持ちます。 これが問題で、Python標準ライブラリのXML解析器でtostringを呼ぶと、その部分のXHTMLタグすべてに名前空間接頭辞がつくことになります。 これをそのままWebページに流し込んでも、未知のタグとして扱われるのでマークアップが機能しません。

これを解決するために、Webページ自体をXHTMLにしてしまう、という案も考えたのですが、そうすると今度はHTMLで書かれたフィードが有効なXMLではないケースに対応できなくなります。

最終的には、XML解析の段階でタグをイテレートして、タグ名から接頭辞を消去することで解決しました。

tree = ET.ElementTree(element=list(content)[0])
for el in tree.iter():
    el.tag = el.tag.replace('{http://www.w3.org/1999/xhtml}', '')
content_text = ET.tostring(tree.getroot(), encoding='unicode', method='html')

まとめなど

ということで、DynamoDBとServerless FrameworkとVue.jsを使ったサーバーレスAtomフィードリーダー作成について書きました。 普段はiOS開発をやっているので、もっと手こずるかな〜と思っていたのですが、案外簡単に(実質三日くらい?)作ることができたのでよかったです。 新しい技術を覚えてゆくのはやっぱり楽しいですね。

ところで

ガラパゴスではエンジニアを募集しています。興味を持たれたかたはぜひ採用ページをご覧ください。

www.glpgs.com