2011年8月19日金曜日

Interfaceに内部enumをつけて、そのenumが他のinterfaceを実装していて、自己満足なコードができている。

Interfaceに内部enumをつけて、そのenumが他のinterfaceを実装していて、自己満足なコードができている。


と、ツイートしたので、その自己満足なコードを晒してみる。


package orz.mikeneck.gae.slim3.model.alarm;

import java.util.Date;
import java.util.TimeZone;

import org.slim3.controller.validator.Validator;
import org.slim3.controller.validator.Validators;

import orz.mikeneck.gae.slim3.util.AttributeKeyValue;
import orz.mikeneck.gae.slim3.util.AttributeValidation;

public interface AlarmDate {

    public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("Asia/Tokyo");

    public static final String KEY_YEAR = "YEAR";

    public static final String KEY_MONTH = "MONTH";

    public static final String KEY_DATE = "DATE";

    public static final String KEY_HOUR = "HOUR_OF_DAY";

    public static final String KEY_MINUTE = "MINUTE";

    public static final long MONTH_MAX = 12;

    public static final long MONTH_MIN = 1;

    public static final long DATE_MAX = 31;

    public static final long DATE_MIN = 1;

    public static final long HOUR_MAX = 23;

    public static final long HOUR_MIN = 0;

    public static final long MINUTE_MIN = 0;

    public static final long MINUTE_MAX = 55;

    public int year();

    public int month();

    public int date();

    public int hour();

    public int minute();

    public Date getCreatedAt();

    enum AttributeMapper implements AttributeKeyValue<AlarmDate>, AttributeValidation {
        ATTRIBUTE_YEAR {
            @Override
            public String key() {
                return KEY_YEAR;
            }

            @Override
            public <T> T value(AlarmDate model, Class<T> klass) {
                return klass.cast(model.year());
            }

            @Override
            public Class<?> type() {
                return Integer.class;
            }

            @Override
            public Validator[] validation(Validators valid) {
                return new Validator[] {
                    valid.required(),
                    valid.integerType()};
            }
        }, ATTRIBUTE_MONTH {
            @Override
            public String key() {
                return KEY_MONTH;
            }

            @Override
            public <T> T value(AlarmDate model, Class<T> klass) {
                return klass.cast(model.month());
            }

            @Override
            public Class<?> type() {
                return Integer.class;
            }

            @Override
            public Validator[] validation(Validators valid) {
                return new Validator[] {
                    valid.required(),
                    valid.integerType(),
                    valid.longRange(MONTH_MIN, MONTH_MAX)};
            }
        }, ATTRIBUTE_DATE {
            @Override
            public String key() {
                return KEY_DATE;
            }

            @Override
            public <T> T value(AlarmDate model, Class<T> klass) {
                return klass.cast(model.date());
            }

            @Override
            public Class<?> type() {
                return Integer.class;
            }

            @Override
            public Validator[] validation(Validators valid) {
                return new Validator[] {
                    valid.required(),
                    valid.integerType(),
                    valid.longRange(DATE_MIN, DATE_MAX)};
            }
        }, ATTRIBUTE_HOUR_OF_DAY {
            @Override
            public String key() {
                return KEY_HOUR;
            }

            @Override
            public <T> T value(AlarmDate model, Class<T> klass) {
                return klass.cast(model.hour());
            }

            @Override
            public Class<?> type() {
                return Integer.class;
            }

            @Override
            public Validator[] validation(Validators valid) {
                return new Validator[] {
                    valid.required(),
                    valid.integerType(),
                    valid.longRange(HOUR_MIN, HOUR_MAX)
                };
            }
        }, ATTRIBUTE_MINUTE {
            @Override
            public String key() {
                return KEY_MINUTE;
            }

            @Override
            public <T> T value(AlarmDate model, Class<T> klass) {
                return klass.cast(model.minute());
            }

            @Override
            public Class<?> type() {
                return Integer.class;
            }

            @Override
            public Validator[] validation(Validators valid) {
                return new Validator[] {
                    valid.required(),
                    valid.integerType(),
                    valid.longRange(MINUTE_MIN, MINUTE_MAX)
                };
            }
        };

        @Override
        abstract public String key();

        @Override
        abstract public <T> T value(AlarmDate model, Class<T> klass);

        @Override
        abstract public Class<?> type();

        @Override
        abstract public Validator[] validation(Validators valid);

        @Override
        public void validate(Validators valid) {
            valid.add(key(), validation(valid));
        }
    }
}


こやつは、Slim3のModelクラスのインターフェースなのですが、何がいいかというと、requestのマッピングとか、Validationのコードなどが劇的にキレイになる。

Mapping

package orz.mikeneck.gae.slim3.controller.alarm;

import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;

import orz.mikeneck.gae.slim3.model.alarm.AlarmDate;
import orz.mikeneck.gae.slim3.model.alarm.AlarmDateModel;

public class IndexController extends Controller {

    @Override
    public Navigation run() throws Exception {
        AlarmDate model = new AlarmDateModel();
        for(AlarmDate.AttributeMapper mapper : AlarmDate.AttributeMapper.values())
            request.setAttribute(mapper.key(), mapper.value(model, mapper.type()));
        return forward("index.jsp");
    }
}


