بایگانی برچسب: s

آموزش اندروید-فصل ۲۹-۳: تست ابزاری اندروید با Espresso

در دو قسمت قبلی این فصل یعنی آموزش اندروید-فصل ۲۹-۱: تست برنامه‌های اندروید و آموزش اندروید-فصل ۲۹-۲: تست برنامه اندروید با JUnit و Mockito درباره اصول اولیه تست واحد یا یونیت تست گفتم. در ادامه می‌خواهم وارد یکی از مهم‌ترین بخش‌های تست برنامه‌های اندروید بشوم: تست ابزاری برنامه‌های اندروید.

اسپرسو چیست؟

اسپرسو یک چارچوب تست برنامه است که در «کتابخانه پشتیبان تست اندروید» یا «Android Testing Support Library» قرار دارد. اسپرسو APIهایی برای شبیه سازی اعمال کاربر و نوشتن تست‌های بررسی عملکرد UI در اختیار ما می‌گذارد. تست‌های اسپرسو بر اساس کارهایی که کاربر می‌تواند یا ممکن است در زمان استفاده از برنامه انجام دهد نوشته می‌شود.

اگر خیلی ساده بخواهم بگویم در این تست‌ها ما

  • عناصر UI را در صفحه پیدا می‌کنیم و
  • با این عناصر تعامل کرده یا وضعیت آن‌ها را بررسی می‌کنیم

تست‌های ابزاری اندروید در شاخه app/src/androidTest/java پروژه قرار دارند و ما هم تست‌های خود را در همین مسیر خواهیم نوشت. تست‌های اسپرسو از سه مؤلفه اصلی تشکیل شده‌اند:

  • ViewMatchers: این‌ها مجموعه‌ای از اشیا هستند که از آن‌ها برای پیدا کردن view ی مورد نظر در ساختار سلسله مراتبی صفحه استفاده می‌شود. آن‌ها را به متد onView می‌فرستیم تا view مورد نظر را پیدا کنند و به ما برگردانند.
  • ViewActions: از این‌ها برای انجام کارهایی مثل کلیک بر روی view ها استفاده می‌شود. آن‌ها را به متد ()ViewInteraction.perform می‌فرستیم.
  • ViewAssertions: از این‌ها برای اثبات و بررسی وضعیت ویو استفاده می‌کنیم. آن‌ها را به متد ()ViewInteraction.check می‌فرستیم.

برای مثال:

onView(withId(R.id.my_view))       // withId(R.id.my_view) - ViewMatcher
    .perform(click())                       // click() - ViewAction
    .check(matches(isDisplayed())); //matches(isDisplayed()) - ViewAssertion

تنظیم پروژه برای استفاده از اسپرسو

برای استفاده از اسپرسو در پروژه از اندروید SDK مخزن «Android Support Repository» را نصب کنید:

توصیه می‌شود که به تنظیمات شبیه‌ساز اندروید خود بروید و انیمیشن‌های دستگاه را غیر فعال کنید تا سرعت تست‌ها بالاتر برود:

حالا به سراغ تنظیمات بیلد گریدل مربوط به ماژول برنامه بروید. اگر پروژه را با ویزارد اندروید استودیو ساخته باشید در این قسمت نیازی نیست هیچ کاری بکنید و اندروید استودیو به صورت خودکار هر آنچه که نیاز دارید را به این فایل اضافه کرده است. چک کنید ببینید در بخش defaultConfig خط زیر وجود داشته باشد:

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

و در قسمت وابستگی‌ها یا dependencies خط‌های زیر وجود داشته باشند:

androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
    exclude group: 'com.android.support', module: 'support-annotations'
})

برای این که ببینید آیا تنظیمات بیلد گریدل را به خوبی انجام داده‌اید، به این نمونه دقت کنید:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "ir.smartlab.spressodemo"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
}

پروژه را سینک کنید! بعد از اتمام دانلود موارد جدید و سینک شدن پروژه، همه چیز آماده است تا یک فایل چیدمان یا layout درست کنیم و اولین تست ابزاری خودمان را بر روی آن اجرا کنیم.

اسپرسو چطور کار می‌کند؟

