狠狠撸

狠狠撸Share a Scribd company logo
アニメーション
 / 各种テスト
2012/03/21(水)@PRO&BSC
         樋口 祐紀
 (higuchi_yuki@probsc.jp)
本日の内容
? アニメーション
 –User eXperience を高めましょう
  ? 楽しい感じに
  ? 処理待ちのイライラを軽減

? 各种テスト
 –単体/結合/安定化試験を自動化し、
  アプリの品質を高めましょう
                            2
アニメーション
    クラス名                   概要
 AlphaAnimation    フェードイン/アウト
 RotateAnimation   回転
 ScaleAnimation    拡大/縮小
TranslateAnimation 移動
  AnimationSet     アニメーションの合成
サンプルのレイアウト
<?xml version="1.0" encoding="utf-8"?>                     main.xml
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:gravity="center">

<Button
  android:layout_width="wrap_content"
  android:padding="20dp"
  android:id="@+id/button"
  android:layout_height="wrap_content"
  android:text="Hello World !!" />

</LinearLayout>



                                                                      4
AlphaAnimation: ???????/???
public class MainActivity extends Activity {
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
        Button btn = (Button)findViewById(R.id.button);
        btn.setOnClickListener(new OnClickListener() {
           public void onClick(View v) {
               AlphaAnimation alpha =
                     new AlphaAnimation(1.0f, 0.0f);
               alpha.setDuration(3000);
               v.startAnimation(alpha);
           }
        });
    }
}                                                         5
RotateAnimation: 回転
RotateAnimation rotate = new RotateAnimation(
      0, 360, v.getWidth() / 2, v.getHeight() / 2);
rotate.setDuration(3000);
v.startAnimation(rotate);




     ScaleAnimation: 拡大/縮小
ScaleAnimation scale = new ScaleAnimation(
      1, 2, 1, 2, v.getWidth() / 2, v.getHeight() / 2);
scale.setDuration(3000);
scale.setInterpolator(new CycleInterpolator(3));
v.startAnimation(scale);
                                                          6
TranslateAnimation: 移動
TranslateAnimation translate = new TranslateAnimation(
      -10, 10, -10, 10);
translate.setDuration(3000);
translate.setInterpolator(new CycleInterpolator(3));
v.startAnimation(translate);


  AnimationSet: ???????の合成
AnimationSet set = new AnimationSet(true);
set.addAnimation(alpha);
set.addAnimation(rotate);    これまでのアニメーションの定
set.addAnimation(scale);     義 部 分 ( startAnimation
v.startAnimation(set);       以外)を残しておき、それらの
                               定義の下に書いてみましょう
                                                         7
アニメーションを利用したビューア
? APK
  – http://goo.gl/LLYXP
? ソース
  – http://goo.gl/oWcyG
? ソースのインポート方法
  – Eclipse の [ファイル(F)] – [インポート
    (I)...] から「インポート」ダイアログを開く
  – [一般] - [既存プロジェクトをワークスペー
    スへ] を選び、「ルート?ディレクトリーの選
    択」にてインポート対象フォルダを選択して
    取り込み完了                         8
Tips: メニューの追加方法
/** メニューID (バージョン) */
static final int MENU_ID_MENU1 = (Menu.FIRST + 1);

public boolean onCreateOptionsMenu(Menu menu) {
  // メニュー (バージョン) の追加
  MenuItem menuVersion = menu.add(
      Menu.NONE, MENU_ID_MENU1, Menu.NONE, "バージョン");
  menuVersion.setIcon(android.R.drawable.ic_menu_info_details);

    return super.onCreateOptionsMenu(menu);
}

public boolean onOptionsItemSelected(MenuItem item) {
  switch (item.getItemId()) {
      case MENU_ID_MENU1: // バージョン表示
         showDialog(DIALOG_ID_VERSION);
         break;

        default:
           return super.onOptionsItemSelected(item);
    }

    return true;
}
                                                                  9
