ラベル Android の投稿を表示しています。 すべての投稿を表示
ラベル Android の投稿を表示しています。 すべての投稿を表示

2012年2月18日土曜日

Developers Summit 2012 17-D-7 実践Androidデベロッパーズテストにて講演してきました。

通称デブサミ2012の二日目最終セッションでAndroidテスト部の一員として講演してきました。

講演資料はスライドシェアにあります。
こちらの26ページから56ページを担当させて頂きました。

また、その期間のツイートはtogetterにまとめてあります。

なお、今回の講演にあたってサンプルで作成していたアプリケーション「お小遣い・ログ」のソースコードはGitHubにて公開しています。

Twitter上で私をフォローしていただいている方はご存じの方も多いかもしれませんが、最近の私のTwitterのプロファイルは「冬眠中」となっています。

実は、これは文字通り意味していまして、若干過眠症気味な状態になっています。

そういったわけで、今回資料を作成するにあたっては、メンバーの皆さん @snskさん@nowsprintingさん@ussy00さんには大変ご迷惑をおかけいたしました。

ここで感謝を表したいと思います。

また、今回の資料を作成するにあたって、何度か会社を休んでしまいました。

相談に乗っていただいた株式会社トップゲートの加藤社長ならびに同僚の皆様にも大変感謝しております。


2012年2月13日月曜日

AndroidのUnit Testをするなら読んでおきたいコード

単なるリンクメモ


Activityのテスト関連

  • android.test.ActivityInstrumentationTestCase2<T>
    • Activityのテストを書く時に継承するクラス。有無を言わず読んどけ。
  • android.test.ActivityTestCase
    • ActivityInstrumentationTestCase2<T>が継承しているクラス。とりあえず、すこしだけ読んどいたほうがいいかも。
  • InstrumentationTestCase
    • ActivityInstrumentationTestCase2<T>が最も依存しているクラス。より高度な知識を得たい時には読んどいたほうがよい。
  • android.test.InstrumentationTestRunner
    • テストを起動するActivityと同等のもの。もしJUnitと同等のレポートを出力する場合には、このクラスを改造するので読んでおいたほうがよい。
  • android.app.Instrumentation
    • InstrumentationTestRunnerの基底クラス。JUnitと同等のレポート出力するために読んでおきたい。



2012年1月1日日曜日

Summary of 2011 and objective of 2012

Now looking back to 2011, there was some change in my environment.

About 2011


From my impression, changes are ...

  • Found new jobs at TopGate Inc.
  • Attracted Google's technologies.
  • Attracted Groovy programming language.
  • Trying to do off-shore work in Vietnam.

About No.2

I just started to study Android technologies at a community named Saitama Android Board.
As I learned Android technologies, I found that there are limitation of Android to create an interesting application without any server or any service.
So I had become tending to learn cloud technologies like Google App Engine.


About No.1

And it was good time to learn Google App Engine because my former company gave me a time to learn Google technologies. But in my former company there is some wasting and boring procedure to search and to learn new technologies. So I decided to change my job, to get a new environment. And luckily vvakame, one of my twitter friend, told me to work together in TopGate Inc where there are some programmers familiar with Google technologies. And I changed my employment from former one to TopGate Inc.


About No.3

At the same time to change my job, I was introduced to Groovy programming language. It was kimkou_26, one of my twitter friend, that told me the most powerful and exciting language. Groovy works on Java Virtual Machine with supporting almost all of Java technologies and with light syntax. Groovy attracted me very much. And with Groovy I met some programmers on groovy community.


If you ask me to decide which gave more impact on me, Google or Groovy, it is too difficult for me to decide. Because the objective is different and I love both technologies.


About No.4

Working foreign country become common to programmers in Japan. Because Japanese Yen is becoming so strong among these days that it costs higher to employ Japanese than to employ Chinese or Vietnamese for them to do same thing. In my project it finished in failure. But I'll get success on off-shore projects this year.


Next 2012


I'd like to work as listed bellow.

  • To work on Google Apps Installation
  • Create TopGate's additional value
  • To get success on off-shore project

About No.3

As I mentioned before, I'd like to get success in off-shore projects. From the view of the cost of project to work outside Japan cannot be ignored.


About No.1

This is my objective to work installation Google Apps to a customer. It seems Google considers Google Apps as their most successful cloud technology. And I think the tendency of companies to move from owning their server to using existing service becomes more popular. So it is business chance to become major company.


About No.2

TopGate Inc. is the re-seller of Google technologies. But I want to add additional G technology, I call it Yet Another G Technology. I mean Groovy and its co-production, like Grails, Griffon, Gradle, with these production we can offer some solutions faster than ordinal technologies. And this may cause create a value for our customer. By becoming familiar with these technologies, we -- TopGate Inc. -- will be able to establish our excellent brand image.


To reader of this blog and to another person, thank you this year!


2011年8月8日月曜日

仕様化テストの話をしようか

今日は仕様化テストのお話


先日、Androidテスト祭りで取り上げましたが、仕様化テストというテスト技法(?)があります。

簡単に仕様化テストとは何かをまとめるとこうなります。

ソフトウェアの仕様を調べ、その結果に基づいてテストを書くこと

マイケル・C・フェザーズ著、ウルシステムズ株式会社監訳、『レガシーコード改善ガイド』(翔泳社、2009年)、p.200


これは仕様のよくわからないアプリケーションないしはメソッドに対して仕様を明らかにすることができる技法なので、確実に覚えておきたい技法です。

というわけで、今回は画面サイズというなんかAndroidではちょっと嫌な部分について仕様化テストを書いてみようと思います。

まずはActivityを作ります。
これはHello, Android状態のアプリです。

MikeCountDown.java

public class MikeCountDown extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mike_countdown);
    }
}


レイアウト頑張ります。


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
    <LinearLayout
      android:id="@+id/view_position"
      android:layout_height="fill_parent"
      android:layout_width="fill_parent"
      android:layout_marginLeft="5dip"
      android:layout_marginRight="5dip"
      android:layout_weight="3"
      android:orientation="vertical">

<!-- 省略 -->

       <SeekBar
         android:id="@+id/seekBar"
         android:max="15"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:paddingLeft="25dip"
         android:paddingRight="25dip"
         android:layout_marginBottom="25dip"
         android:thumb="@drawable/thumb"/>

<!-- 省略 -->

    </LinearLayout>
</LinearLayout>


さて、ここでお題

SeekBarの横幅の大きさを求めよ


ここで『リファクタリング―プログラムの体質改善テクニック』の一言が思い浮かぶわけです。

デバッグは最悪、テストはまだいい


微妙に間違っていると思いますが…

つまり、やつの大きさを求めるためにデバッグモードで起動するのは最悪ということです。
ではわからないものにどう対処するか?
わからないものには、テストを書きましょう。

MikeCountDownTest.java

public class MikeCountDownTest extends ActivityTestCase<MikeCountDown> {
    
    private Activity activity;
    
    public MikeCountDownTest() {
        super("org.mikeneck.android.countdown", MikeCountDown.class);
    }
    
