2016-03-26 21 views
5
android studio 2.1. preview 4 

çiğ dizinde bulunan bir dosyayı açmak için test etmek için bir junit4 unit test oluşturma nullpointer çalışır.junit4(). openRawResource kullanarak Mockito

Ancak, kod çalıştırıldığında her zaman openRawResource numaralı boş gösterici olabilir.

Bu, sınamaya çalıştığım işlevdir. Bu, gerçek cihaz üzerinde çalışırken çalışır. Ama birim testinde değil.

public String getNewsFeed(Context mContext) { 
    InputStream inputStream = mContext.getResources().openRawResource(R.raw.news_list); // Null pointer 

    Writer writer = new StringWriter(); 
    char[] buffer = new char[1024]; 

    try { 
     InputStreamReader inputReader = new InputStreamReader(inputStream, "UTF-8"); 
     BufferedReader bufferReader = new BufferedReader(inputReader); 
     int n; 
     while ((n = bufferReader.read(buffer)) != -1) { 
      writer.write(buffer, 0, n); 
     } 

     inputStream.close(); 
    } 
    catch (IOException ioException) { 
     return ""; 
    } 

    return writer.toString(); 
} 

Bu benim test durumu tüm öneriler için

@RunWith(MockitoJUnitRunner.class) 
public class NewsListPresenterTest { 

    @Mock 
    private Context mContext; 
    @Mock 
    private NewsListPresenter mNewsListPresenter; 

    @org.junit.Before 
    public void setUp() throws Exception { 
     MockitoAnnotations.initMocks(this); 
     mNewsListPresenter = new NewsListPresenter(mContext); 
    } 

    @org.junit.Test 
    public void testLoadNewsFeed() throws Exception { 
     assertNotNull(mNewsListPresenter.getNewsFeed(mContext)); 
    } 
} 

Çok teşekkürler, Sen GetResources() üzerinde çağrıldığında ne yapacağını mContext alay anlatmak zorunda

+0

Lütfen logcat'ınızı gönderir misiniz? –

cevap

5

Android'de iki tür birim testini karıştırıyorsunuz. Bu pek çok insan için net değil, bu yüzden burada açıklayacağım.

Neden bir aygıtta çalışıyor: Çünkü bu aygıtla yapılan bir testtir. Cihazla test nedir? Gerçek bir cihaz/emülatör ve test kodunda çalışan bir test 'src/androidTest' klasöründedir. Yerel bir sınav testi olarak kullanılmadığından, yerel bir cinayet testi olarak neden bulamamaktadır?Yerel Junit testleri, bilgisayarınızda değil, bilgisayarınızın JVM'sinde çalışır. Yerel Junit testleri, Android kodlarını içermemeli/kullanmamalı çünkü gerçek Android kodu, bilgisayarınızın JVM'sinde değil, cihaz/emülatör üzerindedir.

Daha hızlı çalıştırmak için bir deneme testi olarak çalıştırılmasını istediğinizi varsayalım ve bu yüzden testinizi 'src/test' klasörüne taşıdığınızı ve context.getResources() 'in bir NullPointerException attığını varsayıyorum.

