Unit Testing in Android에 대한 심도 있는 연구

10729 단어 unittestingAndroid
1. Testing for ContentProvider는 Provider를 위한 Case를 쓰기 전에 SDK 문서에서 Provider 테스트에 대한 설명을 자세히 읽어야 합니다.그러나 그 설명만 읽으면 정확한 Case를 쓸 수 없다. 왜냐하면 너도 알다시피 안드로이드의 문서는 비교적 형편없다. 일부 관건적인 문서에는 설명이 없다. 너도 알다시피 이것은 안드로이드에서 적지 않다.Provider의 Case를 작성하세요. 다음과 같습니다.

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
}
컴파일에 오류가 있습니다. Provider Test Case2에 은밀한 구조가 없다고 합니다. 구조 함수가 필요합니다. 표준 JUnit 구조를 작성하십시오!

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    public FeedProviderTest(String name) {
        super(name);
    }
}
WTF, 여전히 컴파일 오류가 있고 더 심각합니다!설마 Provider Test Case2는 Test Case에서 계승된 것이 아니라 Eclipse의 건의를 사용하여 두 개의 매개 변수가 있는 구조를 만들었다.

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    public FeedProviderTest(String name) {
        super(name);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
        // TODO Auto-generated constructor stub
    }
}
그러나 이름만 있는 Feed Provider Test(String name)는 오류가 있다. 다시 매개 변수가 없는 것을 시도해 보자. 이것은 Provider Test Case2에 이런 구조 함수가 없다는 것을 설명한다. 그러나 일리가 없다. 왜냐하면 이것은 Test Case에서 계승된 것이기 때문이다.신기하고 이상해!Provider Test Case2는 매개 변수의 구조가 하나도 없기 때문에 매개 변수가 있는 구조를 제거할 수 밖에 없습니다!

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

    public void testConstructor() throws Throwable {
        assertNotNull("can construct resolver", getMockContentResolver());
        ContentProvider provider = getProvider();
        assertNotNull("can instantiate provider", provider);
    }
}
기본적인 테스트를 작성하고 실행하여 Warning을 받았습니다. JUnit Framework에서 보도한 데모 Provider Test는 공공의 구조 함수인 Test Case(name)나 Test Case()를 정의하지 않았습니다. 어떤 상황인지 제가 정의하지 않은 것이 아니라 컴파일 오류가 있습니다. 빌어먹을 Provider Test Case2는 이 두 가지 구조가 없기 때문입니다!빌어먹을, 이 구조를 다시 추가할 수밖에 없어!그러나 부류가 없기 때문에 부류의 이중 매개 변수의 구조만 인용할 수 있습니다!

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> { 
    public DemoProviderTest() {
        super(null, null);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

    public void testConstructor() throws Throwable {
        assertNotNull("can construct resolver", getMockContentResolver());
        ContentProvider provider = getProvider();
        assertNotNull("can instantiate provider", provider);
    }
}
그런데 매개 변수는 무엇을 전달합니까?일단 Null로 해볼게요!완전히 오류가 있습니다. 부류의 구조를 초기화할 때 NPE가 발생했습니다. 이것은 Null이 틀림없이 옳지 않다는 것을 설명합니다!두 개의 파라미터가 있는 구조 Demo Provider Test(Class provider Class, String provider Authority)를 보았고, Class 대상과 Provider Authority를 전달해야 한다고 말했습니다. 다시 시도해 보세요!

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    public DemoProviderTest() {
        super(FeedProvider.class, AUTHORITY);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

    public void testConstructor() throws Throwable {
        assertNotNull("can construct resolver", getMockContentResolver());
        ContentProvider provider = getProvider();
        assertNotNull("can instantiate provider", provider);
    }
}
이번에Okay가 되었습니다. 그러나 이렇게 하면 두 개의 매개 변수의 구조가 의미가 없습니다. 그래서 한 개의 매개 변수를 두 개의 매개 변수로 호출하도록 합니다.

    public DemoProviderTest() {
        this(FeedProvider.class, AUTHORITY);
    }
역시Okay입니다. 이것은 우리의 Case가 반드시 Provider Test Case2에 정확한 구조 매개 변수를 제공해야 한다는 것을 설명합니다!게다가 set Up과 Tear Down:

    @Override
    public void setUp() throws Exception {
        mContentResolver = getMockContentResolver();
    }

    @Override
    public void tearDown() throws Exception {
        mContentResolver = null;
    }
이 실행되고 테스트 Constructor가 끊긴 것을 발견했습니다. get Mock Content Resolver () 가 Null로 돌아왔다고 했습니다. 그럴 리가 있겠습니까? 너무 이상합니다!초기화가 정확하지 않을 수도 있다는 생각에 setUp에 부류의 호출을 추가했습니다.

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mContentResolver = getMockContentResolver();
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
        mContentResolver = null;
    }
