[Android] Unit Test dan UI Test dengan JUnit, Mockito, Espresso

android testing

“Testing ?”, “Yes.. because every pro always testing !”.. *hhaha just kidding. So ceritanya di tempat kerja sekarang, setiap developer harus bikin Unit dan UI test untuk aplikasi yang lagi di’develop. Alhasil penulis yang sebelumnya ngga pernah bikin test jadi punya banyak PR deh hehe. Nah postingan kali ini akan coba bahas Unit Test dan UI / Instrument Test dengan JUnit, Mockito, dan Espresso Framework.

Android Studio menyediakan tools yang diperlukan untuk kegiatan testing, mengenai alasan kenapa testing itu diperlukan tidak akan dibahas di postingan ini, karena jika teman-teman yang di depan komputer lagi baca tulisan ini, itu artinya kita sama-sama setuju kalau testing itu penting 🙂

Tools untuk testing yang disediakan Android Studio ini dinamakan Testing Support Library. Di dalamnya terdapat :

  • AndroidJUnitRunner -> JUnit 4-compatible test runner for Android.
  • Espresso -> UI testing framework, suitable for functional UI testing within an app.
  • UI Automator -> UI testing framework, suitable for cross-app functional UI testing accross system and installed apps.

Nah disini kita akan mencoba membuat aplikasi demo untuk Unit Test dan UI Test. So let’s fire our Android Studio !

SSH Gitlab : git@gitlab.com:bnctvns/unit_ui_test_demo.git
Download Zip : Gitlab

1. Create New Project.

create project

2. Minimum SDK.

min sdk

3. Pilih Tipe Activity.

empty activity

4. Klik Next untuk dialog nama activity.

5. Setelah workspace Android Studio siap, ganti nama file “MainActivity.java” dengan “ButtonActivity.java”. Caranya adalah klik kanan di class “MainActivity.java”, kemudian pilih menu “Refactor” -> “Rename”. Setelah itu ganti juga nama file “activity_main.xml” dengan “activity_button.xml”, dengan cara yang sama.

6. Buat class baru dengan nama “EditActivity.java“, dan file layout dengan nama “activity_edit.xml”.

structure

7. Sebelumnya kita harus menginstall terlebih dahulu tools yang dibutuhkan untuk proses testing, langkah-langkahnya adalah sebagai berikut :

  • Buka Android SDK Manager.
  • Buka tab “SDK Tools”.
  • Centang “Android Support Repository”.
  • “Apply” kemudian proses download dan install akan dimulai.

8. Setelah proses instalasi Android Support Repository selesai, maka berikutnya adalah modifikasi file build.gradle level module seperti berikut :