Validators

package orz.mikeneck.gae.slim3.controller.alarm;

import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;
import org.slim3.controller.validator.Validators;

import orz.mikeneck.gae.slim3.model.alarm.AlarmDate;

public class WakeController extends Controller {

    @Override
    public Navigation run() throws Exception {
        Validators valid = new Validators(request);
        for(AlarmDate.AttributeMapper mapper : AlarmDate.AttributeMapper.values())
            mapper.validate(valid);
        return redirect(basePath);
    }
}


Validationとかの部分が二行で書けるので、コントローラーにモデルに関する複雑なことを書かんでよくなる。
まあ、もっといいやりかたがあるんだろうけど、コレが今のオレの程度…orz


2011年8月15日月曜日

OAuth2.0について調べてまとめてみた。

OAuth2.0について調べてまとめてみた。

Google Data Apiを触ってみようと思ったけど、3-legged OAuthよりもOAuth2.0が推奨されていて、それが何のことかよくわからなかったので調べてみることにした。
(Google Data ApiのJavaライブラリーは3-legged OAuthを使っているみたいだけど…)

まあ、詳しくは調べていないので、雑です。

OAuthのフロー(1)
UserはClientを起動する。

OAuthのフロー(2)
ClientはProviderに対して、ProviderにあるUserのリソースにアクセスする許可をUserからもらうように依頼する。

OAuthのフロー(3)
ProviderはClientがUserのリソースにアクセスすることを許可するかどうかをWebページにて確認する。

OAuthのフロー(4)
UserはClientがProviderにあるUserのリソースにClientがアクセスすることを承認する。

OAuthのフロー(5)
Providerは一時的なAccess Tokenを発行する。

OAuthのフロー(6)
Userは一時的なAccess TokenをClientに入力する。

OAuthのフロー(7)
ClientはProviderに有効なAccess Tokenを発行するように依頼する。

OAuthのフロー(8)
Providerはリソースアクセス用のAccess TokenとAccess TokenをリフレッシュするためのRefresh TokenをClientに渡す。

OAuthのフロー(9)
期限時間内はもらったAccess Tokenで持って、Providerと通信をして、Userリソースにアクセスする。
また、Userに対してClientの機能を提供する。

OAuthのフロー(10)
期限時間が過ぎたら、Refresh Tokenを使って、Providerに対して、新しいAccess Tokenを発行するように依頼する。

資料はこんな感じです。


最後の(9)と(10)はまだ検証していないので、ツッコミお待ちしております。

2011年8月10日水曜日

『アジャイルサムライ』読書記録 - 7章 - 見積もり:当てずっぽうの奥義

単なる読書メモなので、あまり読んでもおもろくないと思う。

まあ、SIerはそれをずっと維持し続けようとするから、あとで火を噴く訳だな。
開発なんてやってみなければどれくらいのスピードでできるかわからない。

  • 今後の計画をたてられる
  • 見積もりは当てずっぽうだという前提を踏まえている
  • ソフトウェア開発の複雑さを認めている


このプロジェクトをやり遂げられそうなのか?!は非常に重要なところだと思う。

できないものをできるとかいうと後々痛い目を見そうだし。

7.2 ピンチをチャンスに


  • ストーリーそれぞれを互いに相対的なサイズで見積もる
  • ポイントをもとにして進捗を追跡する



相対的な見積もりについて

ポイントについて

見積もりは確実なものでないとわかっているので、日数でなく、これくらいみたいな感じで見積もっておいたほうがよいってことかな。

SIer「このプログラム何日かかる?」
プログラマー「わかりませんが、この前の仕事の5割増ですね。」
SIer「つまり3日だね?」
プログラマー「どうですかね?この前の仕事は確かに2営業日でできていますけどね。まあ、たぶんこの前の仕事より5割増くらいだということですね。」
SIer「つまり3日だね?」
プログラマー「いや、分かりません。」
SIer「この前のは2日でできたのだから、それの5割増だから、3日だろう。」
プログラマー「どうですかね?この前の仕事は確かに2営業日でできていますけどね。まあ、たぶんこの前の仕事より5割増くらいだということですね。」
SIer「つまり3日だね?」
…以下続く

見積り技法


三角測量

ストーリーの一覧から1回のイテレーションの期間に収まりそうなサイズのストーリーを大中小選び出す。
選び出す際の視点。
  • 論理的なグループ分けができる
  • エンド・トゥ・エンドになっている
  • プロジェクトを象徴するストーリー

スパイク

今まで経験したことのないようなストーリーに対する見積もり方法。
タイムボックス化して数日以内でストーリーを見積もれる程度に様々な調査を行う。

プランニングポーカー

開発メンバーが一つ一つのストーリーを自分自身で見積もる。
その結果をメンバー同士で共有する。
一致していたら、その見積もりにする。
異なっていたら相違について話し合って見積りを出す。

重要なポイントは話し合いがあること。
プランニングポーカーは投票システムではないこと。
ストーリーのサイズは小さくする。(1、3、エピックを表すのに5を使う。)



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テスト部よろしくお願いします。