2016年2月16日火曜日

JSTQB Foundation Level 試験受けた

2016年2月13日(土)に JSTQB Foundation Level の試験を受けてきましたー。

ソフトウェアテストに関する試験です。

公式はこちら http://jstqb.jp/index.html

ソフトウェアテスト界では、有名な試験のようです。が、わたしは昨年の11月までは知らなかったです。
なぜ、この試験を受けようと思ったかというと、
品質があまりにも悪すぎる自分の会社への反抗心ですね。

ちょうど、昨年の10月に不毛なテストをやらされて、モチベーションがだだ下がりしたので
せめて自分だけは品質に対して高い意識を持ちたかった。
(不毛なテストについてはまた後日、書きたいです)

この試験、勉強する範囲は、基本情報などと比べると少なく楽ですが、やや勉強しづらいです。
ベンダー系の試験で、合格点が公表されていないどころか、過去問が世にほとんど出回っておりません。

試験を受ける際、解答用紙と一緒に問題を回収されます。家に帰るころには、どんな問題が出ていたのか、全く思い出せません。だから、問題について詳しいことが書けないのです。

合格発表はだいたい三ヶ月後らしいので、まだ受かったかどうかはわからないですが
自信はあるので、せめて勉強方法、費用など、書ける部分だけでも書きます。


【使ったお金】

試験代: 21,600円

本代: 6,436円
・ソフトウェア教科書 JSTQB Foundation 第3版 4,298円



・演習で学ぶソフトウェアテスト特訓150問 2,138円




合計: 28,036円。

ちょっと高いです。
浮かせられるとしたら、ソフトウェアテスト特訓150問です。
あると便利ですが、なくても問題ないです。

【勉強のために使ったもの】

・公式にあるシラバス http://jstqb.jp/syllabus.html
 これは、基本中の基本です。
 と言いつつ、わたしは試験の一週間前までシラバスは読んでいませんでした。
 実際に試験を受けてみると、暗記部分は、シラバスの理解(と暗記)が全てでした。

・ソフトウェア教科書 JSTQB Foundation 第三版
 わたしは紙の本ではなく、kindle版を購入し、一周さらっと読みました。
 わかりやすく書かれているため、読みやすかったです。
 内容も面白かったです。
 唯一の公認テキスト。
 シラバス、試験と同様の言い回し、表現で書かれているため非常に役にたちました。

・演習で学ぶソフトウェアテスト特訓150問
 わたしは時間があったので、少なくとも五周はやりました。
 もし、時間がなければ、この本はなくても構わないと思います。
 「ソフトウェア教科書 JSTQB Foundation 第三版」の問題を繰り返し解いたほうが実践向きです。

・確認問題集のサイト http://jstqb.web.fc2.com/drill/
 ここは、少しいやらしい問題が出ます。
 問題をきちんと読む癖がついていない人はやって損はないと思います。
 わたしは、はやとちりしてしまうタイプなので、
 しっかり問題を読んで、頭の中の知識を正しく引き出す練習に使いました。

・スクエアリング・サービス というサイト http://squaring.jp/index.jsp
 有料サービスと無料サービスがあり、わたしは無料サービスを使わせていただきました。
 本二冊では、問題の数に限りがあり、どうしても条件反射的に答えてしまうので
 上記の本の問題を覚えきってしまった後に使いました。
 必須だとは思いませんでした。

【具体的な勉強方法】

2016年1月4日から、約一ヶ月かけて「ソフトウェア教科書 JSTQB Foundation 第三版」を読みました。
正直、この期間はダラダラやっていたので、真面目に勉強している感じはなかったです。
お風呂に入りながら、「演習で学ぶソフトウェアテスト特訓150問」で読んだ部分の問題をさらっと解いていました。正答率はこの時点では、三割切ってました。
2月に入ってから、本格的に勉強らしい勉強をはじめました。
「ソフトウェア教科書 JSTQB Foundation 第三版」はkindle版を買っていたのでパソコンから読めたので、各章の後ろの問題すべて、スクリーンショットを撮り、問題と解説に分けエクセルにまとめました。
「演習で学ぶソフトウェアテスト特訓150問」、「ソフトウェア教科書 JSTQB Foundation 第三版」の問題をひたすら繰り返しました。
試験一週間前からは、問題が載っているサイトで問題を解きながら、シラバスを何度も読みました。
何度も読むなら、「ソフトウェア教科書 JSTQB Foundation 第三版」よりもシラバスのほうがいいです。
理由は、「ソフトウェア教科書 JSTQB Foundation 第三版」が約365ページに対し、シラバスが60ページ程度だからです。一度、理解してしまった後は、「ソフトウェア教科書 JSTQB Foundation 第三版」は暗記に使うよりは、辞書的に使うのがいいと思います。