    @Override
    public void setUp() throws Exception {
        super.setUp();
        activity = getActivity();
    }
    
    public void testGetWidth() {
        View view = activity.findViewById(R.id.seekBar);
        assertEquals(1, view.getWidth());
    }
}


テスト結果


落ちたので、大きさを取得することができました。
というわけで、これが通るようにテストを書きなおします。

MikeCountDownTest.java

    public void testGetWidth() {
        View view = activity.findViewById(R.id.seekBar);
        assertEquals(464, view.getWidth());
    }


テスト結果

はい、見事にテスト通過しました。

これでエミュレーターの仕様を知ることができました。
それと同時に、動かすことのできる仕様を手に入れることができました。

このような感じで現状の仕様にあわせてテストを書いていって、仕様書にしてしまう手法を仕様化テストといいます。

今テストがないのに、どうやってテストを入れればいいのかお悩みの皆さん、この手法でテストを挟み込んでいきましょう。


2011年8月7日日曜日

AndroidのViewのisEnabledあたりをTDDする。

Androidを実際にTDDしてみようと思う。

お題のアプリは『作りながら覚えるAndroidプログラミング』(ソフトバンク・クリエイティブ)という本のStep.6のカウントダウンタイマーを作るというやつです。

本に記載されているコードはこんな感じです。

public class CountdownTimer extends Activity {

    // 一部省略

    Button startButton;

    Button stopButton;

    SeekBar seekBar;

    public static void countdown(int counter){
        // 一部省略
        if(counter != 0) {
            stopButton.setEnable(true);
            startButton.setEnable(false);
            seekBar.setEnabled(false);
        } else {
            stopButton.setEnable(false);
            startButton.setEnable(false);
            seekBar.setEnabled(true);
        }
    }
}


まあ、counter0でなけば、
  • startButtonが触れなくなる。
  • stopButtonが触れるようになる。
  • seekBarが触れなくなる。
ですし、counter0であれば、
  • startButtonが触れなくなる。
  • stopButtonが触れなくなる。
  • seekBarが触れるようになる。
となります。

残年なところはこれActivityの子クラスなので、テストが半端なくめんどくさいのです。

というわけで、こういった操作を行うAndroidPOJOなクラスがあると考えてテストしながら作ってみたいと思います。



いや、そもそもViewってテストできるんだっけ?


これは非常に不安なので、不安を解消するためにテストを書いてみました。


public class CountDownControllerTest extends AndroidTestCase {
    public void testCanViewBeToggle() {
        Button button = new Button(getContext());
        assertTrue(button.isEnabled());
        button.setEnabled(false);
        assertFalse(button.isEnabled());
    }
}


テスト結果



グリーンですね。
大丈夫そうです。

ではおもむろに、期待する振る舞いを書いてみましょう。

CountDownControllerTest

private CountDownControllerTest controller;
    private Button startButton;
    private Button stopButton;
    private SeekBar seekBar;
    
    @Override
    public void setUp() thrwos Exception {
        super.setUp();
        controller = new CountDownController();
        startButton = new Button(getContext());
        stopButton = new Button(getContext());
        seekBar = new SeekBar(getContext());

        controller.startButton(startButton);
        controller.stopButton(stopButton);
        controller.seekBar(seekBar);
    }

    public void testSetProceeding() {
        controller.setProceeding();
        assertFalse(startButton.isEnabled());
        assertTrue(stopButton.isEnabled());
        assertFalse(seekBar.isEnabled());
    }


CountDownControllerというものがあって、それが画面のViewの操作に関する責務を担うものとします。それらのViewは外(ここではActivityを想定)から与えられるものとします。そして、操作メソッドはsetProcessing()とします。

最初の段階のCountDownControllerの実装は次のとおりです。

CountDownController

public class CountDownController {
    public void startButton(Button startButton) {
    }
    public void stopButton(Button stopButton) {
    }
    public void seekBar(SeekBar seekBar) {
    }
    public void setProgressing() {
    }
}


テスト結果

まあ、赤ですよ。
そりゃそうですね。何もしていませんから。

ViewというかButtonとか、SeekBarに対して仮の実装はできないので、おもむろに本実装をしてしまいます。

CountDownController
public class CountDownController {

    private View startButton;

    private View stopButton;

    private View seekBar;

    public void startButton(Button startButton) {
        this.startButton = startButton;
    }

    public void stopButton(Button stopButton) {
        this.stopButton = stopButton;
    }

    public void seekBar(SeekBar timeBar) {
        this.seekBar = seekBar;
    }

    public void setProgressing() {
        startButton.setEnabled(false);
        stopButton.setEnabled(true);
        seekBar.setEnabled(false);
    }
}


テスト結果

おっ!グリーン!きましたね。

では、もうひとつのケースを追加しちゃいましょう。

CountDownControllerTest

public void testEndProcessing() {
        controller.endProgressing();
        assertFalse(startButton.isEnabled());
        assertFalse(stopButton.isEnabled());
        assertTrue(seekBar.isEnabled());
    }


もちろん、仮実装します。コンパイルエラーをなくすための。

CountDownController

public void endProcessing() {
    }


テスト結果


ハイ♪ハイ♪ハイ♪ハイ♪ハ~イ♪レッドですよ~♪

というわけで、おもむろに本実装をします。
CountDownController

public void endProcessing() {
        startButton.setEnabled(false);
        stopButton.setEnabled(false);
        seekButton.setEnabled(true);
    }


では、テスト実行。

いいね、いいね!グリーン。いいね。

じゃあ、今度は仕様に即してテスト書こうか。

counterという値に1を設定した場合はsetProcessingと同じ結果になるように、counterという値に0を設定した場合はendProcessingと同じ結果になるようにします。

まずは、counter1の場合。

CountDownControllerTest

public void testCountDown() {
        controller.countDown(1);
        assertFalse(startButton.isEnabled());
        assertTrue(stopButton.isEnabled());
        assertFalse(seekBar.isEnabled());
    }


仮実装(コンパイルを通るだけ)にした上で、テストを実行



もちろん落ちるので、仮実装します。
CountDownController

public void countDown(int counter) {
        setProcessing();
    }


テスト結果


次にcounter0の場合。

CountDownControllerTest

public void testCountDownOnEnd() {
        controller.countDown(0);
        assertFalse(startButton.isEnabled());
        assertFalse(stopButton.isEnabled());
        assertTrue(seekBar.isEnabled());
    }


テスト結果


落ちるので、本格的かつシンプルに実装する。

CountDownController

public void countDown(int counter) {
        if(counter == 0) {
            endProcessing();
        } else {
            setProcessing();
        }
    }


そして、テスト

グリーン。

int0とか1というきわどい値をやっているので、-1をやらないわけがない。

CountDownControllerTest

public void testCountDownOnException() {
        try{
            controller.countDown(-1);
            fail();
        } catch(IllegalArgumentException e) {
            assertTrue(true);
        }
    }


そしてテスト