android {
    defaultConfig {
        ....
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile 'com.android.support:appcompat-v7:24.2.0'
    compile 'com.android.support:recyclerview-v7:24.2.0'
    compile 'com.android.support:design:24.2.0'

    // unit testing dependencies
    testCompile 'junit:junit:4.12'
    testCompile 'org.mockito:mockito-core:1.10.19'

    // android testing dependencies
    androidTestCompile 'com.android.support:support-annotations:24.2.0'
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

Kita harus mengatur konfigurasi dependencies dengan menggunakan API yang disediakan oleh JUnit 4 framework. Untuk menggunakan mock objek, kita akan menggunakan framework Mockito.

*Fungsi dari mock object adalah untuk mereplika objek yang digunakan oleh objek yang sedang kita tes. Dengan kata lain kita harus melakukan tes dengan mengisolasi objek yang sedang kita tes, dari dependencies yang objek tersebut gunakan. Alasannya adalah agar tes yang dilakukan hanya menguji hal-hal yang berada di dalam jangkauan objek yang sedang dites saja, dan tidak terpengaruh dari objek yang berada di luar itu. Nah oleh karena itu kita menggunakan mock framework.

9. Sync Gradle

Unit Testing

10. Pertama kita akan mencoba unit testing. Secara sederhana, unit testing adalah tes yang dijalankan di local JVM (di komputer kita). Google menyarankan untuk menggunakan unit testing jika method atau class yang ingin kita tes tidak membutuhkan atau hanya membutuhkan sedikit dependencies dari Android Framework (seperti class Context).

Kelebihan test ini adalah waktu yang digunakan untuk kegiatan tes akan lebih efisien, karena kita tidak perlu men’deploy aplikasi ke device atau emulator. Biasanya akan digunakan framework yang berfungsi sebagai mock object dalam kegiatan tes. Disini kita akan menggunakan framework Mockito.

Class test yang akan kita buat harus berada di direktori “module_name/src/test/java/”. Direktori ini dibuat otomatis ketika kita membuat project di Android Studio. *Jika karena beberapa sebab direktori ini tidak ada, kita bisa membuat direktori ini secara manual.

11. Buat interface ServiceCalculator.java dan class MyCalculator.java

Disini kita akan buat class MyCalculator yang akan digunakan oleh client (Activity) yang mempunyai fungsi-fungsi perhitungan sederhana. Dalam melakukan perhitungan, class MyCalculator ini akan menggunakan interface ServiceCalculator. Interface ini akan kita ganti dengan mock object dari framework Mockito.

*Penggunaan desain ini disebut dependency injection, yang dimaksudkan untuk mempermudah kegiatan testing.


package com.bonioctavianus.instrumenttestdemo;

/**
 * Created by bonioctavianus on 9/12/16.
 */
public interface ServiceCalculator {

    int add(int num1, int num2);

    int substract(int num1, int num2);

    int multiply(int num1, int num3);

    int divide(int num1, int num2);
}

 


package com.bonioctavianus.instrumenttestdemo;

/**
 * Created by bonioctavianus on 9/12/16.
 */
public class MyCalculator {

    private ServiceCalculator serviceCalculator;

    private int num1;
    private int num2;

    public ServiceCalculator getServiceCalculator() {
        return serviceCalculator;
    }

    public void setServiceCalculator(ServiceCalculator serviceCalculator) {
        this.serviceCalculator = serviceCalculator;
    }

    public int getNum1() {
        return num1;
    }

    public void setNum1(int num1) {
        this.num1 = num1;
    }

    public int getNum2() {
        return num2;
    }

    public void setNum2(int num2) {
        this.num2 = num2;
    }

    public int add() {
        return serviceCalculator.add(num1, num2);
    }

    public int substract() {
        return serviceCalculator.substract(num1, num2);
    }

    public int multiply() {
        return serviceCalculator.multiply(num1, num2);
    }

    public int divide() {
        return serviceCalculator.divide(num1, num2);
    }
}

12. Berikutnya adalah membuat class test untuk MyCalculator dengan nama “MyCalculatorTest.java“. Class ini harus berada di direktori “app/src/test/java/…/”.


package com.bonioctavianus.instrumenttestdemo;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

/**
 * Created by bonioctavianus on 9/12/16.
 */

@RunWith(MockitoJUnitRunner.class)
public class MyCalculatorTest {

    @Mock
    private ServiceCalculator serviceCalculator;

    // object to test
    private MyCalculator myCalculator;

    private int num1 = 10;
    private int num2 = 5;

    @Before
    public void setUp() {
        myCalculator = new MyCalculator();
        myCalculator.setServiceCalculator(serviceCalculator);
        myCalculator.setNum1(num1);
        myCalculator.setNum2(num2);
    }

    @Test
    public void addTest_shouldCorrect() {
        // mock the behaviour of service calculator
        when(serviceCalculator.add(num1, num2)).thenReturn(15);
        assertEquals("Result should be 15", 15, myCalculator.add());
    }

    @Test
    public void substractTest_shouldCorrect() {
        // mock the behaviour of service calculator
        when(serviceCalculator.substract(num1, num2)).thenReturn(5);
        assertEquals("Result should be 5", 5, myCalculator.substract());
    }

    @Test
    public void multiplyTest_shouldCorrect() {
        // mock the behaviour of service calculator
        when(serviceCalculator.multiply(num1, num2)).thenReturn(50);
        assertEquals("Result should be 50", 50, myCalculator.multiply());
    }

    @Test
    public void divideTest_shouldCorrect() {
        // mock the behaviour of service calculator
        when(serviceCalculator.divide(num1, num2)).thenReturn(2);
        assertEquals("Result should be 2", 2, myCalculator.divide());
    }
}

Disini kita menggunakan beberapa annotation, diantaranya :

  •  “@RunWith(MockitoJUnitRunner.class)” di awal class untuk proses initialize Mockito.
  • “@Mock” untuk membuat objek mock yang akan menggantikan objek yang asli. Pada contoh ini objek ServiceCalculator.
  • “@Before” untuk proses initialize sebelum melakukan tes. Method yang diberi annotation “@Before” ini akan dijalankan sebelum menjalankan semua method dengan annotation “@Test”.
  • “@Test” untuk method yang akan dites.

Selain annotation, kita juga menggunakan beberapa fungsi dari Mockito dan JUnit, seperti :

  • “when()” untuk menandakan event dimana kita ingin memanipulasi behaviour dari mock objek.
  • “thenReturn()” untuk memanipulasi output dari mock objek.
  • “assertEquals()” untuk validasi output yang diharapkan, dan output yang sebenarnya.

Ok, next jalankan tes dengan cara klik kanan di class “MyCalculatorTest.class” pada project structure panel, kemudian pilih “Run MyCalculatorTest“. Setelah proses build selesai, maka hasil tes akan muncul pada console.

test success

Yup kira-kira demikian contoh sederhana unit testing dengan JUnit dan Mockito. Sebenarnya masih ada banyak fungsi lain yang bisa digunakan untuk kegiatan unit testing, yang bisa dilihat di Docs Mockito.

Nah berikutnya adalah untuk UI / Instrument Test. Tes ini akan dijalankan di device atau emulator. Penggunaan tes ini adalah menguji interaksi user ketika menggunakan aplikasi kita, selain itu kita juga bisa menggunakan tes ini jika tes yang akan kita buat memerlukan dependencies dari framework Android yang tidak bisa diganti oleh mock objek.

UI Testing

13. UI testing ini dijalankan di device atau emulator. Pertama non-aktifkan fitur animasi pada device. Langkah ini adalah optional, karena jika fitur animasi aktif, maka tes terkadang gagal dan memberikan hasil yang tidak diharapkan (biasanya saat transisi Activity akan muncul exception PerformException).

Opsi untuk menonaktifkan animasi ada pada “Settings” di halaman “Developer Options“. Fitur yang perlu dinonaktifkan adalah sebagai berikut :

  • Window animation scale
  • Transition animation scale
  • Animator duration scale

14. Modifikasi file “strings.xml” :


<resources>
    <string name="app_name">Instrument Test Demo</string>

    <string name="btn_show_toast">Show Toast</string>
    <string name="btn_show_snackbar">Show Snackbar</string>
    <string name="btn_show_dialog">Show Dialog</string>
    <string name="btn_show_edit_activity">Open Edit Activity</string>

    <string name="hint_edit_text_name">What\'s your name ?</string>
    <string name="btn_enter">Enter</string>

</resources>

15. Modifikasi layout “activity_button.xml“.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/root_container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:padding="16dp">

    <Button android:id="@+id/btn_toast" android:layout_width="300dp" android:layout_height="wrap_content" android:text="@string/btn_show_toast"/>

    <Button android:id="@+id/btn_snackbar" android:layout_width="300dp" android:layout_height="wrap_content" android:text="@string/btn_show_snackbar"/>

    <Button android:id="@+id/btn_dialog" android:layout_width="300dp" android:layout_height="wrap_content" android:text="@string/btn_show_dialog"/>

</LinearLayout>

16. Modifikasi layout “activity_edit.xml“.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/root_container" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:padding="16dp">

    <EditText android:id="@+id/et_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hint_edit_text_name" android:imeOptions="actionDone" android:inputType="textCapWords" android:maxLines="1" android:padding="10dp" android:textSize="18sp"/>

    <Button android:id="@+id/btn_enter" android:layout_width="300dp" android:layout_height="wrap_content" android:layout_marginTop="32dp" android:text="@string/btn_enter"/>

</LinearLayout>

17. Modifikasi class “ButtonActivity.java“.


package com.bonioctavianus.instrumenttestdemo;

import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class ButtonActivity extends AppCompatActivity {

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

        initViews();
    }

    private void initViews() {
        findViewById(R.id.btn_toast).setOnClickListener(new ClickHandler());
        findViewById(R.id.btn_snackbar).setOnClickListener(new ClickHandler());
        findViewById(R.id.btn_dialog).setOnClickListener(new ClickHandler());
    }

    private void showToast() {
        Toast.makeText(this, R.string.app_name, Toast.LENGTH_SHORT).show();
    }

    private void showSnackBar() {
        Snackbar.make(findViewById(R.id.root_container), R.string.app_name, Snackbar.LENGTH_SHORT).show();
    }

    private void showDialog() {
        new AlertDialog.Builder(this)
                .setMessage(R.string.app_name)
                .setCancelable(false)
                .setPositiveButton(android.R.string.ok, null).create().show();
    }

    private class ClickHandler implements View.OnClickListener {

        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn_toast:
                    showToast();
                    break;
                case R.id.btn_snackbar:
                    showSnackBar();
                    break;
                case R.id.btn_dialog:
                    showDialog();
                    break;
            }
        }
    }
}