Tips: ハードウェアキーの補足
public boolean onKeyDown(int keyCode, KeyEvent event) {
  super.onKeyDown(keyCode, event);
    switch (keyCode) {
      case KeyEvent.KEYCODE_VOLUME_DOWN:
      case KeyEvent.KEYCODE_DPAD_DOWN:
      case KeyEvent.KEYCODE_DPAD_RIGHT:
      case KeyEvent.KEYCODE_SPACE:
      case KeyEvent.KEYCODE_ENTER:
          ... (省略) ...
          return true;
        case   KeyEvent.KEYCODE_VOLUME_UP:
        case   KeyEvent.KEYCODE_DPAD_UP:
        case   KeyEvent.KEYCODE_DPAD_LEFT:
        case   KeyEvent.KEYCODE_DEL:
          ... (省略) ...
          return true;
    }
    return false;
}
                                                          10
Tips: 回転後も消えないダイアログ
/** ダイアログID (バージョン) */
static final int DIALOG_ID_VERSION = 0;
protected Dialog onCreateDialog(int id) {
   Dialog dialog;
    switch (id) {
      case DIALOG_ID_VERSION:
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
         builder.setIcon(R.drawable.icon);
         builder.setTitle("イメージビューア");
         builder.setMessage("version 3.0");
         builder.setPositiveButton("OK", null);
         dialog = builder.create();
         break;                       onCreateDialogを経由して表示するこ
                                    とでダイアログのライフサイクルをActivity
        default:
           dialog = null;
                                    に管理してもらうことになります。
    }                               このため、正確に書くと「(画面の)回転後
                                    も消えない」ではなく、「(画面の)回転後
    return dialog;                  にActivityの再生成に伴ってダイアログ
}
                                    も再生成される」が正しくなります。
    ... (省略) ...
    showDialog(DIALOG_ID_VERSION);
                                                                        11
Tips: FrameLayout の使い方
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@android:color/white"
  android:padding="10dp" >
  <ImageView
    android:id="@+id/imgView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"     アンカーを明示しないと
    android:src=/slideshow/android-lecture-04-probsc-inc/12052012/"@drawable/img01" />         左上に配置されます
  <Button
    android:id="@+id/btnLeft"
    android:layout_width="100dp"
    android:layout_height="wrap_content"
     android:layout_gravity="bottom|left"
     android:text="←"                       どこにアンカーを置くか
     android:textSize="24sp" />             がポイントになります
  ... (省略) ...

                                                                          12
各种テスト
                  ツール                                  概要

    junit.framework.TestCase                    ①単体试験
                                                (主な対象: ロジック)


android.test.ActivityInstrumentationTestCase2   ②结合试験
                                                (主な対象: UI)


           Monkey Runner                        ③安定化试験
                                                (主な対象: メモリリーク)
テストプロジェクトの追加
? Androidプロジェクト作成時に追加できます




                            14
サンプル(SimpleCalc)の用意①
<?xml version="1.0" encoding="utf-8"?>                   <Button
<LinearLayout xmlns:android=                                android:id="@+id/btnMul"
     "http://schemas.android.com/apk/res/android"           android:layout_width="fill_parent"
  android:layout_width="wrap_content"                       android:layout_height="wrap_content"
  android:layout_height="wrap_content"                      android:text="×"
  android:layout_gravity="center_horizontal"                android:textSize="22sp" />
  android:orientation="horizontal" >                     <Button
  <EditText                                                 android:id="@+id/btnDiv"
     android:id="@+id/editNum1"                             android:layout_width="fill_parent"
     android:layout_width="68dp"                            android:layout_height="wrap_content"
     android:layout_height="wrap_content"                   android:text="÷"
     android:layout_gravity="center_vertical"               android:textSize="22sp" />
     android:gravity="center"                         </LinearLayout>
     android:inputType="number" />                    <EditText
  <LinearLayout                                          android:id="@+id/editNum2"
     android:layout_width="48dp"                         android:layout_width="68dp"
     android:layout_height="wrap_content"                android:layout_height="wrap_content"
     android:orientation="vertical" >                    android:layout_gravity="center_vertical"
     <Button                                             android:gravity="center"
        android:id="@+id/btnAdd"                         android:inputType="number" />
        android:layout_width="fill_parent"            <TextView
        android:layout_height="wrap_content"             android:layout_width="48dp"
        android:text="+"                                 android:layout_height="wrap_content"
        android:textSize="22sp" />                       android:layout_gravity="center_vertical"
      <Button                                            android:gravity="center"
        android:id="@+id/btnSub"                         android:text="="
        android:layout_width="fill_parent"               android:textSize="22sp" />
        android:layout_height="wrap_content"          <EditText
        android:text="-"                                 android:id="@+id/editAnswer"
        android:textSize="22sp" />                       android:layout_width="68dp"
                                                         android:layout_height="wrap_content"
                                                         android:layout_gravity="center_vertical"
                                                         android:editable="false"
                                                         android:gravity="center" />