すぐさま期待する動作になるように実装。

CountDownController

public void countDown(int counter) throws IllegalArgumentException {
        if(counter == 0) {
            endProcessing();
        } else if(counter > 0) {
            setProcessing();
        } else {
            throw new IllegalArgumentException();
        }
    }


そして、すぐテスト

グリーン!( ゚Д゚ノノ☆パチパチパチパチ

このあと、ユーザーコード(つまり開発者が使うコード)のためにインターフェースを作成したいのだけど、それはみなさん考えてみてください。

こういう感じでAndroidでもTDDができるので、しかもAndroidPOJOでもTDDができるので、ぜひとも取り入れてみてはいかがでしょうか?

Androidテスト祭りでTDD晒してきたよ

Androidテスト祭りが開催されました。


ご来場いただいた皆様、USTをご覧になった皆様、および講演を引き受けてくださった皆様、大変ありがとうございました。
この場を借りて御礼申し上げます。

また、忙しい中、準備に奔走したスタッフの皆様お疲れさまでした。

当日の模様ですが、Togetterなどで少しまとめられております。

@sakura_bird1 のAndoroid テスト祭り実況

あと、手前味噌ですがこちらでもまとめています。

さて、恥ずかしながら、私も拙いスキルを晒して参りました。

プチライブコーディングとかはABC2011Summerにて晒していたし、Android埼玉支部の支部会でも晒してきたので、それほど緊張はしていないつもりでしたが、やはり70人、UST150人を前にしてのTDDはプレッシャーがありますね。全然スピードが悪かったと思います。

まあ、お題はいつものアレです。

そう、ボーリングですね。

比較的面倒なロジックな割にはルールは結構知られているという面白い題材です。

まあ、実際にはTDD以外にも仕様化テストの話もして参りました。
仕様化テストの話は明日にでも書きます。

資料は次のとおりです。



きっと、いろいろな方もブログを書かれると思いますので、私なりに簡単にまとめました。


KeyNote :: より効率的に開発するために

井芹 洋輝(TDD研究会) 太田 健一郎(TDD研究会)


いや、これはレベルが高かった。
単純に言うと、Activityにビジネスロジックを書くな」ということですね。

ゲームでもなんでもいいんですが、アプリケーションの中心となる機能(ゲームで言えば、ボールが当たるとか、点数を計算するなど)をActivityに書いてしまうと、途端にアプリケーションのテストができなくなって、品質を担保できなくなるということが話しの中心でした。
そして、テスト容易性を考慮しつつアプリケーションの設計を行おうというものです。

結構内容は高度なもので、ボリュームもたくさんだったため、ふたりとも早口でした。
というわけで、レベルが高いかな…などと思いました。

ちなみに、二人に聞いたところ、「妥協を一切しなかった」とのことです。

Android製品テストマップ

鈴木 利彦(株式会社ベリサーブ)


え~っと、まず、すんませんした!…m(_ _)m
どうやら、私が太田さんのノパソの操作を誤ったらしく、スクリーンが途切れてしまいました。
その後も回復がうまく出来ず、snskさんに応急処置をしてもらいました。
いや、ほんと、まじすんませんした。

話は端末の品質の話から、テスト観点の話など多岐にわたりました。

印象的だったのは、今後品質の良くなるメーカーの特徴を述べていたところで、社名は伏せられていましたが、BTSに登録したイシューに真摯に対応している端末メーカーの品質が向上しているとのことです。

品質を向上させるためには日々少しずつ改善していく必要があり、それにちゃんと取り組んでいるメーカーにはノウハウが溜まっていくであろうという話でした。

Android受け入れテストガイドライン

生路 茂太(株式会社ACCESS)


製品のQAに関するお話です。
フィードバックされた意見をどのようにして次のリリースに活用するかといったあたりの分析の仕方が述べられていました。


Android向けUIテスト自動化ツール「Scirocco」のご紹介

立花 優人(株式会社ソニックス/ファウンダー スマートデバイスソリューション事業部 アーキテクト)


UIテストツール「Scirocco」の紹介とデモです。

通常、UIテストをJUnit形式で書こうとすると、10行以上のテストコードになりますが、Sciroccoを使うとほんの2~3行でテストコードが書けるようになります。
UI周りのテストを書く場合には非常に参考になりますね。
Scirocco自体はオープンソース「Apache2.0ライセンス」で提供されています。

なお、ソニックスさんはこれほどよいツールを何故無償提供したかというと、「社会貢献」だそうです。
大人ですね~。

SQLite 周りのテストをしよう

@ussy00 (Androidテスト部/株式会社ヌーラボ)


uss00さんがすんごいのを作ったそうです(棒
自分の番が次なので内容をほとんど覚えていない…

とりあえず、SQLiteのテストをやり易くするためにライブラリーを作ったとのことで、
GitHubにて公開されています。

招待講演 :: テスト可能なUI設計パターン

MVCとかMVPといった責務の役割をAndroidにも適用しようというお話です。
実はAndroidの開発になってからJavaのプロダクトコードの品質が7~8年前のレベルに戻ってしまったそうです。
MVCとかMVPというのは古い、使い古された考えですが、目的は責務の分割です。しかし、現状AndroidのアプリではActivityにモデルロジックが書かれてしまったり、Viewの役割をもつコンポーネントにモデルが記述されたりしていて、責務分割が忘れられている傾向があるようです。
原因のひとつはAndroid入門書の本をそのまま写経することなどがあったりします。(これは悪いことではない)。
ただ、入門書はその本としての可読性を優先するために、ソフトウェアとしての品質を犠牲にしています。
なので、責務分割は必ずして品質を担保しようということでした。
ちなみに紹介されたAndroid-Bindingは試してみたいと思います。

クロージング :: これからのアンドロイドテストの話をしよう

宮田 友美 (Androidテスト部部長/株式会社オープンストリーム)


これからのテストの話しです。
これまでのテスト部は「どうテストをするか」にフォーカスを当てていましたが、今後は「ユーザーは何を求めているのか」という方向にも進んでいくという宣言がなされました。
また、テストの自動化も積極的にやっていきたいし、testterのコミッターももっと増やしたいとのこと。

あと、私事でしたが、お子様が3/9予定日だということです。おめでとうございます。


魂心会もとい懇親会

いろいろな方とお話…しませんでした…orz
LTの準備全然やっていなかったので…

というわけで、Groovyの宣伝だけしました。
他のLTで、一番衝撃的だったのは、某G**gleの社員さんでNative Driverの開発者の方がLTしたことでしょうか。
いや、むしろメインセッションでやってほしいくらいでした。

懇親会ではKORODROIDさんや、デ部の方々とご挨拶致しました。
また、Android埼玉支部ではいつもお世話になっているねこすけさんともご挨拶いたしました。

今後ともよろしくおねがいします。

まあ、こんな感じですが、こんごともAndroidテスト部よろしくお願いします。

2011年7月18日月曜日

Android Bazaar Conference 2011 Summer に行ってきました。

国内最大のAndroidに関するイベントAndroid Bazaar Conference 2011 Summer に参加してきました。

私自身は一般参加として登録しましたが、Androidテスト部の展示のお手伝いをしてきました。

当初、monkeyrunnerでテスト部で作成したTwitterクライアントを実機上で走らせるデモを行う予定だったのですが、準備不足マシンの調子が悪く、スムーズにデモを開始できなかったため、一人TDDデモをやってみました。

ネタはいつものアレです。


お題:ボウリングのスコアを算出するプログラムを作成せよ。

  • 条件
    • テストコードとソースコードを作成する。
    • 入力は配列もしくはjava.util.List<Integer>の形で与えられる。
    • ストライクの後には0が設定されるものとする。
    • ガーター、ミスはともに0が設定されるものとする。

というやつです。

これを声を大きくしながら実演すること6~7回。
さすがに疲れましたが、
  • TDDというのを聞いたことがあったが、実際にこうやるものだと思わなかった。
  • 非常に面白かった。
  • 今度会社でやってみようと思う。
といった声をきかせていただいたので、非常に満足な結果になったと思います。

実はこれ、Androidというプラットフォームに関係の無いネタだったのです。
しかし、ソフトウェアの特性上、何らかの判断などを行うので、表示機能やセンサー周りでないモデル・ロジックの部分での開発にTDDを組み込んでいくというのは避けて通れません。
その点でTDDにより常にフィードバックを受けて、自信を持って開発していけると、最終的に(保守的にも新規機能追加に関しても)品質の高いソフトウェアを開発できると思います。



個人としてはAndroid Developers' Club (デ部)のゆかいな仲間たちによる「Android3.0/3.13.2 HoneycombとAPI解説」を聞いてきました。

最近人気のあるArduinoなどのサポートが強化されておりますし、各種コントローラーをつなげることもできるようになっているあたりは非常に面白いと思います。
また、3.0以降に追加されたFragmentについても簡単ですが解説されていて、実際触ってみたいと思いました。

…エミュレーターが重くなければなw

2011年7月16日土曜日

今すぐフォローしておきたいAndroid テストクラスタ

ネタでtweetしたら、色々追加されたので、改めて適当に脚色をつけて紹介。

  • @miyatay
    Android テスト部の部長です。
    エンタープライズでAndroidアプリを開発していて、JenkinsとかMavenとかも活用しているらしいです。テストを書かないでコミットすると以下略。
  • @ussy00
    「ぬ」社所属のクールなプログラマ
    淡々とチケットを登録して、タスクを消化していく様がとてもカッコイイ。コミットにチケットが関連付けられていないと気持ち悪いらしい。
  • @nowsprinting
    Android テスト部 ソースカツ課の頼れるリーダー
    Androidテスト部公認(?!)アプリ(リリースされていませんがw)testterの最もコミット数の多いリーダーです。testterのコンポーネントに関して胸ぐらつかみ合いの大げんかをして、「テストってなんだろう?」と海を見つめながら語った記憶が…(嘘)
  • @sassy_watson
    ダッフィー好きなプログラマー
    Android用くるくる回るブラウザを作ったり、Java初心者プログラマーにJUnitテストを丁寧に教えるイケメンプログラマー。イケメンと煽ると反応が(ry
  • @oota_ken
    テストについて語らせると、とどまることを知らないテストプロフェッショナル
    TDDに関しての研究や実績があるAndroidテスト部最強のソルジャー。実はAndroidよりWP7の方が(お察し下さい)。
  • @myb1126
    Android 横浜PF部の人で、相当なギーク
    Javaは知らない(自称)らしいですが、Jythonでmonkeyrunnerを走らせるのが得意。効率悪い仕事をするともれなく突っ込まれる。ほむほむと呼ぶとアイコンが変わるというもっぱらの噂。
  • @bols_blue
    大学院でgccポーティングオレオレコンパイラーを作った猛者
    JUnit~monkeyrunnerまで幅広くカバーするAndroidテスト部のきのこ。オレと住んでいるところが近いため、センドロにてただいま休戦協定中。著書多数。
  • @dicea
    所謂上流テストツールを作っている会社の結構偉い人
    Android、DS、iPhone、などの操作をシミュレートするツールを製作している会社の偉い人です。ガンダムマニアなので、ガンダムのネタを振ると食いついてきます。
  • @kandayasu
    chromeに詳しい頼りになるプログラマー
    Chromeの操作に迷ったら何でも教えてくれる、頼りになるプログラマー。Eclipseでのテスト実行の遅さを嘆き、Antでテストしていく強者。

オレあまりAndroidクラスタのコアな人達知らんので、これくらいしか挙げられんっす。情弱者ですいません。


番外編

Androidというよりはテストの人たち。

  • @snsk
    Androidテスト部の副部長
    マックとお酒とデシジョンテーブルを愛するテストマネージメントプロフェッショナルな勉強家。行動力もあって、流石副部長というお方です。
  • @goyoki
    組み込みTDD王子
    TDDの研究・普及に活躍するテスト会のイケメン王子。@snsk副部長のTDDの師匠であり、@oota_kenのよきパートナーである。テストでわからないことがあれば、迷わず質問すべきである。







オレ?
しがないエロオヤジですよ。

2011年6月27日月曜日

ちょっとやりたいアプリメモ

やりたいアプリのメモ

  • 恋ゲー的情報処理試験アプリ
  • Chrome Extension : ページ内の一部の文章を選択したら、その部分と、ページのタイトルと、URLをTwitterに投稿する
  • 英語、フランス語、スペイン語から日本語への翻訳英会話Androidアプリ - 大学で三ヶ国語(英語、スペイン語、ラテン語)をやっていたオレにしかできないアプリ

今んとこはこれくらいかな

しかし、昨日、英語での音声出力ができなかったのがショックで、3.は頓挫中。
だれかオレにアドバイスを~(涙)

2011年6月21日火曜日

Androidでも流れるインターフェース

無駄に流れるインターフェースでOnClickListenerを実装してみた。

まずはアプリの仕様から。

画面仕様
  • 最初の画面はTextViewButtonで構成される。
  • Buttonをクリックすると、次の画面に移動する。
  • 次の画面はTextViewButtonで構成される。
  • Buttonをクリックすると、最初の画面に移動する。

非常に単純な作りですね。
ちなみにこれは次の記事でテストを書くのでよく覚えておいてね。


で、百聞は一見にしかずで、これまた画面の動きをハードコピーしました。


この画面で「push me!」を押すと、


この画面になります。

多分、Androidをやっている人たちなら、この画面簡単に作れちゃうでしょう。
いや実際オレも30分くらいで完成しました。
では、これからオレがdisるコード例を示します。


class MainActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        View view = findViewById(R.id.button);
        view.setOnClickListener(
            new OnclickListener(){
                @Override
                public void onClick(View v){
                    Intent intent = new Intent(this,
                            NextActivity.class);
                    startActivity(intent);
                }
            }
        );
    }
}


読みづらいですね。
まだ、OnClickListenerがひとつだからいいものの、複数のViewにボタンを設定するとなったら、発狂しそうですね。

というわけで、オレが実装したのは次のようなコードです。

class MainActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Create.activity(NextActivity.class)
            .from(this)
            .listenOn(R.id.button);
    }
}


