본문 바로가기
Programming/Android

Android Studio 단위테스트(Unit Test) 하기

by JAMINS 2016. 2. 21.

안드로이드 앱을 개발하다보면 정상적으로 작동하는지 테스트를 하는 경우가 많다. 한 기능을 추가하고 그 기능이 제대로 작동하는지 작은 단위로 테스트를 하기도하고 개발이 전부 완료되었을 때 출시하기 전에 종합적인 테스트를 하면서 앱의 품질을 체크하고 이상이 없으면 출시를 하게 된다.

이 때, 우리는 테스트를 어떤식으로 수행하는가? Spring, Java 웹 개발, Application 등 Java 기반의 소프트웨어를 테스트할 때는 주로 JUnit을 이용하여 단위테스트를 했을 것이다. 하지만 안드로이드 앱의 경우는 어떻게 해야하는가? 아마도 직접 앱을 실행해보고 결과를 확인하면서 진행한 경우가 많았을 것이다.

예를들어 회원가입 기능을 추가하였을 때, 이메일 형식의 ID인지 체크하고 비밀번호를 입력받아 가입시키는 기능을 테스트한다고 가정해보자. 우리는 이메일 형식인지 체크하는 로직을 직접 만든 후 앱이나 에뮬레이터를 실행하여 직접 결과를 확인하는 식의 단위 테스트를 진행했을 것이다. 그리고 로그를 찍어가며 문제가 뭔지 파악하고 수정하고 다시 테스트 하는 작업을 반복했을 것이다. 이러한 작업은 규모가 작을 경우는 문제되지 않을 수 있으나 앱의 규모가 커진다면 빌드하는 시간 + 테스트를 UI로 입력하는 시간 이 점점 길어질 것이다. 이메일 ID형식을 체크하는 기능을 테스트하기 위해 전체 앱을 매번 빌드하는 것은 너무나 비효율적이다. JUnit을 이용하면서 단위테스트를 진행한 것 처럼 안드로이드도 단위테스트를 지원하는 방법들이 여러가지 있다. 안드로이드에서 단위테스트를 하는 방법을 알아보자.

1. Testing Support Library 준비

단위테스트 전에 반드시 준비해야하는 것은 Testing Support Library다. 안드로이드에서는 단위테스트를 효율적으로 진행하도록 도와주는 몇 가지 툴을 제공한다.

  • AndroidJUnitRunner : JUnit4 기반 Android test runner
  • Espresso : UI 테스팅 프레임워크, 앱에서 추가한 UI기능 테스트에 적합
  • UI Automator : UI 테스팅 프레임워크, cross-app UI 테스팅에 적합

등이 있는데 이 중 AndroidJUnitRunner와 Espresso를 이용한 단위테스트를 진행해볼 것이다. Gradle을 이용하여 Testing Support Library를 가져온다. 안드로이드 프로젝트를 생성한 후 build.gradle 에서 defaultConfig와 dependencies 부분을 아래 처럼 추가한다.

defaultConfig 에서 testInstrumentationRunner는 반드시 입력해줘야 한다. 테스트를 진행하는 Runner를 설정해야만 테스트를 정상적으로 진행할 수 있다.

2. 테스트 만들기

준비가 끝났다면 그 다음은 테스트를 만들 차례다. 단위테스트를 하는 테스트 클래스를 생성하여 테스트할 로직에 대한 코드를 입력해야한다.

안드로이드 프로젝트를 생성하면 위와 같이 테스트 패키지가 보일 것이다. 여기에 테스팅 클래스를 정의하고 테스트하면된다. 만약 숫자를 더하는 Calculator 클래스가 정상적으로 작동하는지 검증하고 싶다면 테스트 클래스에 여러개의 시나리오를 작성하고 테스트를 해보면된다.

Calculator

public class Calculator {
    int a, b;

    public Calculator() { }

    public Calculator(int b, int a) {
        this.b = b;
        this.a = a;
    }

    public int add(int a, int b) {
        return a + b;
    }

    public boolean equalTo(int a, int b) {
        return a == b;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }
}

숫자를 더하는 간단한 클래스를 생성.

TestSample

import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class TestSample {
    private Calculator calculator;

    @Before
    public void setUp() {
        calculator = new Calculator();
    }

    @Test
    public void test() {
        int result = calculator.add(12, 11);
        assertThat(result, is(20));
    }
}