?res/layout/main.xml                                </LinearLayout>
                                                                                                    15
サンプル(SimpleCalc)の用意②
package jp.probsc.simplecalc;
                                               ?src/.../Calculator.java
/**
 *計算クラス
 */
public class Calculator {
  /** 足し算 */
  public static int Add(int a, int b) {
      return a + b;
  }
    /** 引き算 */
    public static int Sub(int a, int b) {
      return a - b;
    }
    /** 掛け算 */
    public static int Mul(int a, int b) {
      return a * b;
    }
    /** 割り算 */
    public static double Div(int a, int b) {
      return a / b;
    }
}                                                                   16
サンプル(SimpleCalc)の用意③
package jp.probsc.simplecalc;                                  final EditText num2 =
                                                                  (EditText)findViewById(R.id.editNum2);
import   android.app.Activity;                                 num2.setSelectAllOnFocus(true);
import   android.os.Bundle;
import   android.view.View;                                    final EditText numAnswer =
import   android.view.View.OnClickListener;                       (EditText)findViewById(R.id.editAnswer);
import   android.widget.Button;
import   android.widget.EditText;                              Button btnAdd =
                                                                  (Button)findViewById(R.id.btnAdd);
/**                                                            btnAdd.setOnClickListener(new OnClickListener() {
 * シンプル電卓クラス                                                      public void onClick(View v) {
 */                                                                 int ret = Calculator.Add(
public class MainActivity extends Activity {                            escape(num1), escape(num2));
   /**                                                              numAnswer.setText(String.valueOf(ret));
    * onCreate コールバックメソッド                                         }
    */                                                         });
   public void onCreate(Bundle savedInstanceState) {       }
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);                       /**
                                                            * EditText をエスケープして整数値を返す
      // ウィジェットの初期化                                         *
      init();                                               * @param target エスケープ対象の EditText ウィジェット
  }                                                         * @return 整数値
                                                            */
  /**                                                      public static int escape(EditText target) {
   * ウィジェットの初期化                                               try {
   */                                                            return Integer.parseInt(target.getText().toString());
  private void init() {                                       } catch (Exception ex) {
     final EditText num1 =                                       target.setText("0");
        (EditText)findViewById(R.id.editNum1);                   return 0;
     num1.setSelectAllOnFocus(true);                          }
                                                           }
?src/.../MainActivity.java                             }
                                                                                                                17
サンプル(厂颈尘辫濒别颁补濒肠)の完成
        ? ソース
         – サンプルプロジェクト
            ? http://goo.gl/SQ0LW
         – テストプロジェクト
            ? http://goo.gl/KM1bW


        ? この四則演算を行うアプリの
          テストを行います。試験は下
          記の4種類です
         – ① 単体試験
         – ② 結合試験
         – ③ 安定化試験
                                18
①単体试験

          単体試験の準備
 ? ロジックを試験します
   – テストプロジェクトに「CalculatorTest.java」を追加
              package jp.probsc.simplecalc.test;

              import jp.probsc.simplecalc.Calculator;
              import junit.framework.TestCase;

              public class CalculatorTest extends TestCase {
                public void testAdd() {
                    assertEquals(0, Calculator.Add(0, 0));
                    assertEquals(1, Calculator.Add(1, 0));
                    assertEquals(2, Calculator.Add(2, 0));
                    assertEquals(1, Calculator.Add(0, 1));
                    assertEquals(2, Calculator.Add(0, 2));
                    assertEquals(2, Calculator.Add(1, 1));
                    assertEquals(4, Calculator.Add(2, 2));
                }
              }
                                                               19