Buraya 2 seçenek var: o Android'in sınıflar bağlı değildir, böylece

  • yönteminizi Refactor bir junit testi olarak bu testi çalıştırmak için

    1. kullanın Robolectric

    için Seçenek 2, bunu ben yapardım.

    public String getNewsFeed(InputStream inputStream) {... use inputStream... } 
    

    Şimdi yöntem herhangi bir Android kod görmez ve normal bir junit yöntemi olarak test edebilirsiniz: Bir InputStream için yöntemin argüman değiştirin. Daha sonra, bu gibi, yönteme sahte inputStream geçebileceği:

    @Test 
    public void testLoadNewsFeed() throws Exception { 
        String fileContents = "line one"; 
        InputStream inputStream = new ByteArrayInputStream(fileContents.getBytes()); 
        assertNotNull(mNewsListPresenter.getNewsFeed(inputStream)); 
    } 
    

    sen (ben bunu tavsiye etmem) yine yapabiliriz uygulamanızda kullandığınız gibi hala aynı inputStream geçmek istiyorsanız o testte bu kodu kullanarak:

    @Test 
        public void testLoadNewsFeed() throws Exception { 
         InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("raw/news_list"); 
         assertNotNull(mNewsListPresenter.getNewsFeed(inputStream)); 
        } 
    

    Ve siz build.gradle dosyasına bu satırı eklemek gerekir:

    sourceSets.test.resources.srcDirs += ["src/main"] 
    

    Android birim testler çok kafa karıştırıcı olabilir. this blog mesajında ​​bu kavramları okuyabiliyorum. Yazdığım

  • 6

    olduğunu.

    when(mContext.getResources()).thenReturn(some_mock_of_Resources); 
    

    Hiçbir şey belirtmezseniz, sahte boş değer döndürülür.

    Örneğiniz için bu, muhtemelen bir Kaynak alayına da ihtiyaç duyacağınız anlamına gelir ve ayrıca bunun üzerinde openRawResource() çağrıldığında ne zaman/geri döneceğini de söylersiniz.

    +0

    Daha spesifik olabilir misiniz? Bunu yapıyorum: ne zaman (mContext.getResources()). ThenReturn (R.raw.news_list); Ama R.raw.news_list tanınmıyor. Bana biraz daha rehberlik edebilir misin? – ant2009

    +0

    Eğer ilk kez alay ile birim testleri yapıyorsanız, başlamak için mockito eğiticilerinden bazılarını takip etmek isteyebilirsiniz. –

    +0

    @ ant2009 nerede yapıyorsunuz? (MContext.getResources()). ThenReturn (R.raw.news_list) '? Gönderi kodunuzda değil. – gontard

    2

    Bir alay Context oluşturdunuz, böylece sınama altındaki yöntem tarafından kullanılan yöntemleri kullanmanız gerekir. getNewsFeed Reklamlara getNewsFeed çağırma önce testinde yüzden Context::getResources kullanıyor, sahip olmalıdır:

    Resources resoures = ... 
    when(mContext.getResources()).thenReturn(resources); 
    

    Mockito documentation of stubbing oldukça açıktır.


    Denemenizde bazı sorunlarınız da var. Onların hiçbir sonuçlarının olmadığını düşünüyorum, ancak Mockito'yu keşfettiğinizi gösterme eğilimindeler.

    yazmıştın:

    @Mock 
    private NewsListPresenter mNewsListPresenter; 
    

    alan ek açıklama @Mock ile, hangi sınıf test altında NewsListPresenter bir taklidini oluşturmak için Mockito söyler. (setUp'daki gerçek örneği oluşturduğunuz için büyük bir sorun değil). @InjectMocks'a bir göz atabilirsiniz (i am not a big fan of it olsa bile).

    Başka bir nokta, hem @RunWith(MockitoJUnitRunner.class) hem de MockitoAnnotations.initMocks(this)'u kullanmanızdır; bu, koşucu tarafından çağrıldığından beri sonuncusundan kaçınabilirsiniz. Ayrıca, koşucu, validate framework usage'dan beri daha iyi bir seçimdir.


    Son gözlemim, tasarımınız üzerindedir. Bir android kısıtlaması olabilir, ancak hem sunucu kurucusunda hem de getNewsFeed yönteminde bağlamı enjekte ediyorsunuz, bu garip görünüyor ve tutarsız duruma yol açabilir. Kurucu enjeksiyonunu seçerim (daha fazla nesne yönelimli tasarım).

    Sınama işlemini kolaylaştırmanın başka bir yolu, bir yardımcı program sınıfında yöntemin ana bölümlerini ayıklamak ve bir bağlama alay olmak gerekmeden farklı dalları (boş akış, boş akış, geçerli akış, IOException ...) test etmek olabilir. ve kaynaklar:

    class IOUtils { 
        static String readFromStream(InputStream inputStream) { 
         .... 
        } 
    } 
    

    Not o guava bir similar utility sağlar.