インターフェースを単純につくっています。
まずはインターフェースから。


interface MoveActivity extends OnClickListener{

    public MoveActivity from(Activity preActivity);

    public MoveActivity to(Class<? extends Activity> nextActivity);

    public MoveActivity listenOn(int view);
}


インターフェースはこれだけです。
前のエントリとほとんど変わりません。変わったのはlistenOn(int view)を付け加えたあたりですね。
したがって、これを実装したクラスのテスト性もほとんど変わりません。

これの実装が頑張りどころです。

class Create implements MoveActivity {

    private Activity preActivity;
    private Class<? extends Activity> nextActivity;

    public static MoveActivity activity(
            Class<? extends Activity> newActivity){
        return new Create()
                .to(newActivity);
    }

    @Override
    public MoveActivity from(Activity preActivity){
        this.preActivity = preActivity;
        return this;
    }

    @Override
    public MoveActivity to(Class<? extends Activity> nextActivity){
        this.nextActivity =  nextActivity;
        return this;
    }

    @Override
    public MoveActivity listenOn(int view){
        preActivity.findViewById(view).setOnClickListener(this);
        return this;
    }

    @Override
    public void onClick(View view){
        Intent intent = new Intent(preActivity, nextActivity);
        preActivity.startActivity(intent);
    }
}