①単体试験

            単体試験の実施
 ? クラスを右クリックし、[実行(R)]-[Android JUnit Test]を実行




  1つでもパスしないテストケース
  があると、バーが赤色になります
 テスト実施はテストケース(メソッ
 ド)ごと、クラスごと、プロジェクトご
  との単位にて実施できます。


                                                20
②结合试験

                            結合試験の準備
   ? UIを試験します                        ※テストプロジェクトに「MainActivityTest.java」を追加
package jp.probsc.simplecalc.test;                                 getInstrumentation()
                                                                      .waitForIdleSync();
import   android.test.ActivityInstrumentationTestCase2;            getActivity().runOnUiThread(
import   android.widget.Button;                                                      new Runnable() {
import   android.widget.EditText;                                     public void run() {
import   jp.probsc.simplecalc.MainActivity;                             num1.setText("2");
                                                                        num2.setText("4");
public class MainActivityTest extends                                   btnAdd.performClick();
  ActivityInstrumentationTestCase2<MainActivity> {                    }
  public MainActivityTest() {                                      });
      super(MainActivity.class);
  }                                                                 getInstrumentation()
                                                                       .waitForIdleSync();
  public void testCalc() {                                          EditText numAnswer = (EditText)
    final EditText num1 =                                              getActivity().findViewById(
        (EditText)getActivity().findViewById(                     jp.probsc.simplecalc.R.id.editAnswer);
           jp.probsc.simplecalc.R.id.editNum1);                     assertEquals("6",
    final EditText num2 =                                               numAnswer.getText().toString());
        (EditText)getActivity().findViewById(                 }
           jp.probsc.simplecalc.R.id.editNum2);           }
    final Button btnAdd =
        (Button)getActivity().findViewById(
           jp.probsc.simplecalc.R.id.btnAdd);                                                      21
②结合试験

           結合試験の実施
 ? テ ス ト プ ロ ジ ェ ク ト を 選 択 し て [ 実 行 (R)]-
   [Android JUnit Test] を実行することで、先
   ほどの単体試験と同時にテストを実施できます




   UIのテストは実機、またはエ
   ミュレータ上で実行されます。
                                         22
③安定化试験

               Monkey Runner の利用
                                                         # テストの実施
#!/usr/bin/python
# coding: UTF-8                                          def doTest():
                                                                                   mr_simplecalc.py
                                                             device = init()
from com.android.monkeyrunner import MonkeyRunner,
MonkeyDevice                                                snapshot(device)   Pythonスクリプトです
import datetime
                                                            for i in range(1, 1001):
# 日時文字列の取得                                                      printMsg('cnt: %d' % i)
def getDate():
    d = datetime.datetime.today()                               # 1つめの数値を入力
    return d.strftime('%Y%m%d_%H%M%S')                          MonkeyRunner.sleep(1)
                                                                device.touch(65, 215, 'DOWN_AND_UP')
# スナップショットの取得                                                   device.type('%d' % i)
# (monkeyrunner.bat と同階層に snapshot フォルダを準備のこと)
def snapshot(device):                                           # 2つめの数値を入力
    MonkeyRunner.sleep(1)                                       MonkeyRunner.sleep(1)
    result = device.takeSnapshot()                              device.touch(240, 215, 'DOWN_AND_UP')
    result.writeToFile(                                         num = i * 2
        'snapshot/%s.png' % getDate(), 'png')                   device.type('%d' % num)

# テスト初期化                                                        # 「+」ボタンを押下
def init():                                                     MonkeyRunner.sleep(1)
    device = MonkeyRunner.waitForConnection()                   device.touch(155, 110, 'DOWN_AND_UP')
    device.startActivity(
       component='jp.probsc.simplecalc/.MainActivity')          snapshot(device)
    return device
                                                         # テストの開始
# 標準出力へメッセージを出力                                          printMsg('test start.')
def printMsg(msg):                                       doTest()
    print '[%s] %s' % (getDate(), msg)                   printMsg('test end.')                   23
③安定化试験

      Monkey Runner の実行結果