برای درک بهتر فرایند تست با اسپرسو، نگاهی از بالا به آن می‌اندازیم. ابتدا باید یکی از عناصر ویو را انتخاب کنیم. مثلا دکمه‌ای که کاربر باید روی آن کلیک کند. سپس عملیاتی که کاربر باید روی آن ویو انجام دهد را شبیه‌سازی می‌کنیم. مثلا کاربر روی دکمه کلیک می‌کند یا متنی را در یک EditText وارد می‌کند. در انتها بررسی می‌کنیم تا ببینیم آیا رفتاری که انتظار داریم از برنامه رخ داده است یا نه؟ مثلا به صفحه بعد رفته‌ایم؟ کل فرایند تست ابزاری با اسپرسو همین است: پیدا کردن، انجام عملی که از کاربر انتظار داریم و بررسی رفتار برنامه در واکنش به آن عمل.

برای شروع کار با اسپرسو و نوشتن اولین تست ابزاری با اسپرسو، یک پروژه می‌سازیم. در صفحه اول این پروژه دو دکمه قرار دارد: دکمه Login که ما را به صفحه ورود کاربر می‌برد و دکمه Sign Up که ما را به صفحه ثبت نام برنامه هدایت می‌کند. در تست اول دکمه Login را تست می‌کنیم. می‌خواهیم ببینیم که آیا کلیک بر روی این دکمه ما را به صفحه ورود برنامه می‌برد یا نه. در تست دوم که در صفحه Login اتفاق می‌افتد، می‌خواهیم ببینیم پر کردن فرم لاگین و ارسال فرم لاگین به سرور آیا ما را به صفحه ورود موفق می‌برد یا نه. بنابراین به اکتیویتی‌های Login و LoginSuccess هم احتیاج خواهیم داشت.

حالا بیایید پروژه را بسازیم.

ساخت پروژه

برای شروع یک اکتیویتی به نام MainActivity بسازید و لی‌اوت آن را به شکل زیر طراحی کنید:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context="ir.smartlab.spressodemo.MainActivity">

    <Button android:id="@+id/login_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Login" android:layout_marginLeft="8dp" />

    <Button android:id="@+id/signup_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Sign Up"/>
</LinearLayout>

این صفحه شبیه عکس زیر خواهد شد:

سپس اکتیویتی Login را به شکل زیر بسازید:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent">

    <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <LinearLayout android:id="@+id/layout_login" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginTop="16dp" android:orientation="vertical">

        <EditText android:id="@+id/edit_text_email" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Email" android:inputType="textEmailAddress" />

        <EditText android:id="@+id/edit_text_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Password" android:inputType="textPassword" />

        <Button android:id="@+id/button_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="Login" />
    </LinearLayout>

    <ProgressBar android:id="@+id/progress_bar_login" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_marginTop="8dp" android:indeterminate="true" android:visibility="gone" />
</RelativeLayout>

و در فایل کلاس این اکتیویتی تغییرات زیر را اعمال کنید تا کلیک بر روی دکمه Login صفحه LoginActivity را باز کند:

public class MainActivity extends AppCompatActivity {

    private Button loginButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        loginButton = (Button) findViewById(R.id.login_button);
        loginButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, LoginActivity.class));
            }
        });
    }
}

یک بار پروژه را اجرا کنید و بر روی دکمه Login کلیک کنید تا از صحت عملکرد آن مطمئن شوید.

نوشتن اولین تست اسپرسو

حالا در نمای Project اندروید استودیو شاخه androidTest را باز کنید:

فایل ExampleInstrumentedTest برای نمونه است. می‌توانید آن را پاک کنید. حالا یک پکیج یا بسته جدید در این مسیر ایجاد می‌کنیم به نام mainactivity و در آن یک کلاس ایجاد می‌کنیم به نام MainActivityTest:

ابتدا به این کلاس دو حاشیه نوشت یا annotation اضافه می‌کنیم:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
    // ...
}

حاشیه‌نوشت @RunWith کلاسی که قرار است این تست را اجرا کند معرفی می‌کند. در حال حاضر مقدار AndroidJUnit4.class را به این حاشیه‌نوشت می‌دهیم. حاشیه‌نوشت دیگری که به این کلاس اضافه کردیم @LargeTest است. تست‌هایی را که احتمال می‌دهیم زمان اجرای آن‌ها طولانی (بیشتر از یک ثانیه) است با این حاشیه‌نوشت مشخص می‌کنیم. این تست‌ها معمولا تست‌هایی هستند که برای بررسی یکپارچگی کل برنامه از آن‌ها استفاده می‌شود و ممکن است از تمام منابع سیستم مانند دیتابیس یا پایگاه داده و فایل و شبکه و … استفاده کنند. اگر تست بخش کمتر و کوچکتری را تست می‌کند می‌توانید به جای @LargeTest از @MediumTest یا حتا @SmallTest استفاده کنید.