ここのポイントは普通ならgetInstance(Class<? extends Activity>)とやるメソッド名を、activity(Class<? extends Activity>)としている所です。Javaに慣れている方には若干読みづらく感じますが、あまりJavaに詳しくない人でもこれならActivityを組み立てることできますね。

あともうひとつのポイントは返す型はかならずインターフェースにすること。

これはDevLove Hangar Flight で矢野さんがおっしゃっていた、Javaの型とはクラスではなく、インターフェースのことだというインターフェース原理主義に即しています。これなら、Androidのバージョンが変わってAPIが変更されてもActivity部分は変えなくて済みますからね。

2011年6月20日月曜日

日本Androidの会 6月定例会に行ってきたよ

とりあえず、月曜日なので短めに。

表題の件について、駒場に行ってきました。
19時頃の駒場東大前駅の西口はいいですね。
JKが(以下自粛)

では、レポートを…


HoneyCombアプリつくってみたよ
みやけんさんによるHoneyCombアプリ作成のレポートです。

Android3.0からはAndroid Tab対応になったのはご存知だと思いますが、UI部分がかなり変わってきています。
Android2.xではひとつのActivityがひとつの画面に対応していましたが、HoneyCombアプリではActivityの中にフラグメントとよばれる要素が加わり、複数の画面で構成できるようになったとのことです。(嘘いっているかもしれませんが…)

というわけで、今までのようにActivityの中に、Viewはもとより、ModelもControllerも突っ込んでいたアプリは作り方そのものを見直さないといけないようです。

そうなるとMVCとかMVVM(あまりよく知っていない)とかの話になるのかと思ったのですが、そこまで突っ込んだ話にはなりませんでした。

あとHoneyCombアプリを作成する際に気をつけたいのが
  • とにかくスペックのいいマシンで開発する
  • 活きの良いHoneyCombタブを用意する。
  • やっぱりEclipse
です。

一つ目については、その通りで、スペックのいいマシンでないと、指が止まる→考えていたことを忘れる→何をコーディングしていたのか曖昧になるという悪循環になります。
SIerの悪口を言うのはアレですが、SIerの偉い人はこのあたりのことをあまり理解していないことが多い。そのSIerがExcelからAndroidアプリのコードを作り出すとかやろうとしだしたら、ちょっとやばいことになると思う。

二つ目はAndroid3.0のエミュレーターは重たいので、端末を接続してやるのが多分最適解だと思っています。かなり同意しました。

三つ目ですが、これはどうなんだろう。
IntelliJ IDEAを使っていますが、Androidに関してはEclipseの方がサポートが強い感じがします。テストの実行に関してはAntが一番速いらしいし、融通が効くので(@SmallTest/@MediumTest/@LargeTestアノテーションを有効活用できる)、こちらをおすすめしたいところですが…

ChromeOS/Chromebookについて
丸山先生によるChromeOS/Chromebookの報告。

結論から先に述べると、

Don't buy Chromebook

だそうです。

一応、Googleがかなり本気を入れているらしいのが、ビジネスマシンのクラウド化で、Chromebookはそれをもろに狙っているターゲットマシンという位置づけだそうです。

まあ、確かに業務なんてブラウザだけでできますからね。Chromebookはそれだけの用途にはうってつけだと思います。

セキュリティなどの対策もChromebookではしっかりやっているそうです。

そのなかで、ちゃんとGoogleが企業のデータを預かってちゃんと運用できるのかといったコンプライアンス(?)的なあたりが、冒頭の結論に導かれたようです。

WWDC報告
結構、しゃべってはいけないことが多いらしく、基調講演の内容を主に解説されていました。

個人的には○○○のエミュレーター機能は面白いと思いました。

Windows Phoneについて
興味がわかなかったので、ほとんど右から入って左に抜けていました。
すみません。

ただ、気になったのが、WP7とWP-mangoって何が違うのかよくわからなかったのと、日本では電波法の関係からまだデバッグ用に使える端末がないらしく、ちょっと暗雲が立ち込めていますね。

台湾の報告
台湾のAndroid端末市場についての報告。

向こうではAndroidタブが流行っているそうですが、そのほとんどがAndroid2.2系のものが主流だそうです。

オレ的にはChromeOSの話が聞ければ満足だったので、まあ、参加してよかったと思います。

2011年6月19日日曜日

Android埼玉でJUnitのセッションをしてきました。

Android埼玉でJUnitについてセッションの時間を設けていただいたので、私見ながらJUnitの紹介をしてきました。

まず、その前に、Android埼玉、オレ遅刻してきましたorz
ひどいですね。発表者なのに…
進藤さんご迷惑をおかけいたしました。

埼玉Androidの会の方は藤原さん(@zegenvs)さんが主催するモクモク会をメインに参加していて、定例会の方は実は二回目でした。
というのも、埼玉Androidの定例会はたいていテスト部MTGとかぶることが多くて、テスト部メインなオレは泣く泣く埼玉Androidに参加できなかったのです。(半分ウソつきました。)

以下、今回セッションの時間をいただけた経緯です。
  • 社内用にJUnitの資料をGoogle Docsで作成
  • 特に秘匿にする必要性を感じなかったので、Twitterでアドレス付きでつぶやいた
  • 埼玉支部長の進藤さんから埼玉Androidの会でぜひと…
という感じですね。
30分時間をいただけるなんて光栄です。
ありがとうございます。