Class ini akan menampilkan tiga buah button, yang masing-masing button akan menampilkan informasi melaui Toast, Snackbar, dan Alert dialog.

18. Modifikasi class “EditActivity.java


package com.bonioctavianus.instrumenttestdemo;

import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;

/**
 * Created by bonioctavianus on 9/12/16.
 */
public class EditActivity extends AppCompatActivity {

    private EditText etName;

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

        initViews();
    }

    private void initViews() {
        etName = (EditText) findViewById(R.id.et_name);
        findViewById(R.id.btn_enter).setOnClickListener(new ClickHandler());
    }

    private void showWelcomeDialog(String message) {
        new AlertDialog.Builder(this)
                .setMessage(message)
                .setCancelable(false)
                .setPositiveButton(android.R.string.ok, null).create().show();
    }

    private class ClickHandler implements View.OnClickListener {

        @Override
        public void onClick(View view) {
            if (view.getId() == R.id.btn_enter) {
                String message = "Welcome " + etName.getText().toString();
                showWelcomeDialog(message);
            }
        }
    }
}

Class ini akan menampilkan edittext untuk mengisi nama, dan ketika button enter diclick, maka akan muncul alert dialog yang menampilkan pesan selamat datang.

19. Buat class test “ButtonActivityTest.java” di direktori “module_name/src/androidTest/java/”.