در گام بعدی یک قانون یا Rule به کلاس تست اضافه می‌کنیم:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
    @Rule
    public ActivityTestRule&amp;lt;MainActivity&amp;gt; mMainActivityTestRule = new
            ActivityTestRule&amp;lt;MainActivity&amp;gt;(MainActivity.class);
    // ...
}

این حاشیه‌نوشت اعلام می‌کند که تست‌ها بر روی اکتیویتی MainActivity اجرا می‌شوند و سیستم این اکتیویتی را قبل از اجرای تست و قبل از اجرای هر متد @Before اجرا می‌کند و بعد از اتمام تست و اجرای همه متدهای @After می‌بندد.

حالا زمان آن است که اولین تست را بنویسیم:

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
    @Rule
    public ActivityTestRule&amp;lt;MainActivity&amp;gt; mMainActivityTestRule = new
            ActivityTestRule&amp;lt;MainActivity&amp;gt;(MainActivity.class);

    @Test
    public void clickLoginButton_openLoginScreen() {
        //locate and click on the login button
        onView(withId(R.id.button_login)).perform(click());

        //check that the login screen is displayed
        onView(withId(R.id.edit_text_email)).check(matches(allOf(isDescendantOfA(withId(R.id.layout_login)), isDisplayed())));
    }
}

کل تست از دو خط تشکیل شده است. در خط اول به دنبال یک ویو با شناسه button_login می‌گردیم و روی آن کلیک می‌کنیم:

onView(withId(R.id.button_login)).perform(click());        

در خط دوم چک می‌کنیم که آیا در میان فرزندان (isDescendantOfA) ویویی که حالا نمایش داده می‌شود (isDisplayed) و شناسه آن (layout_login) است، آیا ویویی با شناسه (edit_text_email) وجود دارد یا نه؟

onView(withId(R.id.edit_text_email)).check(matches(allOf(isDescendantOfA(withId(R.id.layout_login)), isDisplayed())));

می‌دانم که این سبک نگارش ممکن است کمی پیچیده باشد ولی منطق پشت آن بسیار ساده است. با کمی تمرین و مطالعه مثال‌های بیشتر می‌توانید به راحتی با همه این متدها آشنا شوید.

به همین سادگی اولین تست ابزاری خودمان را نوشتیم! حالا می‌خواهیم این تست را اجرا کنیم.

اجرای تست

همانطور که در قسمت‌های قبلی درباره تست برنامه‌های اندروید نوشتیم برای انجام یک تست خاص کافی است کلاس تست را باز کنیم و دکمه سبز رنگ کنار متد تست را کلیک کنیم:

اگر بخواهیم همه تست‌های کلاس را اجرا کنیم، دکمه سبز رنگ اجرا که در کنار نام کلاس قرار دارد را کلیک کنیم:

و اگر بخواهیم کل تست‌های ابزاری نوشته شده برای پروژه را اجرا کنیم کافی است در نمای Project بر روی شاخه androidTest راست کلیک کنیم و گزینه Run Tests را انتخاب کنیم یا از کلیدهای ترکیبی Ctrl + Shift + F10 استفاده کنیم:

حالا اگر تستی که نوشتیم را اجرا کنیم و همه چیز درست باشد، در پنجره یا نمای run که به صورت خودکار باز می‌شود نتیجه اجرای تست نمایش داده می‌شود. توجه کنید که تست‌های ابزاری در محیط واقعی اجرا می‌شوند بنابراین باید یک شبیه‌ساز یا دستگاه اندرویدی متصل به سیستم داشته باشید و بعد تست را اجرا کنید:

حالا اگر در آینده خود شما یا یک برنامه‌نویس دیگر در تیم شما، تغییری در برنامه بدهد، مثلا شناسه یا ID یک ویو را عوض کند یا کاری کند که کلیک بر روی این دکمه به جای نمایش صفحه Login کاربر را به صفحه دیگری ببرد، اجرای این تست با خطا مواجه شده و به سرعت می‌توان اشکال به وجود آمده در برنامه را تشخیص داد. برای این که ببینیم اگر تست اصطلاحا fail شود چه اتفاقی می‌افتد، فرض کنید بعد از مدتها تصمیم می‌گیرید با کلیک روی دکمه Login یک پنجره دیالوگ به کاربر نشان بدهید و از او تأیید بگیرید:

loginButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        new AlertDialog.Builder(MainActivity.this)
                .setTitle("Login")
                .setMessage("Go to login page?")
                .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        startActivity(new Intent(MainActivity.this, LoginActivity.class));
                    }
                })
                .setNegativeButton("Cancel", null)
                .show();
    }
});

به همین سادگی تست fail می‌شود! اگر تست را دوباره اجرا کنیم چه اتفاقی رخ می‌دهد؟ ببینید:

در مطلب بعدی با تست‌های بیشتری آشنا می‌شویم!

 

آموزش اندروید-فصل ۳۰: استفاده از پروگارد در برنامه‌های اندروید

پیش از این در مطلب «آنچه بعد از دو سال برنامه‌نویسی اندروید آموختم-قسمت اول» به ضرورت استفاده از پروگارد در برنامه‌های اندروید اشاره مختصری کردیم اما آموزش استفاده از پروگارد تا این زمان به تأخیر افتاد. در این مطلب می‌خواهم مزایای استفاده از پروگارد را بگویم و شیوه استفاده از آن در اندروید با استفاده از اندروید استودیو را به شما آموزش دهم. امیدوارم که این مطلب برای شما مفید باشد.

ادامه مطلب

آموزش اندروید-فصل ۲۷-۸: Retrofit چیست و چطور کار می‌کند

مقدمه

در قسمت‌های قبلی این فصل، درباره موضوعات متوعی بحث کردیم. در این مطلب آخرین بخش مطالب مربوط به اتصال برنامه اندروید به سرور را با هم مرور می‌کنیم و در بخش (یا بخش‌های آینده) یک برنامه واقعی می‌نویسیم و از همه چیزهایی که تا به حال یاد گرفته‌اید استفاده خواهیم کرد.

در این بخش می‌خواهم شما را با Retrofit آشنا کنم. طبق تعریفی که خود پدیدآورندگان Retrofit کرده‌اند:

یک کلاینت REST نوع-امن جاوا و اندروید

است. از حاشیه‌نوشت‌های جاوا برای توصیف درخواست‌های HTTP و جانگهدارهای پارامترهای URL و … استفاده می‌شود. همچنین می‌توان از آن برای آپلود فایل و بدنه درخواست چندپاره (Multipart request body) استفاده کرد.

نکته: رتروفیت (Retrofit) نسخه ۲ کاملاً متفاوت با نسخه‌های ماقبل است. در این نوشته ما فقط از رتروفیت نسخه ۲ استفاده می‌کنیم و به نسخه‌های ماقبل آن کاری نداریم. اگر احیاناً آموزشی را از اینترنت می‌گیرید، حتماً حواستان به نسخه Retrofit هم باشد.

ادامه مطلب

آموزش اندروید-فصل ۲۷-۷: REST چیست

مقدمه

تا این جا مطالب زیادی درباره اتصال برنامه‌های اندروید به یک سرور و ارسال و دریافت اطلاعات نوشتیم:

در ادامه مباحث مرتبط با اتصال برنامه‌های اندروید به سرور می‌خواهم شما را با یک معماری و یک مفهوم بسیار پرکاربرد آشنا کنم: REST.

ادامه مطلب

آموزش اندروید-فصل ۲۷-۶: Glide چیست و چطور کار می‌کند

مقدمه

در ادامه مباحث مرتبط با اتصال برنامه‌های اندروید به اینترنت و وب، این بار به سراغ عکس‌ها و تصاویر می‌رویم.

دانلود کردن عکس‌ها از اینترنت و نمایش آن‌ها در برنامه یکی از کارهای بسیار رایج در برنامه‌های اندروید است. این کار همانقدر که رایج و فراگیر است، سخت و پیچیده هم هست! برقراری اتصال اینترنتی، دانلود عکس، قرار دادن آن در ImageView، کش کردن داده‌های عکس برای صرفه‌جویی در مصرف منابع، مدیریت خطاهای احتمالی و …