あと、資料の誤りを指摘してくださった、千葉Androidの会(#chibandroid)のnouzui2007さんありがとうございます。



で、他の方の発表ですが、一人未完の資料をどう持っていくか作戦を練っていて
モクモクしていやがったので、左耳から入って右耳から出て行っててあまり覚えていません。
あいかわらず失礼なやつだ、オレ。

一応、記憶に残っているプレゼンで以下の二つがあります。

めーめー会
@chocokanpanさんが2~3ヶ月に一度開催する、ジンギスカンを食べる会のLTです。
実は埼玉Androidの会でLTするのが実は二回目で、その初回が第二回埼玉Androidの会だったそうです。
で、それの何が印象に残っているかというと、オレの勉強会デビューがまさにその第二回埼玉Androidの会だったわけです。
そして、埼玉Androidの会のセッションデビュー日とめーめー会の二回目のLTが重なるあたり、奇遇だな~と感心していた次第であります。

スマフォビジネスの事情
Androidアプリ開発者の皆様ならご存知のアプリ紹介サイト[アンドロイダー]を運営するトリワークスさんによるスマートフォンのビジネスモデルのおはなしです。

Androidアプリでビジネスを展開するための非常に有益な情報が提供されたと思います(スルーっと解説)。
で、実はトリワークスさんも埼玉Androidの会で講演するのは二回目でして、その初回はなんと、第二回埼玉Androidの会。これまた偶然ですね。トリワークスさんも@chocokanpanさんに「奇遇ですね」とおっしゃっていました。

そいで、私のプレゼンですが、
いつものTwitter口調でずっとやっていました。
そして、人生初のライブコーディングしましたが、あれは、練習が必要ですね。
内容はすでに頭の中にあるんですけど、手が追いつかないのと、クビが痛いのと、焦るのとで、、、いい経験させてもらいました。
資料はこんな感じです。




オレとしては珍しく、懇親会に参加しました。
土曜日サイコー!

主だった話は埼玉Androidの会でロゴマークを作るということで、
おざわ(@osa030)さんのロゴの話題になりました。

おざわさん曰く、

タマといえば「○んたま」でしょ!


かなり大爆笑でした。

愉快な方達がいる埼玉Androidの会、とても楽しかったです。
埼玉Androidの皆様、今後とも宜しくです。

2011年6月13日月曜日

簡単なActivityInstrumentationTestCase2の書き方

Androidのプログラムの作成方法については、いろいろなサイトで有用な情報が得られます。
しかし、テストとなるとあまり情報が多くありません。

というわけで、
簡単なActivityInstrumentationTestCase2<T extends Activity>の書き方を
記事にしました。

今回のテスト対象のアプリケーションはこんな感じです。
概要
  • EditViewButtonTextViewで構成されている。
  • EditViewに文字を入力して、Buttonを押すと、TextViewに「Hello, + [EditViewの文字]」が表示される。



まあ、百聞は一見にしかずなので、画面イメージ出しましょう。
これが最初の画面です。


そして、これに文字を入れて、ボタンを押すとこうなります。






















では、早速テストを書きましょう。
まずは、コンストラクターとsetUp()から。


class HelloAndroidTest
        extends ActivityInstrumentationTestCase2<HelloActivity>{

    private Activity activity;

    private Instrumentation instrumentation;

    public HelloAndroidTest(){
        super(HelloActivity.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        activity = getActivity();
        instrumentaion = getInstrumentation();
        setActivityInitialTouchMode(false);
    }        
}


まず、ここのポイントは
  • コンストラクター
  • setUp()中にあるActivityInstrumentationTestCase2#getActivity()
  • 同様にsetUp()中にあるActivityInstrumentationTestCase2#getInstrumentation()
ですね。

コンストラクターではテスト対象のアクティビティ・クラスをActivityInstrumentationTestCase2に渡してやります。「総称型(ジェネリクス)でクラスを指定しているんだから、なんでわざわざクラスを通知してやる必要があるの?」という声も聞こえそうですね。すこしだけ解説すると、タイプパラメーター<T>というのは、ただただタイプとして機能するだけで、java.lang.Class<?>オブジェクトのような機能や、役割を一切持ちません。

したがって、

        Class clz = T.class;

とか、

        boolean isInstance = object instanceof T;

とか、

        T object = T.newInstance();

などはすべてコンパイルエラーになります。

これよりも詳しい説明は私が説明するよりも他にもっと優れた解説があります。Java総称型メモなどを参考にしてください。

話を元に戻すと、ActivityInstrumentationTestCase2で、テスト対象のアクティビティを起動するために、そのクラスの情報が必要なわけですね。さて、これジェネリクスで指定したクラスと異なるクラスを渡したらどうなるのか?わかりませんねぇ。あとでやってみましょう。まぁ、予想できる結果として、この後の解説で述べる#getActivity()java.lang.ClassCastExceptionが発生するように思います。

setUp()中にあるActivityInstrumentationTestCase2#getActivity()はActivityを起動して、参照を返すメソッドです。これにより、テストコードはテスト対象のアクティビティをそのコンテクスト上で起動することが可能になります。テストはそれ自体でひとつのアプリケーションですが、テストアプリケーションとは別のスレッド上にテスト対象のアプリケーションが起動するのです。これをsetUp()に置くことで、毎回のテストがアクティビティを起動した状態で実行できます。

最後に、setUp()中にあるActivityInstrumentationTestCase2#getInstrumentation()です。このInstrumentationは、アクティビティの起動とは何の関係もありませんが、アクティビティのUIスレッドと同期をとるような場合や、UIに対して操作を行う場合に必要になるオブジェクトです。今回のテストアプリケーションでも使いますので、最初にその参照を取得しておきます。

では、テストコードをどうぞ。


    public void testPressButton() {
        // EditText にフォーカスを当てる ---- (1)
        EditText editText = (EditText)activity.findViewById(
                orz.mikeneck.hello.R.id.edit_text);
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                editText.requestFocus();
            }
        });
        // UIとの同期をはかる ---- (2)
        instrumentation.waitForIdleSync();
        // UIにキーを送る ---- (3)
        sendKeys(KEYCODE_SHIFT_LEFT);
        sendKeys(KEYCODE_A);
        sendKeys(KEYCODE_N);
        sendKeys(KEYCODE_D);
        sendKeys(KEYCODE_R);
        sendKeys(KEYCODE_O);
        sendKeys(KEYCODE_I);
        sendKeys(KEYCODE_D);
        // 前提条件の確認 ---- (4)
        assertEquals("Android", editText.getText().toString());
        // Button をクリックする ---- (5)
        Button button = (Button)activity.findViewById(
                orz.mikeneck.hello.R.id.button);
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                button.performClick();
            }
        });
        // UIとの同期をはかる ---- (6)
        mInstrumentaion.waitForIdleSync();
        // TextView の値をテストする ---- (7)
        TextView textView = (TextView)findViewById(
                orz.mikeneck.hello.R.id.edit_text);
        assertEquals("Hello,Android", textView.getText());
    }