【最終的な問題の正解率】
試験の前日に解いた問題の正解数です。参考までにどうぞ。

「ソフトウェア教科書 JSTQB Foundation 第三版」は各章の後ろの問題の数が93個あり、82個正解。
最後の章の模擬試験が、40問中、34個正解。

「演習で学ぶソフトウェアテスト特訓150問」は150問中、144問正解。

全部で283問中、260問正解なので、91%の正解率です。

【実際、試験はどんな感じだったのか】

思っていたよりも、やさしい問題が多かったように思います。
ひっかけのような、変な問題もあまりなかったです。
ただ、時間はギリギリでした。
見直しの時間をたっぷり取れるとは思わないほうがいいです。
問題が、3ページに渡る超大作があったりして、だいぶ時間と集中力を奪われます。
そういう大作が、6問くらいあったと思います。
具体的にいうと、デシジョンテーブルを使って、テストケースの数を答える問題。
状態遷移図を使ってテストケースの数を答える問題。
長い文章から、テストケースの数を答える問題。
ステートメントカバレッジの問題。(100%にするには、xとyになにいれたらいいか?っていうやつ)

学生さんだと、やや難しいかもしれないと思ったけれど、テスト経験者や、業務知識ある方なら、難易度はそれほど高くないと感じました。
ただ、情報がなさすぎて、試験までハラハラしますね。とっても。お値段が高いですし。



単純な4択でひとつ覚えている問題があるので書きます。

問題:
この中でイテレーティブインクリメンタル開発ではないのはどれか。

1.プロトタイピング
2.アジャイル
3.RUP
4.PSP

答えは多分PSPかな。
PSPなんて、全く聞いたことなく、消去法でしか答えられなくて、一番最初に迷った問題なので記憶に残った。

大作だろうが、単純な暗記だろうが、所詮は4択なので
これはないだろって選択肢にはバツをつけて
4択を3択に。3択を2択に。ってやるといいと思います。

2014年10月28日火曜日

- ポリモーフィズム - Java

三大機能、ラストは「ポリモーフィズム」です!

近頃は、無駄話が少なかったので、今日は少し前置きを長くします。

「カラカル」ってご存知でしょうか?
(ご存知の方は、すみません。知らないつもりで!)

聞いたことない!!って方に、まず画像を。















これを見て、なんだと思うか?
椅子や、オムライスには見えませんね。

動物に見えるはずです。
動物は動物でも、何に似てるかなー??と考えていきつくのは
おそらく、ネコ!(トラもかな)あたりですよね。

今日、学ぶ、ポリモーフィズムというのは
このカラカルをネコと捉えるテクニックです。

カラカルを人に、「四本足でー」、「胎生でー(卵を産まない)」、「夜行性でー」と長々と説明するよりも「ネコ科の動物だよ!」と一言伝えたら、おおよその姿形、習性はわかりますよね。

「厳密には、カラカル!だけど、ネコとして捉えよう!」というのがポリモーフィズムです。

その、ポリモーフィズムの利用に特殊な構文はない。(今まで学んできた、extendsやprivateのような)

今日は、自分だけの動物園を作るために、動物を沢山作ってみます。



package zoo;
// 継承の材料用のアニマル(動物)インターフェース
public interface Animal {

  void  eat();
  void sleep();

}

package zoo;
// アニマルインターフェースを継承したネコ科インターフェース
public interface Felidae extends Animal{
 void run();
}

package zoo;

public class Caracal implements Felidae{
 String name = "カラカル";
 public void eat(){
  System.out.println(this.name + "は、保護されているので、ご馳走をもらって食べている");
 }
 public void sleep (){
  System.out.println(this.name + "は、素敵な寝床で寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、すばしっこく走っている");
 }
}

package zoo;

public class Lion implements Felidae{

 String name = "ライオン";
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 public void sleep (){
  System.out.println(this.name + "は大きなイビキをかいて寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、狭いところでは、あまり走れない");
 }
}

package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の変数 c に Caracal インスタンス を格納
  Felidae c = new Caracal();

  // Felidae型の変数 l に Lion インスタンス を格納
  Felidae l = new Lion();

  c.eat();
  l.eat();

  c.sleep();
  l.sleep();

  c.run();
  l.run();

 }
}













クラス、インターフェースが沢山あってわかりづらいけれど、
ここで、重要なのは Mainクラス。