همانطور که می‌دانید نظر من و بسیاری از برنامه‌نویسان اندروید این است که به جای درگیر شدن در پیاده‌سازی جزئیات این فرایندهای پیچیده، بهتر است از کتابخانه‌های موجود استفاده کنیم. برای دانلود و دستکاری عکس‌ها در اندروید کتابخانه‌های زیادی وجود دارد که معروف‌ترین آن‌ها Picasso و Glide هستند. در این مطلب نحوه کار کردن با Glide را به شما آموزش خواهم داد.

ادامه مطلب

آموزش اندروید-فصل ۲۷-۳: نکات پیشرفته OkHttp

در دو نوشته قبلی (اینجا و اینجا) به طور خیلی ساده نحوه کار کردن با OkHttp برای دریافت و ارسال اطلاعات از/به اینترنت گفتیم. در این مطلب کمی بیشتر با امکانات OkHttp کار می‌کنیم. امکاناتی که تقریباً همه جا به آن‌ها نیاز خواهیم داشت.

ادامه مطلب

آموزش اندروید-فصل ۲۷: اتصال به اینترنت در اندروید-۲

در مطلب قبلی نحوه گرفتن اطلاعات از اینترنت را به صورت خیلی ساده گفتیم. در این مطلب می‌خواهیم شیوه ارسال اطلاعات به یک سرور را به کمک کتابخانه بسیار مفید OkHttp آموزش بدهیم.

ادامه مطلب

آنچه بعد از دو سال برنامه‌نویسی اندروید آموختم-قسمت سوم

در قسمت اول و دوم این مطلب ۱۰ تا از توصیه‌های Aritra Roy را با هم مرور کردیم. در این مطلب بخش دیگری از توصیه‌های وی را با هم می‌خوانیم.

۱۱- با تنظیم بیلد گریدل پنج ساعت در هفته در وقت صرفه‌جویی کن

بسیار محتمل است که از اندروید استودیو و گریدل برای توسعه برنامه‌های اندروید استفاده کنی. گریدل عالی ولی کند است و وقتی پروژه کمی بزرگ‌تر شود کندتر هم می‌شود.

ساعت‌های بی شماری را به باد می‌آورم که بیکار می‌نشسم تا بیلد گریدل تمام شود. در زمان‌های شلوغی کار به راحتی یک ساعت از وقت من صرف بیلدهای گریدل می‌شود و این یعنی ۵ ساعت در هفته و ۲۰ ساعت در ماه.

اما راه‌هایی هست تا گریدل را سریع‌تر کنی.

برای این کار اینجا و اینجا را ببین. با استفاده از این رهنمودها و بهینه‌سازی مناسب، زمان بیلد من از چهار دقیقه به حدود ۳۰ ثانیه رسید.

ادامه مطلب

آنچه بعد از دو سال برنامه‌نویسی اندروید آموختم-قسمت دوم

در قسمت قبل پنج توصیه Aritra Roy را خواندید. در این مطلب ادامه مطلب را می‌خوانیم:

۶- از معماری مناسب استفاده کن

همیشه از خودت به خاطر انتخاب یک معماری مناسب در اول کار، سپاسگزار خواهی بود. می‌توانی از MVP یا همان Model-View-Presenter استفاده کنی که برنامه را به لایه‌های متفاوتی تجزیه می‌کند تا مدیریت آن ساده‌تر باشد. این باعث می‌شود تا انعطاف کد بالاتر برود و زمان نگهداری آن کاهش یابد.

این یک پروژه دموی بسیار عالی برای شروع است. اگر با مفهموم آشنا نیستید می‌توانید این راهنمای دقیق و با جزئیات را بخوانید.

جایزه: حتما این و این را ببینید. اینها کمک بزرگی به پیاده‌سازی MVP در پروژه به شما می‌کنند.

ادامه مطلب

هفته‌نامه اسمارت لب ۶#

دو هفته قبل به علت مشغله زیاد و یک مسافرت عالی فرصت نوشتن خبرنامه (و البته هیچ مطلب جدیدی در سایت) را نداشتم و البته ظاهراً برای کسی مهم نبود و کسی نپرسید که خبرنامه چه شد! بابت این همه انگیزه بالایی که به من می‌دهید ازتان سپاسگزارم!

این هفته به جای شماره ۴ خبرنامه شماره ۶ آن را منتشر می‌کنم تا معلوم نشود که دو هفته خبرنامه نداشتیم!

ادامه مطلب