大雑把な解説ですが、
  • EditTextにフォーカスを当てます。これはUI上の操作に当たるので、Activity#runOnUiThread(java.lang.Runnable)を使用します。
  • UIとの同期をはかります。別スレッド(アクティビティ)との同期はInstrumentationwaitForIdleSync()を用います。
  • UIにキーを送ります。android.view.KeyEventをstatic importしておいてください。
  • EditTextにキーを送ったので、それが反映されていることをテストします。想定される値は「Android」です。
  • Buttonをクリックします。これはUI上の操作に当たるので、Activity#runOnUiThread(java.lang.Runnable)を使用します。
  • UIとの同期をはかります。別スレッド(アクティビティ)との同期はInstrumentationwaitForIdleSync()を用います。
  • Buttonをクリックしたので、仕様通りにTextViewのテキストが変更されているかテストします。想定される値は「Hello,Android」です。
となります。

ポイントは
  • UI上の操作は必ずActivity#runOnUiThread(java.lang.Runnable)を使用する。
  • UI操作後はInstrumentationwaitForIdleSync()を用いる。
です。

#runOnUiThread(java.lang.Runnable)を使わないとUIが操作できないのは、他のアプリケーションからいくらでも値を変更するなどの改変が行われてしまうからというセキュリティ的な側面があるのかと思われます。(Javaそんなに詳しいわけではないので、そのあたりを突っ込んでくれる人、大募集)

二番目のInstrumentationwaitForIdleSync()ActivityInstrumentationTestCase2の真骨頂です。UIスレッドとの同期をはかりスムーズにテストを実行出来るようなオブジェクト類が提供されており、それがActivityInstrumentationTestCase2です。別スレッドのUIと同期するこの能力はテストの実行においてかなり強力な機能です。



こんな感じで、非常に簡単なテストでしたが、みなさまもUI上で不具合を発見したら、なるべくテストコードを書いて、それらを再現できるようにしてみてください。きっと、素晴らしいUIをもつアプリケーションが作成できると思います。

2011年6月9日木曜日

会社でJUnit勉強会をやってみたよ

会社でJUnit勉強会なるものを開いてみました。


JUnit勉強会と言っても、なんか高度なことをやる意図は持ってなく、単純にJUnitの使い方、実際のプログラムへの適用、TDDのやり方の紹介みたいな感じでやってみました。

資料はここを参照

で、結果ですが、

かなり失敗だった


と思います。

原因
  • JUnitの動作を図解したほうが分かりやすかったと思われる。
  • 最初のテストをHello Worldにしたのがまずった。
  • さらにボウリングスコアの計算というTDD定番のネタで研修したあたりがまずった。

1.については対象者がJavaを始めて1ヶ月というAさんと、入社3年目でJava経験2年のBさんであり、まだ、Javaのmainメソッドからの起動を追いかけるというレベルでのsetUpなどの動きを伝えるには、やや高度であったあたりが、厳しかったという感じです。

2.についても同様で、Aさんはもちろんですが、レガシープロジェクトで育ったBさんにも、インターフェースを使ったプログラムの記述を説明するのはなかなか厳しかった。(もちろんこれは覚えてもらわないといけないと思っていますが…)

3.についてはTDDの考え方、というか動き方、これを説明しておかなければならなかったというのがあります。

とはいっても、みなさんテストコードの重要性は理解していただいているので、(毎日オレがうるさくテストコード、テストコードと言っているうちに、テストコードマンセイになった)徐々に実力を向上させていきたいと思う次第であります。

それと同時にIntefaceを介在させることの説明について、
まだまだ自分がうまく説明出来ていないというのを
発見できたので、今後自分ももっと噛み砕いて説明できるようになりたいと思う次第であります。

というわけで、明日はより高度なAndroidのActivityInstrumentationTestCase2<? extends Activity>の
勉強会をやりたいと思います。


つ~か、ActivityInstrumentationTestCase2<? extends Activity>はジェネリクスを使っているあたりで、説明が頓挫しそうな予感がする。

さらに、JUnit勉強会資料を公開した瞬間に@ryu_compin(日本Androidの会埼玉支部、支部長)さんから、埼玉Androidの会でセッションやってというオファーが来ました。30~40分枠をいただけるということで、なんとも光栄の至りでございます。

ん~、でも皆さん、あくまで単なるJUnitでございますよ…

2011年5月27日金曜日

Androidテスト部の宣伝してきました。

ワイヤレスジャパン2011でAndroid LTセッションがあり、参加してきました。

たまたま日本Androidの会にてLT参加者募集ということでしたし、
実は『Androidテスト祭り』実行委員にも関わらず何のタスクもなかった
(できるものが何もなかった)ので、
宣伝がてら参加することにしました。

いや、こういう書き方すると、
イヤイヤ参加しているように読めますが、
実際は目立ちたがり屋なので、
喜んで参加している次第です。


全体的な印象ですが、やはり管理者層の方が多く、
ワイヤレスジャパンでAndroidの情報収集をしている人は
これからAndroid開発でのビジネスを考えている方のようでした。

そんなわけで町田支部の@ngsw_taroさんのAndroidアプリ開発LTは
熱心にメモを取られている方が多かったように思います。

で、私のLTですが、
タイトルは『Androidテスト祭り~僕とテストしてRIA充になろうよ』。

で、これがその資料です。


結果的にはすべりまくりでした。

  • 「ねえ、僕と契約して魔法少女になってよ」というネタはプログラマーには受けるネタですが、管理者層の人にはさっぱり伝わらないこと
  • ワイヤレスジャパンでAndroidの情報を収集している人は既にリア充(いや、そういうわけでもないだろうけど)なので「RIA充」と言っても意味が通じないこと
  • 「王子で王子が語る」というベタなネタは意外と受けが良いこと

こんな感じで、あとで司会の関西の方に突っ込まれまくりました…

ただ、まあありがたい事に私のイベントの周知もメモをとってくれている方を
ちらほら見かけましたので、
宣伝としては成功したのかもしれないような気がします。

2011年4月3日日曜日

AndroidのOnClickListenerをテストしよう

最近、Androidの本、よく売れていますね。

本屋でもJavaのコーナーよりも場所が広かったりします。

で、それらに掲載されているコードに文句をつけるつもりは毛頭ございませんが、

書いてあるのをそのまま参考にすると、テストで痛い目を見ます。

では、まずダメなアプリの例から。


package orz.mikeneck;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import  android.widget.Button;

public class FirstActivity extends Activity {

 /**
  * @see android.app.Activity#onCreate(android.os.Bundle)
  */
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  // Setting layout to a display.
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  // Adding onClickListener to a button.
  Button button = (Button) findViewById(R.id.button_id);
  button.setOnClickListener(

   new View.onClickListener() {
    public void onClick(View v){

     // If this button is pressed, new activity will be launched.
     Intent intent = new Intent(this, NextActivity.class);
     startActivity(intent);
    }
   }
  );
 }
}


ダメであるという理由は、次のテストが実施できないからです。

・ボタンを押したときに、指定したActivityが起動すること

なぜテストが実施できないかというと、

