Robotium ile Test Yazmak

Robotium test framework’ü sayesinde fonksiyonel testler yazmak çok kolaylaşıyor. “Selenium gibi, ama Android için” sloganını kullanan framework, test case’lerinizde kullanıcı ile etkileşim gerektiren kısımları otomatik hale getirmenize yarıyor.

Örnek vermek gerekirse, uygulamanızın login ekranında “Gönder” düğmesine tıkla, “Şifre” kutucuğuna “123456″ gir ve ekranda “Hatalı Şifre” kelimesi geçmediğinden emin ol gibi kontrolleri içeren bir test case’i Robotium kullanarak şöyle yazıyorsunuz:

public void testLogin() throws Exception {
        solo.clickOnButton("Gönder");
        solo.enterText(1, "123456");
        assertFalse(solo.searchText("Hatalı Şifre"));
} 

Bu testler Emulator üzerinde çalıştırıldığı için otomatik hale getirdiğiniz hareketleri gözünüzle görebiliyorsunuz. Yazdığınız testlerin geçip geçmediğini Eclipse’in JUnit ekranından takip edebiliyorsunuz.

robotium

Uygulamanızı Robotium ile test edebilmek için ana projenizin AndroidManifest.xml dosyasına aşağıdaki satırı eklemeniz gerekiyor:

<uses-permission android:name=”android.permission.GET_TASKS” />

Test projesinin Build Path‘ine robotium-solo-1.x.x.jar dosyasını ekledikten sonra ActivityInstrumentationTestCase2 class’ını extend eden bir test case yazmalısınız. Örnek bir test projesi görmek istiyorsanız Robotium’un Getting Started sayfasına bakmanızı öneririm. Hazırladığınız test case’i çalıştırmak için Eclipse’de projenize sağ tıklayarak “Run as Android JUnit Test” demeniz gerekiyor.

Test yazmayı kolay ve eğlenceli bir hale getiren Robotium test framework’ünü denemenizi şiddetle öneriyorum.

, , , ,

1 Comment

Android Geliştirme İpuçları (I)

Android platformunda geliştirme yaparken hayatımı kolaylaştıran ipuçlarını buradan paylaşacağım. İşte ilk ipucu:

1. Google Code Search ile Android Kaynak Kodunu İnceleyin
Tecrübeli yazılımcıların dahil olduğu açık kaynak projelerin kodlarını incelemek bana keyif veriyor, çünkü başkaları tarafından yazılmış kodları okurken farklı düşünme biçimlerini görerek kendimi geliştirme fırsatı buluyorum.

Android platformu açık kaynak olmasına rağmen kaynak koduna erişmek oldukça zahmetli. Bu noktada imdadıma Google Code Search geliyor, kaynak kodunu görmek istediğim class’ı classadı package:android şeklinde aratıyorum.

Mesela AsyncTask class’ının işleyişini merak ediyordum, asynctask package:android şeklinde aratarak AsyncTask.java kaynak kodunu incelediğimde içerisinde LinkedBlockingQueue kullanıldığını gördüm.

Başka bir kullanım alanı ise nasıl çağıracağınızı bilmediğiniz fonksiyonların örnek kullanımını görmek için: Mesela ConnectivityManager‘ın requestRouteToHost metodu argüman olarak integer cinsinden bir hostname istiyordu. hostname’i nasıl integer’a çevireceğim konusunda dökümantasyon da açıklayıcı olmayınca, Code Search üzerinden requestRouteToHost şeklinde arama yaparak örnek bir kullanım buldum.

BONUS: Stackoverflow bildiğiniz gibi Android için resmi destek kanallarından birisi. Daha önce kullanmadığım bir class’ı kullanacaksam öncelikle classadı [android] şeklinde android olarak taglenmiş başlıklar arasında arama yaparak insanların neler yapmaya çalıştığını, ne sorunlarla karşılaştığını inceliyorum.

Siz de yorumlarda kendi ipuçlarınızı paylaşırsanız sevinirim.

, , , ,

No Comments

Android’de Harita Üzerine Balon Yapısı Eklemek