다음에 뛰겠습니다. 모두Okay입니다. 부류를 다시 쓰는 방법과 관련된 모든 방법은 부류의 방법을 호출하여 정확하게 초기화해야 한다는 것을 설명합니다!다음은 정확한 완전판이다.

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    private ContentResolver mContentResolver;

    public DemoProviderTest() {
        this(FeedProvider.class, AUTHORITY);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mContentResolver = getMockContentResolver();
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
        mContentResolver = null;
    }

    public void testConstructor() throws Throwable {
        assertNotNull("can construct resolver", getMockContentResolver());
        ContentProvider provider = getProvider();
        assertNotNull("can instantiate provider", provider);
    }
}
이 예에서 얻은 경험은 구성 요소에 대한 테스트는 모두android에서 계승해야 한다는 것이다.test.*아래의 구성 요소 테스트 프레임워크는 이 구성 요소 테스트 프레임워크에 정확한 파라미터를 전달해야 합니다. 그렇지 않으면Case는 테스트할 수 없습니다. 두 개의 구조 함수

    public DemoProviderTest() {
        this(FeedProvider.class, AUTHORITY);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }
는 하나도 빠질 수 없습니다. 또한 JUnit의 지정된 구조 함수(String이 있거나 파라미터가 없는 것)는 테스트 구조가 지정한 구조를 호출하여 테스트 프레임워크에 정확한 파라미터를 전달해야 합니다!그리고 부류 방법을 다시 쓸 때 부류 방법도 반드시 호출해야 한다. 그렇지 않으면 초기화가 정확하지 않다!그런데 이 구성 요소 테스트 프레임워크가 정말 안 쓰인다고 할 수 밖에 없어요. 우선 그 이름은 이해가 안 돼요. 왜 2가 있어요!안드로이드 진짜 2다!그리고 기왕 틀로 삼았으니 초기화된 작업을 완전하고 철저하게 해야 틀이라고 할 수 있다.사용자는 계승만 하고 자신의 일을 끝내면 작업을 할 수 있어야 한다. 구성 요소 Activity나 Content Provider처럼 코드에 들어갈 때 프레임워크의 초기화 작업은 이미 끝났기 때문에 계승자는 당신의 초기화 작업에만 관심을 가지면 된다!그러나 테스트 프레임워크는 엉망이 되고 계승자는 자신의 초기화에 관심을 가져야 할 뿐만 아니라 부류에게 정확한 매개 변수를 전달해야 한다!2. Testing for Activity 역시 Activity에 대한 테스트는 초기화 부분에 주의해야 한다. 다만 set Up과 Tear Down에 대해 슈퍼를 조절하지 않아도 상관없다!