この一行が、何を表しているかが大事。

中身は ライオン !!! でも、 ネコ科 として捉える !!!

これは、継承の時に学んだ 「is - a の原則」と同じです。
(A is a B = A はBの一種である)

ライオン is a ネコ科 !になる。

なので、ここでも勿論、 is - a の原則から外れているものは代入できない。
たとえば 
Food f = new Lion();
Zoo z = new Tiger();
上の例は代入できない。

それでも、無理やり利用することは出来るのでは?
と思うかもしれないけど
抽象メソッドは全てオーバーライドしないと、エラーになってしまうので
is - a の原則を無視しているものは、中途半端なインターフェース、クラスになってしまう。
(作りたい抽象メソッドがあっても、子クラスで必要ないものは書くことができなくなる。)

しかし、この is - a の関係、人間ならば、感覚で理解出来るけれど
コンピュータはどうやって判断しているのか?

実は「extends」「implements」で判断している。
この二つは、継承関係を Java に教えるためのものでもあったのだ。


上のコードだと、通常の
Lion l = new Lion();
Felidae l = new Lion();
の違いがわからない。

少しだけコードを変えてみる。



package zoo;

public class Lion implements Felidae{

 String name = "ライオン";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 public void sleep (){
  System.out.println(this.name + "は大きなイビキをかいて寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、狭いところでは、あまり走れない");
 }

 // 新規追加メソッド
 public void fight(){
  System.out.println(this.name + "は、自由を求めて戦った");
 }
}

package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の変数 c に Caracal インスタンス を格納
  Felidae c = new Caracal();

  // Felidae型の変数 l に Lion インスタンス を格納
  Felidae l = new Lion();

  l.eat();

  l.sleep();

  l.run();

  l.fight();
 }
}


Lionクラスに
新しいメソッド fight を追加して
Mainクラスで fight を実行させようとしています。

しかし、これはエラーになります。



















エラーメッセージをよくみると
メソッドfight()は型Felidae で未定義です
と書いてます。

コンピュータからみると、中身はLionでも、完全にFelidaeに見えているようです。

・呼び出し側は、同一視
・呼び出される側は、自分に決められた動きをする


ちょっとした実験。


package zoo;

public class Lion implements Felidae{

 String name = "ライオン";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 public void sleep (){
  System.out.println(this.name + "は大きなイビキをかいて寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、狭いところでは、あまり走れない");
 }

 // 新規追加メソッド
 public void fight(){
  System.out.println(this.name + "は、自由を求めて戦った");
 }
}

package zoo;

public class Tiger extends Lion{

 String name = "トラ";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 public void sleep (){
  System.out.println(this.name + "は大きなイビキをかいて寝ている");
 }
 public void run(){
  System.out.println(this.name + "は、狭いところでは、あまり走れない");
 }

 // 新規追加メソッド
 public void fight(){
  System.out.println(this.name + "は、自由を求めて戦った");
 }
}


package zoo;

public class Main {

 public static void main(String[] args) {


  // Felidae型の変数 l に Tiger インスタンス を格納
  Felidae t = new Tiger();

  // Lion型の変数 t に Tiger インスタンス を格納
  Lion l = new Tiger();

  t.fight();
  l.fight();
 }
}

Lionクラスの下にTigerクラスを作ってみた。
(こういう is - a じゃないものが、やってはダメな例です。すみません!)

他にもちょこちょこっと変更があったけれど、ここで重要な Lion,Tiger,Mainのクラスのコードだけ載せます。

特に注目したいのがMainクラスの

// Felidae型の変数 l に Tiger インスタンス を格納
Felidae t = new Tiger();

// Lion型の変数 t に Tiger インスタンス を格納
Lion l = new Tiger();

この部分。

t.fight();
l.fight();

この実行結果は








どんな型に代入しようが、中身がどちらも Tiger だと、実行結果も Tiger になるということ。

一つ上の、エラーコードの例とこの例から言えることは

型の箱にないものは呼び出せないし(エラーになって、実行すらできない)、他がどうあれ中身の箱に入っているメソッドが呼び出され実行される。


ここで、一つ疑問。
Felidae型(fightメソッドをもっていない)の箱に入ってしまった Lion が Fight()メソッドを呼び出すにはどうしたらよいのか。

解決方法は「変数 l の中身を、 Lion であると捉えなおす」という方法。
そのために利用するのが「キャスト演算子」である。