테스트 클래스를 생성하고 위와 같이 테스트를 만든다. 단위테스트를 할 때 Annotation을 자주 이용하는데 그 쓰임새에 대해 간략히 알아보자. @RunWith 로 AndroidJUnit4 라이브러리를 사용하게 하고 @Test로 하나의 단위테스트를 만들었다. @Test를 한 클래스에 여러개 지정하여 한번에 여러 기능들을 테스트 할 수도 있다. @Before은 테스트가 시작되기 전에 수행되는 부분이다.

테스트 시나리오는 Calculator의 add가 잘 동작하는지를 검증하기 위해 12 + 11을 계산해 보는 것이다. 우리의 예측대로라면 23이 나와야 테스트에 성공한 것인데 20을 주면 이 테스트는 실패한 것이다. 상황에 따라 실패하는 테스트를 만들어서 실패를 일부러 내보기도 한다. 여기서는 20을 줘서 실패하도록 해봤다.

3.테스트 수행하기

테스트 클래스를 생성하고 테스트 케이스를 만든 후 제대로 동작하는지 테스트를 수행해야한다. 메뉴 -> Run -> Edit Configurations 에서 Android Tests를 설정해야 테스트를 수행할 수 있다.

테스트의 이름을 작성한 후 Module을 app을 선택한다. Test는 하나의 Test Class를 수행할 것이므로 Class 선택. Class는 테스트 클래스인 TestSample을 선택하고 instrumentation runner는 gradle에서 설정한 AndroidJUnitRunner를 선택한다. OK를 누르면 테스트 수행하기 위한 준비가 다 완료된 것이다.

메뉴 바로 아래에 방금 생성한 TestSample을 지정하고 Run을 수행한다. 이 때, 에뮬레이터를 생성하던지 빌드할 기기가 연결되어 있어야만 한다.

기기를 연결한 후 테스트를 수행한 결과. 23이어야 하는데 20이라 AssertionError가 발생했다. 위의 시나리오대로 결과가 나온 것이다. 만약 23을 주었다면 녹색불이 켜졌을 것이다. 이처럼 각 용도에 맞게 다양한 테스트를 진행할 수 있다.

4. Android Activity

지금까지는 Android와 관련된 테스트를 한 것이 아닌 계산하는 로직에 대해서만 테스트를 진행하였다. Android의 Activity에서 정보를 받아와서 테스트를 하는 방법 역시 존재한다. 테스트 시나리오를 만들어보자. res의 String.xml에 하나의 문자열을 추가하고 이것이 제대로 불어와지는지 검증하는 코드를 작성하고 실행후 녹색불이 나온다면 성공한 것이다.

  1. String.xml 에 문자열을 하나 지정
  2. TestString 이름의 테스트 클래스를 생성

TestString

import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@RunWith(AndroidJUnit4.class)
public class TestString {
    private MainActivity activity;

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

    @Before
    public void setUp() {
        this.activity = mActivityRule.getActivity();
    }

    @Test
    public void testString() {
        String str = mActivityRule.getActivity().getString(R.string.testStr);
        assertThat("hihihi", is(str));
    }
}

MainActivity의 Resource를 가져와서 String을 이용할 것이기 때문에 MainActivity 클래스를 선언하고 @Rule을 이용하여 Activity를 테스트와 연결한다. @Before을 통해 getActivity를 한 후 테스트에서 해당 문자열을 가져온다. 이 문자열이 적절한지 assertThat을 이용하여 체크한다. 여기서는 "hihihi'로 만들었기 때문에 테스트가 성공적으로 끝날것이다.

이를 응용하여 다양한 테스트를 작성하는 것이 가능하다. 치밀하고 꼼꼼한 테스트 케이스 작성이 품질이 좋은 앱을 만들게 하고 더욱더 효율적으로 개발할 수 있게 한다. 다음은 Android UI를 직접 제어하는 방식의 Espresso 테스트를 알아볼 것이다.

댓글1

  • 안드로이드 2016.12.13 11:34

    다른곳 보면은 Build Variants 에서 Test Artifact설정 하던데
    제가 안드로이드 스튜디오가 2.2.2라서 그런가
    없더라구요.. 혹시 뭔지 아시나요??
    답글