package com.bonioctavianus.instrumenttestdemo;

import android.os.SystemClock;
import android.support.test.espresso.action.ViewActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

/**
 * Created by bonioctavianus on 9/12/16.
 */

@RunWith(AndroidJUnit4.class)
public class ButtonActivityTest {

    @Rule
    public ActivityTestRule<ButtonActivity> mActivityRule = new ActivityTestRule<>(ButtonActivity.class);

    /**
     * Confirm that all views in view hierarchy is valid and displayed
     */
    @Test
    public void checkAllViewsIsValid_sameActivity() {
        onView(withId(R.id.btn_toast)).check(matches(isDisplayed()));
        onView(withId(R.id.btn_snackbar)).check(matches(isDisplayed()));
        onView(withId(R.id.btn_dialog)).check(matches(isDisplayed()));
    }

    /**
     * Test button toast click action
     */
    @Test
    public void showToast_sameActivity() {
        // perform click and show Toast
        onView(withId(R.id.btn_toast)).perform(ViewActions.click());

        // for delay purposes
        SystemClock.sleep(1000);
    }

    /**
     * Test button snackbar click action
     */
    @Test
    public void showSnackBar_sameActivity() {
        // perform click and show snackbar
        onView(withText("Show Snackbar")).perform(ViewActions.click());

        // for delay purposes
        SystemClock.sleep(1000);
    }

    /**
     * Test button dialog click action
     */
    @Test
    public void showDialog_sameActivity() {
        // perform click and show dialog
        onView(withId(R.id.btn_dialog)).perform(ViewActions.click());
    }
}

Test class ini akan menjadi class dimana kita akan melakukan tes pada activity ButtonActivity.

20. Buat class test “EditActivityTest.java” di direktori “module_name/src/androidTest/java/”.


package com.bonioctavianus.instrumenttestdemo;

import android.support.test.espresso.action.ViewActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

/**
 * Created by bonioctavianus on 9/12/16.
 */

@RunWith(AndroidJUnit4.class)
public class EditActivityTest {

    private String inputText = "bonioctavianus";

    @Rule
    public ActivityTestRule<EditActivity> mActivityRule = new ActivityTestRule<>(EditActivity.class);

    /**
     * Confirm that all views in view hierarchy is valid and displayed
     */
    @Test
    public void checkAllViewsIsValid_sameActivity() {
        onView(withId(R.id.et_name)).check(matches(isDisplayed()));
        onView(withId(R.id.btn_enter)).check(matches(isDisplayed()));
    }

    @Test
    public void typeText_sameActivity() {
        // Type text then press the button
        onView(withId(R.id.et_name)).perform(ViewActions.typeText(inputText), ViewActions.closeSoftKeyboard());

        // Validate the text was changed
        onView(withId(R.id.et_name)).check(matches(withText(inputText)));

        // Perform click action on button
        onView(withId(R.id.btn_enter)).perform(ViewActions.click());
    }
}