?コマンドプロンプトに下記コマンドを入力してMonkey Runnerを起動します
c:?>
c:?>C:?android?android-sdk-windows?tools?monkeyrunner.bat C:?mr_simplecalc.py
[20120318_145102] test start.
[20120318_145117] cnt: 1
[20120318_145132] cnt: 2       今回のスクリプトの場合、1000回の足し算が
[20120318_145148] cnt: 3       自動的に実行されます。別途、メモリ使用状
[20120318_145201] cnt: 4
[20120318_145215] cnt: 5        況などを監視してアプリの安定化を図ります
...

?monkeryrunner.bat と同階層の snapshot フォルダに下記が保存されます




                                                                           24
よりテストを実施しやすくするために
? 今回ご紹介したUIテストとMonkey Runnerで
  は、ウィジェットのIDや画面上の座標を知っている
  必要があり、「OKボタンを押す」などの直観的な
  テストをかけない...
 NativeDriver や Scirocco などのツールの利用が便利です!


? これまでJavaのプロジェクトではもっくオブジェクト
  を使って試験を行えたが、AndroidはVMが異な
  るため、既存ツールを利用できない...
   EasyMock の知識を Android Mock に活かせます!
                                           25
本日の課題
? イメージビューアの改善
 – 縦画面と横画面のそれぞれに異なる1つ以上のアニ
   メーションを追加しましょう
 – メニューに1つ以上の独自の機能を追加しましょう
? SimpleCalc とテストの改善
 – SimpleCalc に足し算以外の機能を追加しましょう
 – テストプロジェクトに足し算以外の機能のテストケース
   を追加しましょう
                                  26

More Related Content