public class ExplorerActivityTester extends
        ActivityInstrumentationTestCase2<ExplorerActivity> {
    public ExplorerActivityTester() {
        this(TARGET_PACKAGE_NAME, ExplorerActivity.class);
    }

    public ExplorerActivityTester(String pkg, Class<ExplorerActivity> class1) {
        super(pkg, class1);
    }

    @Override
    public void setUp() {
        mInstrumentation = getInstrumentation();
    }
}
3. Obstacles to unit testing은 Android에서 시스템 구조의 특성으로 인해 Android에 단원 테스트 용례와 검증 테스트 용례를 결정합니다. 특히 어렵기 때문입니다. Activity reuse 원인은 모든 테스트 가방입니다. 테스트 가방도 하나의 Apk입니다. 모든 가방은 하나의 목표 Apk만 주입할 수 있습니다. 즉, 하나의 Apk 안의 내용만 테스트할 수 있습니다. 어떤 조작이 Apk 이외의 곳으로 넘어가면테스트 프레임워크의 제어 범위를 넘어섰다.그러나 구성 요소 재사용 메커니즘은 안드로이드에서 매우 보편적이다. Intent를 통해 다른 응용 프로그램(apk)에서 다른 응용 프로그램의 구성 요소를 호출하여 어떤 조작을 완성하는 것은 안드로이드의 특성으로 더 이상 보편적이지 않다!그러나 이것은 단원 테스트 용례에 넘을 수 없는 장애를 묻었다.테스트 프레임워크 자체가 더욱 약하지만 어떤 구성 요소가 튀어나오면 Instrumentation은 이를 제어할 수 없다. 소스 테스트 프레임워크인robutium-solo는 어느 정도에 이 질문을 해결했다. 솔로는 가방 안의 모든 구성 요소를 조작할 수 있다. 특히 여러 개의 Activity가 뛰는 문제를 해결할 수 있다. 그러나 앞에서 말한 바와 같이 하나의 테스트 Apk는 하나의 목표 Apk만 주입할 수 있기 때문에 일단 Activity가 응용 밖으로 튀어나오면솔로도 방법이 없어.이것은 무해한 문제다.따라서 Android에서 테스트를 하면 일부 논리층, API층, 데이터와 Provider, 서비스 등 표층과 비교적 멀리 작동하는 코드만 주목할 수 있다!표층 Activity가 이리저리 뛰어다니는 경우 일부 테스트를 하거나 MockObject로 해결할 수 있지만 이것은 테스트 자체의 의미를 잃는다. 왜냐하면 MockObject를 만드는 데 많은 시간이 걸리기 때문이다. 가치가 없다!b. ActionBar is not clickable 또 하나의 징그러운 문제는 Activity의 ActionBar에 대해 직접 클릭할 수 없다는 것이다. Google이 도대체 무엇을 하고 있는지 모르겠다. 새로운 것을 만들었는데 테스트 프레임워크에서 조작을 지원하지 않는다니!ActionBar를 누르면 솔로를 통해서만 화면 좌표를 클릭할 수 있다고 생각하면 이식과 유지보수가 매우 어렵습니다!조작에 대해 말하자면 원본 프레임워크 Instrumentation이 지원하는 조작이 매우 적고 사용하기 좋지 않다. 이것은 Key Event 이벤트만 보낼 수 있고 많은 경우에 사용하기 어렵다. 예를 들어 대화상자가 있는데Okay나 Cancel을 누르려면 매우 번거롭다. 그리고 ListView의 어떤 항목을 누르려면 매우 번거롭다.마찬가지로 제3자의robotium-solo 프레임워크는 많이 사용되었고 솔로를 통해 잘 봉인되었다.클릭OnText()를 클릭하면 화면에 있는 이 텍스트가 있는 View를 쉽게 클릭할 수 있습니다.그것의 내부 실현 방식은 View의 디스플레이 트리를 통해 Tag(문자)에 따라 관련된View를 찾아서 클릭 이벤트를 보내는 것입니다!이것 또한 왜 솔로도 ActionBar를 클릭할 수 없는지 설명한다. 왜냐하면 ActionBar는 Activity의 View에 있지 않고 StatusBar처럼 시스템급에 속하기 때문이다!c. StatusBar belongs to Settings.apk는 상상하기 힘들죠. 어디서나 볼 수 있는 Statusbar가 Settings에 속한다는 것은 Settings의 가방을 주입해야만 Statusbar를 조작할 수 있습니다.그래서 Statusbar에 당신의 Apk와 관련된 것들(예를 들어 힌트)이 있지만 Settings를 전문적으로 주입하지 않으면 직접 조작할 수 없습니다.apk의 테스트 패키지!4. Security Concern 테스트의 코드(Instrumentation과 Test Runner)도 하나의 Apk 형식으로 존재한다. 이것은 모든 목표 Apk를 주입한 후에 이를 조작할 수 있고 심지어 자원과 데이터를 얻을 수 있다.이것은 안전상의 문제를 가져왔다!테스트 코드가 있는 Apk를 하나의 응용 프로그램으로 삼아 어떤 휴대전화에서 실행되면 어떤 응용 프로그램도 조작할 수 있다.사실 이것은 문제가 아니다. 만약에 응용 시장이 개발자가 올린 응용에 대해 엄격한 테스트와 심사를 할 수 있다면.하지만 지금의 문제는 Google Play든 다른 시장이든 테스트를 잘 하지 않기 때문에 불량자를 유기적으로 만들 수 있다는 것이다!사실 이곳의 관건은 안드로이드 업체들이 맹목적으로 수량을 추구하지 말아야 한다는 데 있다.앱을 집중적으로 판매하는 것은 애플이 생각해낸 아이디어이고, 애플의 앱스토어도 가장 잘한다!Android는 단지 모방자일 뿐이기 때문에 당신은 발전이 느리고 수량이 많지 않으며 품질이 부족하고 수입이 좋지 않습니다. 정상적인 것입니다. 당신은 추종자이기 때문에 늦게 시작합니다!제조업체에 대해 말하자면, 수량을 당신은 통제할 방법이 없고, 한꺼번에 몇 만 개의 응용을 할 수 없다. 이것은 시간이 필요하지만, 적어도 당신은 품질을 엄격하게 통제할 수 있다.당신은 업로드된 응용 프로그램에 대해 엄격한 테스트를 할 수 있습니다. 이것은 사용자에 대한 책임이자 자신에 대한 책임입니다!그래서 장치든 응용 프로그램이든 모두 Apple이 양질의 것을 원한다. Android는 항상 불량해야 하기 때문에 Apple의 물건을 보면 가격이 높고 Android가 싸다. 물론 가격도 Android의 유일한 장점이다!현 사회는 한 푼에 한 푼의 물건이니 싸면 자연히 좋은 물건이 없다!

좋은 웹페이지 즐겨찾기