package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の変数 l に Lion インスタンス を格納
    Felidae f = new Lion();

    f.eat();

    f.sleep();

    f.run();

    // キャスト演算子によって、強制的に f を Lion型の変数 l に Lion インスタンスを格納
    Lion l = (Lion) f;

    l.fight();

    }
 }





















このように、あいまいな型に入っている中身を、厳密な型に代入するキャストを
ダウンキャスト という。

本来、キャストはよほどの理由がない限る避けるもの。
なぜならば、失敗の危険が伴うから。

誤った代入を行った際は
「ClassCastException」
というエラーが出る。

これは、キャストにより強制代入の結果、「嘘の構図」になったため、強制停止せざるを得なくなった、という意味のエラーメッセージ。

誤った代入を未然に防ぐための演算子がある。
















この instanceof演算子は
if文と組み合わせて使ったりできる。


package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の変数 l に Lion インスタンス を格納
    Felidae f = new Lion();

    f.eat();

    f.sleep();

    f.run();

    // もし、 f の中身が Lionならば
    if (f instanceof Lion){
    // Lion型の変数 l に Lion インスタンスを格納
    Lion l = (Lion) f;

    l.fight();
    }
  }
 }


これなら、もし中身が違う場合はダウンキャストが行われないので、エラーを防げる。



ポリモーフィズムの利点

ここまで学んできたけれど、今のところ、利点といえるものは見当たらない。
それどころか、親クラスにはなくて、子クラスにしかないメソッドが使えなくなり、不便に感じる。


しかし、上手に利用するとポリモーフィズムの利点がわかってくる。

次のコードをみてみる。


package zoo;
// 継承の材料用のアニマル(動物)インターフェース
public interface Animal {

  void  eat();

}


package zoo;
// アニマルインターフェースを継承したネコ科抽象クラス
public abstract class  Felidae implements Animal{

 private int hungryPoint;

 public int  getHungryPoint(){
  return this.hungryPoint;
 }
 public void setHungryPoint(int hungryPoint){
  this.hungryPoint = hungryPoint;
 }
 public void recover(int i) {

 }

}


package zoo;

public class Lion extends Felidae{

 String name = "ライオン";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 @Override
 public void recover(int i) {
  System.out.println(this.name+ "は空腹度が" + i + "に回復した");
 }

}


package zoo;

public class Caracal extends Felidae{
 String name = "カラカル";
 public void eat(){
  System.out.println(this.name + "は、保護されているので、ご馳走をもらって食べている");
 }
 @Override
 public void recover(int i) {
  System.out.println(this.name+ "は空腹度が" + i + "に回復した");
 }
}

package zoo;

public class Tiger extends Felidae{

 String name = "トラ";

 // オーバーライド
 public void eat(){
  System.out.println(this.name + "は勢いよく、肉に噛り付いた!");
 }
 @Override
 public void recover(int i) {
  System.out.println(this.name+ "は空腹度が" + i + "に回復した");
 }
}

package zoo;

public class Main {

 public static void main(String[] args) {

  Lion l1 = new Lion();
  Caracal c1 = new Caracal();
  Tiger t1 = new Tiger();

  l1.setHungryPoint(10);
  c1.setHungryPoint(10);
  t1.setHungryPoint(10);

  l1.eat();
  c1.eat();
  t1.eat();

  l1.recover(l1.getHungryPoint());
  c1.recover(c1.getHungryPoint());
  t1.recover(t1.getHungryPoint());

  }
 }














Mainメソッドに、重複したコードが多いように感じる。
引数が変更になった際の修正も面倒。

こういう一括で引数をわたしたいときなどは、配列と組み合わせるとスッキリする。



package zoo;

public class Main {

 public static void main(String[] args) {

  // Felidae型の要素を三つを 配列変数名 f に代入
  Felidae [ ] f = new Felidae [3];

  // 要素の中に Lion,Caracal,Tiger を入れる
  f[0] = new Lion();
  f[1] = new Caracal();
  f[2] = new Tiger();

  // 拡張for文を使いループをまわす
  // f2 は Felidae型の任意の変数
  for (Felidae f2 : f){
   f2.eat();
  }
  for (Felidae f2 : f){
   f2.recover(10);
  }

 }
}


実行結果はさきほどと変わらない。
こういう使い方をするのが、どういう場面かは、わからないけれど
上の例を見ると、とても綺麗なコードになったと思う。
これで、回復度を10→7に変える際も、わずかな労力で変更可能。

また、同じ animalクラスから受け継がれてきた eat()メソッドを一括で呼び出しても
中身(Lion,Caracal,Tiger)に合った動作をそれぞれが実行してくれる。

これが、ポリモーフィズムのすごいところ!