Android Lecture #04 @PRO&BSC Inc.

  • 2. 本日の内容 ? アニメーション –User eXperience を高めましょう ? 楽しい感じに ? 処理待ちのイライラを軽減 ? 各种テスト –単体/結合/安定化試験を自動化し、 アプリの品質を高めましょう 2
  • 3. アニメーション クラス名 概要 AlphaAnimation フェードイン/アウト RotateAnimation 回転 ScaleAnimation 拡大/縮小 TranslateAnimation 移動 AnimationSet アニメーションの合成
  • 4. サンプルのレイアウト <?xml version="1.0" encoding="utf-8"?> main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center"> <Button android:layout_width="wrap_content" android:padding="20dp" android:id="@+id/button" android:layout_height="wrap_content" android:text="Hello World !!" /> </LinearLayout> 4
  • 5. AlphaAnimation: ???????/??? public class MainActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button btn = (Button)findViewById(R.id.button); btn.setOnClickListener(new OnClickListener() { public void onClick(View v) { AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f); alpha.setDuration(3000); v.startAnimation(alpha); } }); } } 5
  • 6. RotateAnimation: 回転 RotateAnimation rotate = new RotateAnimation( 0, 360, v.getWidth() / 2, v.getHeight() / 2); rotate.setDuration(3000); v.startAnimation(rotate); ScaleAnimation: 拡大/縮小 ScaleAnimation scale = new ScaleAnimation( 1, 2, 1, 2, v.getWidth() / 2, v.getHeight() / 2); scale.setDuration(3000); scale.setInterpolator(new CycleInterpolator(3)); v.startAnimation(scale); 6
  • 7. TranslateAnimation: 移動 TranslateAnimation translate = new TranslateAnimation( -10, 10, -10, 10); translate.setDuration(3000); translate.setInterpolator(new CycleInterpolator(3)); v.startAnimation(translate); AnimationSet: ???????の合成 AnimationSet set = new AnimationSet(true); set.addAnimation(alpha); set.addAnimation(rotate); これまでのアニメーションの定 set.addAnimation(scale); 義 部 分 ( startAnimation v.startAnimation(set); 以外)を残しておき、それらの 定義の下に書いてみましょう 7
  • 8. アニメーションを利用したビューア ? APK – http://goo.gl/LLYXP ? ソース – http://goo.gl/oWcyG ? ソースのインポート方法 – Eclipse の [ファイル(F)] – [インポート (I)...] から「インポート」ダイアログを開く – [一般] - [既存プロジェクトをワークスペー スへ] を選び、「ルート?ディレクトリーの選 択」にてインポート対象フォルダを選択して 取り込み完了 8
  • 9. Tips: メニューの追加方法 /** メニューID (バージョン) */ static final int MENU_ID_MENU1 = (Menu.FIRST + 1); public boolean onCreateOptionsMenu(Menu menu) { // メニュー (バージョン) の追加 MenuItem menuVersion = menu.add( Menu.NONE, MENU_ID_MENU1, Menu.NONE, "バージョン"); menuVersion.setIcon(android.R.drawable.ic_menu_info_details); return super.onCreateOptionsMenu(menu); } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_ID_MENU1: // バージョン表示 showDialog(DIALOG_ID_VERSION); break; default: return super.onOptionsItemSelected(item); } return true; } 9
  • 10. Tips: ハードウェアキーの補足 public boolean onKeyDown(int keyCode, KeyEvent event) { super.onKeyDown(keyCode, event); switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_SPACE: case KeyEvent.KEYCODE_ENTER: ... (省略) ... return true; case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_DPAD_UP: case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DEL: ... (省略) ... return true; } return false; } 10
  • 11. Tips: 回転後も消えないダイアログ /** ダイアログID (バージョン) */ static final int DIALOG_ID_VERSION = 0; protected Dialog onCreateDialog(int id) { Dialog dialog; switch (id) { case DIALOG_ID_VERSION: AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setIcon(R.drawable.icon); builder.setTitle("イメージビューア"); builder.setMessage("version 3.0"); builder.setPositiveButton("OK", null); dialog = builder.create(); break; onCreateDialogを経由して表示するこ とでダイアログのライフサイクルをActivity default: dialog = null; に管理してもらうことになります。 } このため、正確に書くと「(画面の)回転後 も消えない」ではなく、「(画面の)回転後 return dialog; にActivityの再生成に伴ってダイアログ } も再生成される」が正しくなります。 ... (省略) ... showDialog(DIALOG_ID_VERSION); 11
  • 12. Tips: FrameLayout の使い方 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:color/white" android:padding="10dp" > <ImageView android:id="@+id/imgView" android:layout_width="fill_parent" android:layout_height="fill_parent" アンカーを明示しないと android:src=/slideshow/android-lecture-04-probsc-inc/12052012/"@drawable/img01" /> 左上に配置されます <Button android:id="@+id/btnLeft" android:layout_width="100dp" android:layout_height="wrap_content" android:layout_gravity="bottom|left" android:text="←" どこにアンカーを置くか android:textSize="24sp" /> がポイントになります ... (省略) ... 12
  • 13. 各种テスト ツール 概要 junit.framework.TestCase ①単体试験 (主な対象: ロジック) android.test.ActivityInstrumentationTestCase2 ②结合试験 (主な対象: UI) Monkey Runner ③安定化试験 (主な対象: メモリリーク)
  • 15. サンプル(SimpleCalc)の用意① <?xml version="1.0" encoding="utf-8"?> <Button <LinearLayout xmlns:android= android:id="@+id/btnMul" "http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_height="wrap_content" android:text="×" android:layout_gravity="center_horizontal" android:textSize="22sp" /> android:orientation="horizontal" > <Button <EditText android:id="@+id/btnDiv" android:id="@+id/editNum1" android:layout_width="fill_parent" android:layout_width="68dp" android:layout_height="wrap_content" android:layout_height="wrap_content" android:text="÷" android:layout_gravity="center_vertical" android:textSize="22sp" /> android:gravity="center" </LinearLayout> android:inputType="number" /> <EditText <LinearLayout android:id="@+id/editNum2" android:layout_width="48dp" android:layout_width="68dp" android:layout_height="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > android:layout_gravity="center_vertical" <Button android:gravity="center" android:id="@+id/btnAdd" android:inputType="number" /> android:layout_width="fill_parent" <TextView android:layout_height="wrap_content" android:layout_width="48dp" android:text="+" android:layout_height="wrap_content" android:textSize="22sp" /> android:layout_gravity="center_vertical" <Button android:gravity="center" android:id="@+id/btnSub" android:text="=" android:layout_width="fill_parent" android:textSize="22sp" /> android:layout_height="wrap_content" <EditText android:text="-" android:id="@+id/editAnswer" android:textSize="22sp" /> android:layout_width="68dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:editable="false" android:gravity="center" /> ?res/layout/main.xml </LinearLayout> 15
  • 16. サンプル(SimpleCalc)の用意② package jp.probsc.simplecalc; ?src/.../Calculator.java /** *計算クラス */ public class Calculator { /** 足し算 */ public static int Add(int a, int b) { return a + b; } /** 引き算 */ public static int Sub(int a, int b) { return a - b; } /** 掛け算 */ public static int Mul(int a, int b) { return a * b; } /** 割り算 */ public static double Div(int a, int b) { return a / b; } } 16
  • 17. サンプル(SimpleCalc)の用意③ package jp.probsc.simplecalc; final EditText num2 = (EditText)findViewById(R.id.editNum2); import android.app.Activity; num2.setSelectAllOnFocus(true); import android.os.Bundle; import android.view.View; final EditText numAnswer = import android.view.View.OnClickListener; (EditText)findViewById(R.id.editAnswer); import android.widget.Button; import android.widget.EditText; Button btnAdd = (Button)findViewById(R.id.btnAdd); /** btnAdd.setOnClickListener(new OnClickListener() { * シンプル電卓クラス public void onClick(View v) { */ int ret = Calculator.Add( public class MainActivity extends Activity { escape(num1), escape(num2)); /** numAnswer.setText(String.valueOf(ret)); * onCreate コールバックメソッド } */ }); public void onCreate(Bundle savedInstanceState) { } super.onCreate(savedInstanceState); setContentView(R.layout.main); /** * EditText をエスケープして整数値を返す // ウィジェットの初期化 * init(); * @param target エスケープ対象の EditText ウィジェット } * @return 整数値 */ /** public static int escape(EditText target) { * ウィジェットの初期化 try { */ return Integer.parseInt(target.getText().toString()); private void init() { } catch (Exception ex) { final EditText num1 = target.setText("0"); (EditText)findViewById(R.id.editNum1); return 0; num1.setSelectAllOnFocus(true); } } ?src/.../MainActivity.java } 17
  • 18. サンプル(厂颈尘辫濒别颁补濒肠)の完成 ? ソース – サンプルプロジェクト ? http://goo.gl/SQ0LW – テストプロジェクト ? http://goo.gl/KM1bW ? この四則演算を行うアプリの テストを行います。試験は下 記の4種類です – ① 単体試験 – ② 結合試験 – ③ 安定化試験 18
  • 19. ①単体试験 単体試験の準備 ? ロジックを試験します – テストプロジェクトに「CalculatorTest.java」を追加 package jp.probsc.simplecalc.test; import jp.probsc.simplecalc.Calculator; import junit.framework.TestCase; public class CalculatorTest extends TestCase { public void testAdd() { assertEquals(0, Calculator.Add(0, 0)); assertEquals(1, Calculator.Add(1, 0)); assertEquals(2, Calculator.Add(2, 0)); assertEquals(1, Calculator.Add(0, 1)); assertEquals(2, Calculator.Add(0, 2)); assertEquals(2, Calculator.Add(1, 1)); assertEquals(4, Calculator.Add(2, 2)); } } 19
  • 20. ①単体试験 単体試験の実施 ? クラスを右クリックし、[実行(R)]-[Android JUnit Test]を実行 1つでもパスしないテストケース があると、バーが赤色になります テスト実施はテストケース(メソッ ド)ごと、クラスごと、プロジェクトご との単位にて実施できます。 20
  • 21. ②结合试験 結合試験の準備 ? UIを試験します ※テストプロジェクトに「MainActivityTest.java」を追加 package jp.probsc.simplecalc.test; getInstrumentation() .waitForIdleSync(); import android.test.ActivityInstrumentationTestCase2; getActivity().runOnUiThread( import android.widget.Button; new Runnable() { import android.widget.EditText; public void run() { import jp.probsc.simplecalc.MainActivity; num1.setText("2"); num2.setText("4"); public class MainActivityTest extends btnAdd.performClick(); ActivityInstrumentationTestCase2<MainActivity> { } public MainActivityTest() { }); super(MainActivity.class); } getInstrumentation() .waitForIdleSync(); public void testCalc() { EditText numAnswer = (EditText) final EditText num1 = getActivity().findViewById( (EditText)getActivity().findViewById( jp.probsc.simplecalc.R.id.editAnswer); jp.probsc.simplecalc.R.id.editNum1); assertEquals("6", final EditText num2 = numAnswer.getText().toString()); (EditText)getActivity().findViewById( } jp.probsc.simplecalc.R.id.editNum2); } final Button btnAdd = (Button)getActivity().findViewById( jp.probsc.simplecalc.R.id.btnAdd); 21
  • 22. ②结合试験 結合試験の実施 ? テ ス ト プ ロ ジ ェ ク ト を 選 択 し て [ 実 行 (R)]- [Android JUnit Test] を実行することで、先 ほどの単体試験と同時にテストを実施できます UIのテストは実機、またはエ ミュレータ上で実行されます。 22
  • 23. ③安定化试験 Monkey Runner の利用 # テストの実施 #!/usr/bin/python # coding: UTF-8 def doTest(): mr_simplecalc.py device = init() from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice snapshot(device) Pythonスクリプトです import datetime for i in range(1, 1001): # 日時文字列の取得 printMsg('cnt: %d' % i) def getDate(): d = datetime.datetime.today() # 1つめの数値を入力 return d.strftime('%Y%m%d_%H%M%S') MonkeyRunner.sleep(1) device.touch(65, 215, 'DOWN_AND_UP') # スナップショットの取得 device.type('%d' % i) # (monkeyrunner.bat と同階層に snapshot フォルダを準備のこと) def snapshot(device): # 2つめの数値を入力 MonkeyRunner.sleep(1) MonkeyRunner.sleep(1) result = device.takeSnapshot() device.touch(240, 215, 'DOWN_AND_UP') result.writeToFile( num = i * 2 'snapshot/%s.png' % getDate(), 'png') device.type('%d' % num) # テスト初期化 # 「+」ボタンを押下 def init(): MonkeyRunner.sleep(1) device = MonkeyRunner.waitForConnection() device.touch(155, 110, 'DOWN_AND_UP') device.startActivity( component='jp.probsc.simplecalc/.MainActivity') snapshot(device) return device # テストの開始 # 標準出力へメッセージを出力 printMsg('test start.') def printMsg(msg): doTest() print '[%s] %s' % (getDate(), msg) printMsg('test end.') 23
  • 24. ③安定化试験 Monkey Runner の実行結果 ?コマンドプロンプトに下記コマンドを入力してMonkey Runnerを起動します c:?> c:?>C:?android?android-sdk-windows?tools?monkeyrunner.bat C:?mr_simplecalc.py [20120318_145102] test start. [20120318_145117] cnt: 1 [20120318_145132] cnt: 2 今回のスクリプトの場合、1000回の足し算が [20120318_145148] cnt: 3 自動的に実行されます。別途、メモリ使用状 [20120318_145201] cnt: 4 [20120318_145215] cnt: 5 況などを監視してアプリの安定化を図ります ... ?monkeryrunner.bat と同階層の snapshot フォルダに下記が保存されます 24
  • 25. よりテストを実施しやすくするために ? 今回ご紹介したUIテストとMonkey Runnerで は、ウィジェットのIDや画面上の座標を知っている 必要があり、「OKボタンを押す」などの直観的な テストをかけない... NativeDriver や Scirocco などのツールの利用が便利です! ? これまでJavaのプロジェクトではもっくオブジェクト を使って試験を行えたが、AndroidはVMが異な るため、既存ツールを利用できない... EasyMock の知識を Android Mock に活かせます! 25
  • 26. 本日の課題 ? イメージビューアの改善 – 縦画面と横画面のそれぞれに異なる1つ以上のアニ メーションを追加しましょう – メニューに1つ以上の独自の機能を追加しましょう ? SimpleCalc とテストの改善 – SimpleCalc に足し算以外の機能を追加しましょう – テストプロジェクトに足し算以外の機能のテストケース を追加しましょう 26