2016-10-12 2 views
12

मेरे पास एक एंड्रॉइड ऐप है जिसमें MyApplication कक्षा है जो Application से विरासत में है।प्रत्येक यूनिट परीक्षण के लिए मेरे आवेदन वर्ग का नया उदाहरण कैसे प्राप्त करें?

मैंने कुछ यूनिट परीक्षण बनाए हैं जो @RunWith(AndroidJUnit4.class) के साथ चलते हैं। यदि मैं प्रत्येक परीक्षा को अलग से चलाता हूं तो वे सभी पास होते हैं। अगर मैं उन्हें एक साथ चलाता हूं - पहला एक गुजरता है और फिर (कुछ) अन्य विफल हो जाते हैं।

समस्या यह है कि ऐसा लगता है कि MyApplication उसमें केवल एक ही बनाया जाता है और फिर इसे संरक्षित और सभी परीक्षण जिसकी वजह से विफल रहता है क्योंकि MyApplication में एक राज्य में जो केवल एक बार प्रारंभ किया जाना चाहिए है वहाँ के लिए प्रयोग किया जाता है।

क्या यूनिट परीक्षण (एंड्रॉइडटेस्ट) चलाने का कोई तरीका है इसलिए प्रत्येक परीक्षण के लिए एप्लिकेशन पुनरारंभ किया जाता है? मुझे परवाह नहीं है कि यह धीमा हो जाएगा (उदाहरण के लिए ऐप को हर बार पुनर्स्थापित करना होगा) मैं सिर्फ परीक्षणों को एक-दूसरे से स्वतंत्र रूप से चलाने के लिए चाहता हूं। (के रूप में @Zinc द्वारा अनुरोध) इकाई परीक्षण से

वास्तविक कोड की तरह दिखता है:

@RunWith(AndroidJUnit4.class) 
public class AutoLogin_ActMainTest { 
    @Rule 
    public ActivityTestRule<ActMain> mActivityRule = new ActivityTestRule<ActMain>(
      ActMain.class) { 


     @Override 
     protected void beforeActivityLaunched() { 
      super.beforeActivityLaunched(); 

      MyTestApp app = (MyTestApp) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext(); 
      DependencyInjector.reset(); 
      app.reset(); 


      FakeUnitDaggerModule fudm = new FakeUnitDaggerModule(); 

      Session session = new SessionImpl(new TimeProviderImpl()); 
      fudm.setResMain(new ResMainTest(session)); 

      FakeAppPrefs appPrefs = new FakeAppPrefs(); 
      FakeLoginPrefs loginPrefs = new FakeLoginPrefs(); 
      CurrentUserHolder currentUserHolder = new CurrentUserHolder(); 

      FakeComponent inj = DaggerFakeComponent.builder(). 
        fakeMyAppDaggerModule(new FakeMyAppDaggerModule(app, appPrefs, loginPrefs, currentUserHolder)). 
        appInfoDaggerModule(new AppInfoDaggerModule("1")). 
        fakeSessionDaggerModule(new FakeSessionDaggerModule(session)). 
        fakeExchangeDaggerModule(new FakeExchangeDaggerModule("https://test.com")). 
        fakeUnitDaggerModule(fudm). 
        build(); 

      DependencyInjector.init(inj); 
      DependencyInjector.getInstance().inject(app); 


      app.onStart(); 
     } 
    }; 


    @Test 
    public void testAutoLogin() { 
     ElapsedTimeIdlingResource idlingResource = new ElapsedTimeIdlingResource(500); 
     Espresso.registerIdlingResources(idlingResource); 
     idlingResource.startWaiting(); 

     onView(ViewMatchers.withId(R.id.tv_logged_in_as)).check(matches(isDisplayed())); 
     Espresso.unregisterIdlingResources(idlingResource); 
    } 
} 
+0

क्या आप अपना कोड साझा कर सकते हैं? – Zinc

+0

@Zinc परीक्षणों में से एक से कोड जोड़ा गया – Ognyan

+0

आप डिगर का उपयोग क्यों नहीं कर रहे हैं? – Saveen

उत्तर

4

