RoboGuiceについて簡単に触れておくとGoogle GuiceベースのAndroid用のDIコンテナである。
roboguice - Project Hosting on Google Code
細かいViewの制御は抜きにして以下のようなアプリを例としてRoboGuiceを使用して作成してみる。
まず前エントリーを参考にMavenプロジェクトからAndroidプロジェクトを作成。
pom.xmlを追記
- <dependency>
- <groupId>org.roboguice</groupId>
- <artifactId>roboguice</artifactId>
- <version>1.1.2</version>
- </dependency>
TopActivity
- public class TopActivity extends RoboActivity {
- @InjectResource(R.string.message)
- String message;
- @InjectView(R.id.txt_msg)
- TextView mTextView;
- @InjectView(R.id.edt_name)
- EditText mEditText;
- @InjectView(R.id.btn_decision)
- Button mButton;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // mTextView.setText(message); // ここでViewにアクセスするとエラー
- setContentView(R.layout.top); // ここでViewがInjectされる
- mTextView.setText(message);
- mButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- Intent intent = new Intent(TopActivity.this, HelloActivity.class);
- intent.putExtra("name", mEditText.getText().toString());
- startActivity(intent);
- }
- });
- }
- }
ListActivityの場合はRoboListActivity、TabActivityの場合はRoboTabActivityなどそれぞれ対応したもの
が存在する。
ソースを見るとわかる通り、@InjectView(resouceId)でViewをInjectしてくれる。
リソースを参照したい場合は@InjectResource(resourceId)を使用すればいい。
他にもこんなことが可能である。
Drawable icon = getResources().getDrawable(R.drawable.icon); ↓ @InjectResource(R.drawable.icon) Drawable icon;
LocationManager loc = (LocationManager) getSystemService(Activity.LOCATION_SERVICE); ↓ @Inject LocationManager loc;
HelloActivityを実装する前にインターフェースと実装クラスを使用して
少しDIっぽい処理を入れてみる。
RoboSampleService
- public interface RoboSampleService {
- public String hello(String name);
- }
RoboSampleServiceImpl
- public class RoboSampleServiceImpl implements RoboSampleService {
- public String hello(String name) {
- return "Hello " + name;
- }
- }
続いて上記サービスをバインド
MyApplication
- public class MyApplication extends RoboApplication {
- @Override
- protected void addApplicationModules(List<Module> modules) {
- modules.add(new MyModule());
- }
- static class MyModule extends AbstractAndroidModule {
- @Override
- protected void configure() {
- // RoboSampleServiceのInject要求に対してRoboSampleServiceImplを返すようバインドする
- bind(RoboSampleService.class).to(RoboSampleServiceImpl.class);
- }
- }
- }
アプリケーションのエントリポイントとして上記クラスを呼び出す必要があるので
AndroidManifest.xmlに以下を追加
<application... android:name=".MyApplication" ...>
最後にHelloActivityの実装
HelloActivity
- public class HelloActivity extends RoboActivity {
- @InjectExtra("name")
- String name;
- @InjectView(R.id.hello_msg)
- TextView textView;
- @Inject
- RoboSampleService service;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.hello);
- textView.setText(service.hello(name));
- }
- }
@InjectExtra("name")でTopActivityでputExtraした値が入る。
また、RoboSampleServiceに@Injectアノテーションをつけることで上記でバインドした
RoboSampleServiceImplをInjectしてくれるのでインスタンスを生成せず使用することが出来る。
続いてDIの長所を活かしてHelloActivityのテストを書いてみる。
テストプロジェクトを作成する前にテスト対象のプロジェクトのクラスパスをエクスポート。
通常通りAndroidテストプロジェクトを作成する。
今回作成したサンプルプログラムのhelloメソッドは静的な文字列を付加するだけの
シンプルな作りであるが、実際は動的であったり、バグが混入していたり、
決まっていなかったりするので依存するクラスの振る舞いを固定化するモックを作成する。
MockServiceImpl
- public class MockServiceImpl implements RoboSampleService {
- @Override
- public String hello(String name) {
- return "test";
- }
- }
続いてテストコードの実装
HelloActivityTest
- public class HelloActivityTest extends RoboActivityUnitTestCase<HelloActivity> {
- private Context mContext;
- public HelloActivityTest() {
- super(HelloActivity.class);
- }
- class MockApplication extends RoboApplication {
- public MockApplication(Context context) {
- super();
- attachBaseContext(context);
- }
- @Override
- protected void addApplicationModules(List<Module> modules) {
- modules.add(new MockModule());
- }
- }
- class MockModule extends AbstractAndroidModule {
- @Override
- protected void configure() {
- // RoboSampleServiceのInject要求に対してMockServiceImplを返すようバインドする
- bind(RoboSampleService.class).to(MockServiceImpl.class);
- }
- }
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getContext();
- setApplication(new MockApplication(mContext));
- }
- @MediumTest
- public void testShouldBeHelloMsg() {
- Intent intent = new Intent(mContext, HelloActivity.class);
- intent.putExtra("name", "name");
- HelloActivity a = startActivity(intent, null, null);
- assertNotNull(a);
- // Mockで返す文字列を"test"にしているため
- assertEquals(((TextView)a.findViewById(R.id.hello_msg)).getText(), "test");
- }
- }
RoboActivityUnitTestCase<対象アクティビティ>を継承してテストコードを作成する。
RoboSampleServiceのInject要求に対してモックを返すようにバインドさせ、
setApplicationでセットすることで依存したオブジェクトの振る舞いを固定化させることが出来る。
Run As -> Android JUnit Testで...
これで一通り完了。案外使いやすいかも?
0 件のコメント:
コメントを投稿