オブジェクトButtonというよりはViewには、
setOnClickListener(android.view.View.OnClickListener)はあっても、
getOnClickListener()がないため、
ボタン(View)を押すという操作をテストから実行することができないからです。

一般的な開発工程のあり方としては、単体テストでバグを摘出するほうが、結合試験以降でバグを抽出することよりもコストが低いと言われています。

結合テストまでこのバグが潜んでしまっていた場合、どのクラスによって発生したのか切り分ける必要があるため、念入りに調査することが必要になります。

プログラマーで勘の良い人はだいたいどの辺にバグがあるのかを検出するのが速いわけですが、誰しもがそうではないわけで、こんな単純なバグが原因だったのかと呆れてしまうこともあったりします。

私もSIに務めているので、そのようなことはよくあったりします。

なんか画面のボタンを押したときに、条件を限定しているわけではないのに、なにか限定された結果が得られて、どのような条件でどのような操作をするとバグが発生するのか、詳細にバグ票に書かなければならないので、結構しんどかったりします。

で、その結果、実はボタンを押したあとの挙動にハードコーディングがしてあったとかよくありました。

…最近は上流のテストはやっていないので、そんなことはなくなったのですが…

話を戻すと、View.onClickListenerはせっかくinterfaceなのだから、それを有効活用しない手はありません。

というわけで、以下がまだましな例。
FirstActivity.java

package orz.mikeneck;

import android.app.Activity;
import android.os.Bundle;
import android.view.View.OnClickListener;
import  android.widget.Button;

public class FirstActivity extends Activity {

 /**
  * @see android.app.Activity#onCreate(android.os.Bundle)
  */
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  // Setting layout to a display.
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  // Getting instance of OnClickListener.
  View.onClickListener listen = new OnClickListenerImpl();
  listener.setPreContext(this);
  listener.setNextContext(NextActivity.class);

  // Adding onClickListener to a button.
  Button button = (Button) findViewById(R.id.button_id);
  button.setOnClickListener(listener);
 }
}


ここで、先程の例ではActivity内にあったFirstActivityの中にあった、
View.onClickListenerの実装が外に出されました。

で、これが外に抽出されたクラスOnClickListenerImpl.javaです。

package orz.mikeneck;

import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;

public class OnClickListenerImpl implements OnClickListener {

 /**
  * Previous Context listening user motion. 
  */
 private Context preContext;

 /**
  * Next Context to be launched.
  */
 private Class nextContext;

 /**
  * If the button is pressed, the next Activity will be launched by this method.
  * And, this method can be called by external classes.
  */
 @Override
 public void onClick(View v) {
  Intent intent = new Intent(preContext, nextContext);
  preContext.startActivity(intent);
 }

 public void setPreviousContext(Context preContext) {
  this.preContext = preContext;
 }

 public void setNextContext(Class nextContext) {
  this.nextContext = nextContext;
 }


}


そうすると、これである特定の画面から、次の画面を呼び出すという挙動のテストができるようになります。
以下はテストコード。
OnClickListenerImplTest.java

package orz.mikeneck.test;

import android.content.Intent;
import android.test.mock.MockContext;
import junit.framework.TestCase;
import orz.mikeneck.OnClickListenerImpl;

public class OnClickListenerImplTest extends TestCase {

 private OnClickListenerImpl listener;

 private boolean isTestOnClickPassed;

 @Override
 public void setUp(){
  this.isTestOnClickPassed = false;
  listener = new OnClickListenerImpl();
  PreContext preContext = new PreContext();
  listener.setPreviousContext(preContext);
  listener.setNextContext(NextContext.class);
 }

 /**
  * Click the button(fake).
  */
 public void testOnClick(){
  listener.onClick(null);
  assertTrue(isTestOnClickPassed);
 }

 private class PreContext extends MockContext{

  /**
   * @see android.test.mock.MockContext#startActivity(android.content.Intent)
   */
  @Override
  public void startActivity(Intent intent) {
   super.startActivity(intent);
   isTestOnClickPassed = true;
  }
  
 }

 private class NextContext extends MockContext{ 
 }
}


実はView.onClickListenerのメソッドonClick(View v)は、
パブリックメソッドなので、クラスの外から呼び出すことが可能です。

したがって、テストクラスでは堂々とlistener.onClick(null)と呼び出していますね。



ただし、ここからは私のうっかりミスが発覚します。
これをそのままテスト実行すると、もれなく


java.lang.UnsupportedOperationException
at android.test.mock.MockContext.getPackageName(MockContext.java:101)
at android.content.ComponentName.(ComponentName.java:75)
at android.content.Intent.(Intent.java:2551)
at orz.mikeneck.OnClickListenerImpl.onClick(OnClickListenerImpl.java:52)
at orz.mikeneck.test.OnClickListenerImplTest.setUp(OnClickListenerImplTest.java:28)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:430)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)


とかなります。

う~ん、前々から気にはしていたんですがね、android.test.mock.MockContextは、
どうも暴れん坊大将軍様で、呼び出すメソッドすべてがUnSupportedException
返してくれるという素敵仕様になっております。
したがって、テストを通すまでかなりの道のりを経なくてはならない化物なのでございます。

で、今回はExceptionの発生しているメソッドのオーバーライドもしました。

 private class PreContext extends MockContext{

  /**
   * @see android.test.mock.MockContext#startActivity(android.content.Intent)
   */
  @Override
  public void startActivity(Intent intent) {
   isTestOnClickPassed = true;
  }

  /**
   * Added method.
   * @see android.test.mock.MockContext#getPackageName()
   */
  @Override
  public String getPackageName() {
   return "orz.mikeneck.test";
  }
  
 }


これで、見事にテストが通りました。

というわけで、ある特定のActivityから指定したActivityを起動することが出来ているという確認が取れるようになりました。



はい、妙に長ったらしい解説でしたが、

Activity内部でinterfaceの実装を記述しないこと


が今回言いたかったことです。


書いていたら、22時過ぎてしまった…orz

Androidアプリ用のイメージ

Androidアプリ用のイメージ、結構揃えるのが面倒くさいですね。

アプリを作っていて、凝ったアプリではないのでアイコンもそれほど凝らなくてもいいようなアプリを作りたいときがあります。

そういうときはネット上の素材に頼るのがよいようです。

というわけで、簡単にアプリ用のイメージをまとめてみました。

Open Source Icons -- Linux系のアイコンがいっぱいある。これはかなり使いやすい。LGPLだったり、GPLだったりするので、ライセンス関連を気にする人は要注意。

Button Maker -- 簡単な文字の入ったアイコンをつくるのに適したサイト。自分でキーボードを作るときにも便利ですね。

PhotoShifter -- Windows用ですが、gif形式のファイルをpngに変換したり、リサイズしたりできます。コマンドラインでも利用可能だそうです。


ちなみに、
res/drawable-hdpiのアイコンのサイズは72x72
res/drawable-hdpiのアイコンのサイズは36x36
res/drawable-hdpiのアイコンのサイズは48x48
です。