Test class ini akan menjadi class dimana kita akan melakukan tes pada activity EditActivity.

21.Ok, next jalankan tes dengan cara klik kanan di test class yang diinginkan pada project structure panel, kemudian pilih “Run…” Setelah proses build selesai, maka aplikasi akan diinstall di device atau emulator, dan tes akan dilakukan. Hasil tes akan ditampilkan di console.

test_ui_success

Menggunakan View Matcher
Untuk mendapatkan referensi / menemukan view yang ingin ditest, kita menggunakan method “onView()” dari class “ViewMatchers”. Method onView() ini bisa menerima argumen String atau int. Pencarian yang dilakukan method “onView()” hanya dilakukan pada view hierarchy yang sedang aktif (layout yang sedang aktif). Jika view tidak ditemukan, maka kita akan mendapatkan exception “NoMatchingViewException”.

Contoh onView() dengan argumen String, digunakan untuk mencari view yang mempunyai teks sesuai dengan argumen yang diberikan (case sensitive) :

  • onView(withText(“Show Toast”));

*note : bila terdapat lebih dari satu view yang mempunyai teks yang sama, maka view yang akan digunakan adalah view pertama yang ditemukan pada hirarki viewgroup.

Contoh onView() dengan argumen int, digunakan untuk mencari view yang mempunyai ID sesuai dengan argumen yang diberikan :

  • onView(withId(R.id.btn_toast));

Menggunakan View Interaction

Untuk melakukan action pada view, kita bisa menggunakan method “perform()” dari class ViewInteraction. Argumen yang diterima adalah objek ViewAction. Argumen yang diberikan bisa lebih dari satu *dipisahkan dengan koma, dan Espresso akan mengeksekusinya sesuai dengan urutan argumen.

Beberapa contoh action yang disediakan :

  • ViewActions.click() : untuk melakukan action click pada view.
  • ViewActions.typeText() : untuk menuliskan text pada view.

Contoh penggunaan :

  • onView(withId(R.id.btn_enter)).perform(ViewActions.click());
  • onView(withId(R.id.et_name)).perform(ViewActions.typeText(inputText), ViewActions.closeSoftKeyboard());

Verifikasi output

Untuk melakukan verifikasi output pada view, kita bisa menggunakan method “check()” dari class ViewInteraction. Argumen yang diterima adalah objek ViewAssertion. Jika verifikasi gagal, maka Espresso akan memberikan exception “AssertionFailedError”.

Beberapa contoh method verifikasi yang disediakan :

  • doesNotExist() : untuk validasi tidak ada view yang cocok dengan kriteria tes yang digunakan.
  • matches() : untuk validasi ada view yang cocok dengan kriteria tes yang digunakan.

Selain itu kita juga bisa membuat “Suite class” untuk menjalankan beberapa class test secara berurutan. Contoh :

Buat class “AppTestSuite.java” di direktori “module_name/src/androidTest/java/” :


package com.bonioctavianus.instrumenttestdemo;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

/**
 * Created by bonioctavianus on 9/12/16.
 */

@RunWith(Suite.class)
@Suite.SuiteClasses({ButtonActivityTest.class, EditActivityTest.class})
public class AppTestSuite {

}

Setelah itu klik kanan class AppTestSuite.java, kemudian pilih “Run ‘AppTestSuite’“.

Dan kira-kira begitu sedikit cerita mengenai Unit Testing dan UI / Instrument Testing, mohon maaf apabila ada kesalahan atau informasi yang kurang tepat. Saran-sarannya bisa diberikan di kolom komentar 🙂

Sebenarnya masih ada satu tipe tes lagi yaitu testing for Multiple Apps dengan UI Automator. Secara sederhananya tes ini menguji interaksi user ketika menggunakan aplikasi kita, dengan aplikasi lain yang ada di ponsel user. Contohnya ketika aplikasi kita memanggil aplikasi SMS atau telepon. Manual mengenai UI Automator ada disini.

Thanks for reading 🙂

References :
Image Android Banner : Android Test
Android Testing

4 comments

    1. Terima kasih sudah berkunjung kesini 🙂

      Unit testing sangat berguna untuk refactoring code atau update feature apps.. karena ketika proses refactoring, kita bisa menjalankan unit testing kita untuk memastikan apakah code yang sedang kita refactor masih berfungsi atau tidak, saat kita melakukan beberapa perubahan

      Suka

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout /  Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout /  Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout /  Ubah )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.