समस्या यह है कि ऐसा लगता है कि MyApplication उसमें केवल एक ही बनाया जाता है और फिर इसे संरक्षित है और सभी परीक्षणों के लिए उपयोग किया जाता है जो विफल रहता है क्योंकि MyAplication में एक राज्य है जिसे केवल एक बार आरंभ किया जाना चाहिए।

आईएमएचओ, यह ऐप में एक बग है, जिसे ठीक किया जाना चाहिए। Application बहुत सच्चे व्यापार तर्क के लिए एक अनुपयुक्त स्थान है (हालांकि यह आपके क्रैश-रिपोर्टर लाइब्रेरी को शुरू करने के लिए ठीक है, StrictMode, आदि)। अन्य सभी को सीधे, इंजेक्शन इंजेक्शन इत्यादि के माध्यम से, मोक्स के माध्यम से, टेस्टेबल होना चाहिए।

कहा जा रहा है कि, कभी-कभी समस्या उस कोड में नहीं है जिसे आप नियंत्रित करते हैं, बल्कि पुस्तकालयों या ढांचे से कोड।

क्या यूनिट परीक्षण (एंड्रॉइडटेस्ट) चलाने का कोई तरीका है इसलिए प्रत्येक परीक्षण के लिए एप्लिकेशन पुनरारंभ किया जाता है?

आजकल, हाँ, हालांकि इस सवाल से पूछा गया था। Android Test Orchestrator (एटीओ) परीक्षण निष्पादन गति की लागत पर, प्रत्येक परीक्षण को अलग करने का बेहतर काम करता है।

+0

ऐप क्लास में एकमात्र कोड निर्भरता इंजेक्टर (डैगर 2) का प्रारंभिकरण है। यह एटीओ बिल्कुल वैसा ही दिखता है जो मैं ढूंढ रहा हूं लेकिन मुझे यह चिंता है: इसके दस्तावेज़ों में यह कहा गया है: "इसलिए, यदि आपके परीक्षण ऐप स्टेटस साझा करते हैं, ** उस साझा स्थिति का अधिकांश ** आपके डिवाइस के सीपीयू या मेमोरी से हटा दिया जाता है प्रत्येक परीक्षण के बाद "। "सबसे" शब्द चिंता का विषय है - क्या आपको पता है कि क्या हटाया नहीं गया है, या कम से कम मुझे एटीओ का कोड मिल सकता है ताकि इसे स्वयं देख सकें? – Ognyan

+0

@ ओग्नीन: "क्या आपको पता है कि क्या हटाया नहीं गया है" - जब मैंने एटीओ को अपने पैसों के माध्यम से रखा, तो यह प्रत्येक परीक्षण के लिए ताजा प्रक्रियाएं बनाना प्रतीत होता था। मुझे नहीं पता कि दस्तावेज़ों का वह हिस्सा किस बात का जिक्र कर रहा है। "जहां मैं खुद को देखने के लिए एटीओ का कोड पा सकता हूं?" - माफ करना, मेरे सिर के ऊपर से नहीं। – CommonsWare

+0

यदि कोई दिलचस्पी लेता है: एक चीज जिसे हटाया नहीं जाता है वह सार्वजनिक डीआईआर में बनाई गई फाइलें हैं (यानी 'getExternalFilesDir() ')। अभी भी परीक्षण करने में सक्षम नहीं है कि निजी डीआईआर में फाइलें संरक्षित हैं, लेकिन अगर ऐसा है तो आपको उदाहरण के लिए http कैश जैसे छिपी हुई फाइल निर्माण पर अतिरिक्त ध्यान देना होगा। एक और गड़बड़ हो सकती है कि "बाहरी" डिवाइस जैसे कैमरे, जीपीएस, आदि जिन्हें प्रत्येक परीक्षण के बीच मैन्युअल रीसेट की आवश्यकता हो सकती है। – Ognyan

0

मैं अपने प्रश्न के बारे में काफी यकीन नहीं है। लेकिन आप एप्लिकेशनटेस्टकेस का उपयोग कर सकते हैं।

public class MyApplicationTest extends ApplicationTestCase<MyTestApp> { 
    public void test1() { 
     createApplication(); 

     ... test here ... 

     terminateApplication(); 
    } 