Android ile ilgili forumlarda sıkça karşılaştığım sorunlardan birisi MapView üzerine balon yapısı yerleştirmek, daha önce bir proje için üzerinde çalıştığım prototipte layout tabanlı basit bir çözüm hazırlamıştım.

Öncelikle bir layout sınıfı hazırlayıp, dispatchDraw metodu içerisinde balonumuzu çiziyoruz:

package com.dogantekin.baloon;
....
public class BaloonLayout extends LinearLayout {
....
@Override
protected void dispatchDraw(Canvas canvas) {
Paint panelPaint = new Paint();
panelPaint.setARGB(0, 0, 0, 0);
RectF panelRect = new RectF();
panelRect.set(0,0, getMeasuredWidth(), getMeasuredHeight());
canvas.drawRoundRect(panelRect, 5, 5, panelPaint);
RectF baloonRect = new RectF();
baloonRect.set(0,0, getMeasuredWidth(), 2*(getMeasuredHeight()/3));
panelPaint.setARGB(230, 255, 255, 255);
canvas.drawRoundRect(baloonRect, 10, 10, panelPaint);
Path baloonTip = new Path();
baloonTip.moveTo(5*(getMeasuredWidth()/8), 2*(getMeasuredHeight()/3));
baloonTip.lineTo(getMeasuredWidth()/2, getMeasuredHeight());
baloonTip.lineTo(3*(getMeasuredWidth()/4), 2*(getMeasuredHeight()/3));
canvas.drawPath(baloonTip, panelPaint);
super.dispatchDraw(canvas);
}
}

Sonra bu layout’u kullanan bir layout xml’i hazırlayarak, balon içerisine yerleştirmek istediğimiz kısımları belirtiyoruz (örnek olarak başlık ve içerik için iki adet textview ve kapatmak için ise bir imageview):

<?xml version="1.0" encoding="utf-8"?>
<com.dogantekin.baloon.BaloonLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/transparent_panel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="5px"
android:paddingTop="5px"
android:paddingRight="5px"
android:paddingBottom="5px">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/note_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="@string/note"
/>
<ImageView
android:id="@+id/close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:paddingRight="10px"
android:src="@drawable/close"
android:clickable="true"
/>
<TextView
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:paddingLeft="5px"
android:paddingRight="5px"
android:text=""
android:id="@+id/note_text"
android:layout_below="@+id/note_label"
android:layout_centerHorizontal="true"
android:minLines="4"
android:maxLines="4"
android:maxLength="160"
android:textSize="5pt"
/>
</RelativeLayout>
</com.dogantekin.baloon.BaloonLayout>

Evet balon yapımız hazır, şimdi ise ilgili MapActivity içerisinde ilk başta hazırladığımız balon layoutunun instance’ını oluşturmamız gerekiyor:

LayoutInflater layoutInflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
noteBaloon = (BaloonLayout) layoutInflater.inflate(R.layout.baloon, null);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(200,100);
layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
noteBaloon.setLayoutParams(layoutParams);

Bu aşamadan sonra balonumuzu map view üzerinde istediğimiz nokta üzerinde göstermek için:

((TextView)noteBaloon.findViewById(R.id.note_text)).setText(msg.getData().getString(HANDLER_MESSAGE_AUTHOR)+"\n"+msg.getData().getString(HANDLER_MESSAGE_NOTE));
mapController.animateTo(noteOverlay.getTapPoint()); // kullanicinin dokundugu noktayi aliyoruz
mapView.addView(noteBaloon, new MapView.LayoutParams(200,200,noteOverlay.getTapPoint(),MapView.LayoutParams.BOTTOM_CENTER));
mapView.setEnabled(false);

sample baloon

Balonumuzu map view üzerinden kaldırmak için ise:

mapView.removeView(noteBaloon);
mapView.setEnabled(true);

baloon removed

yeterli olacaktır. Layout xml’de istediğimiz eklemeleri yaparak daha zengin içeriğe sahip bir arayüz sağlayabiliriz:
rich baloon

1 Comment