    public void test2() { 
     createApplication(); 

     ... test here ... 

     terminateApplication(); 
    } 
} 

रेफरी:: जैसे कुछ बात https://developer.android.com/reference/android/test/ApplicationTestCase.html

+0

में वर्णित किया है, मुझे डर है कि मैं 'एप्लिकेशनटेस्टकेस' का उपयोग नहीं कर सकता, मुझे नियम के पहले 'एक्टिविटी लांच() 'की आवश्यकता है। इसके अलावा 'ApplicationTestCase' को बहिष्कृत किया गया है ... – Ognyan

+0

** ApplicationTestCase ** को बहिष्कृत नहीं किया गया है? –

2

आप की जरूरत है अपने अनुप्रयोग refactor करने के लिए इतना है कि जो कुछ भी कोड नियंत्रित कर रहा है राज्य आवेदन वर्ग के लिए है, लेकिन किसी अन्य वस्तु में बाध्य नहीं है। फिर आप उस टुकड़े को रीसेट कर सकते हैं या एप्लिकेशन क्लास के दृढ़ता की देखभाल किए बिना इसे नकल कर सकते हैं। अधिमानतः यह कुछ फॉर्म निर्भरता इंजेक्शन का उपयोग करके किया जाता है।

+0

एक और वस्तु वास्तव में एक समाधान नहीं है क्योंकि यह केवल उस ऑब्जेक्ट से उस समस्या से समस्या को स्थानांतरित कर रहा है (मैंने समस्या के साथ बहुत कुछ खेला है और DI सहित विभिन्न समाधान)। मुझे पता है कि Google अनुशंसा करता है "आमतौर पर आवेदन को उपclass करने की आवश्यकता नहीं होती है। ज्यादातर स्थितियों में, स्थिर सिंगलेट्स समान कार्यक्षमता प्रदान कर सकते हैं"। वर्तमान में मैं प्रत्येक परीक्षा की शुरुआत में आवेदन स्थिति को रीसेट कर रहा हूं, लेकिन यह एक हैक है। मैं उम्मीद कर रहा था कि हर बार नए आवेदन के साथ परीक्षण करने के लिए परीक्षण करने का वास्तविक तरीका है। कुछ ग्रिड सेटिंग या यहां तक ​​कि एसडीके कॉल की तरह। – Ognyan

0
public class TestApplication extends Application { 

@Override 
public void onCreate() { 
    super.onCreate(); 
    // Sdk.terminate(); - If you specify TestApplication as an 
    //     application class in AndroidManifest, 
    //     you'll have to uncomment this(due to issue with test runner) 
    Sdk.initialize(); 
} 

@Override 
public void onTerminate() { 
    super.onTerminate(); 
    Sdk.terminate(); 
} 
} 

Sdk वर्ग

public class Sdk { 

private static Sdk sInstance; 
private void Sdk(){ 
} 

public static Sdk getInstance() throws RuntimeException { 
    if (sInstance == null) { 
     throw new RuntimeException(); 
    } 
    return sInstance; 
} 

public static void terminate() { 
    sInstance = null; 
} 

public static void initialize() { 
    if (sInstance == null) { 
     sInstance = new Sdk(); 
     //save some information according to what is on the default configurations 
    } else { 
     throw new RuntimeException("Method was already initialized"); 
    } 
}} 

टेस्ट:

public class MyApplicationTest extends ApplicationTestCase<TestApplication> { 

public MyApplicationTest() { 
    super(TestApplication.class); 
} 

public void testMultiplicationTests() { 
    createApplication(); 

    int answer = 42; 
    assertEquals(42, answer); 

    terminateApplication(); 
} 


public void testDefaultSettings() { 
    createApplication(); 

    assertNotNull(Sdk.getInstance()); 

    terminateApplication(); 
}} 
+0

जैसा कि मैंने एक और टिप्पणी में उल्लेख किया है 'ApplicationTestCase' को बहिष्कृत किया गया है और मुझे वास्तव में नए' ActivityTestRule' का उपयोग करने की आवश्यकता है। अन्यथा समाधान चाल चल रहा है (मैं अब इसी तरह के दृष्टिकोण का उपयोग कर रहा हूँ)। – Ognyan

संबंधित मुद्दे