2016-08-03 18 views
10

Próbuję przetestować getJsonFromResource, która wywołuje loadNewsFeed. Chcę móc przetestować 2 przypadki 1, w których loadNewsFeed zwróci pusty ciąg, a drugi, w którym zwróci ciąg znaków json.Używanie funkcji mockito do testowania funkcji korzystającej z kontekstu

Próbuję więc wyśmiewać funkcję loadNewsFeed, aby zwrócić pusty ciąg znaków. Jednakże, gdy wywoływany jest beton getJsonFromResource, wywoła on prawdziwy loadNewsFeed i spowoduje wyjątek wskaźnika pustego. To co próbowałem w moich komentarzach badań wyjaśniając, co robię:

@Test 
public void shouldFailIfJSONStringIsEmpty() throws Exception { 
    /* Mock Context class */ 
    Context context = mock(Context.class); 
    /* initialize the concrete parseNewsFeed passing in the fake context */ 
    ParseNewsFeed parseNewsFeed = new ParseNewsFeed(context); 
    /* Create a mock of the parseNewsFeed so a fake call to loadNewsFeed will return an empty string */ 
    ParseNewsFeed mockParseNewsFeed = mock(ParseNewsFeed.class); 
    /* Mock the events that will be verified */ 
    ParseNewsFeedContract.Events<Status> mockEvents = mock(ParseNewsFeedContract.Events.class); 

    /* Return an empty string when loadNewsFeed is called */ 
    when(mockParseNewsFeed.loadNewsFeed()).thenReturn(""); 

    /* Called the concrete getJsonFromResource */ 
    parseNewsFeed.getJsonFromResource(mockEvents); 

    /* verify that onNewsFailure was called once and onNewsSuccess was never called */ 
    verify(mockEvents, times(1)).onNewsFailure(anyString()); 
    verify(mockEvents, never()).onNewsSuccess(any(Status.class)); 
} 

Jest to klasa próbuję przetestować.

public class ParseNewsFeed implements ParseNewsFeedContract { 
    private Context mContext; 

    public ParseNewsFeed(Context context) { 
     if(context != null) { 
      Timber.d("mContext != null"); 
      mContext = context; 
     } 
    } 

    /** 
    * Get the json from the local resource file and add to the cache to save loading each time 
    * @return the json in string representation 
    */ 
    @Override 
    public void getJsonFromResource(Events<Status> events) { 
     /* Get the json in string format */ 
     final String jsonString = loadNewsFeed(); 

     /* Check that is contains something */ 
     if(!jsonString.isEmpty()) { 
      try { 
       final Status status = new Gson().fromJson(jsonString, Status.class); 
       if(status != null) { 
        Timber.d("url: %s", status.getResults().get(0).getMultimedia().get(0).getUrl()); 
        events.onNewsSuccess(status); 
       } 
       else { 
        Timber.e("status == null"); 
        events.onNewsFailure("Failed to get results from json"); 
       } 
      } 
      catch (JsonSyntaxException e) { 
       Timber.e("Invalid JSON: %s", e.getMessage()); 
       events.onNewsFailure(e.getMessage()); 
      } 
     } 
    } 

    /** 
    * Opens and reads from the news_list and writes to a buffer 
    * @return return the json representation as a string or a empty string for failure 
    */ 
    public String loadNewsFeed() { 
     InputStream inputStream = mContext.getResources().openRawResource(R.raw.news_list); 
     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(); 
    } 
} 

Odpowiedz

3

Wygląda jak chcesz mieć ParseNewsFeed obiekt gdzie metoda loadNewsFeed zostało zgaszone, ale inne metody działają poprawnie. Najprostszym sposobem, aby dostać się, że będzie prawdopodobnie tworzyć szpiega, coś

ParseNewsFeed spyParseNewsFeed = Mockito.spy(new ParseNewsFeed(context)); 
Mockito.doReturn("").when(spyParseNewsFeed).loadNewsFeed(); 
5

Przede wszystkim dlatego oryginalny kod nie działa dlatego, że nie ma związku między dwoma obiektami parseNewsFeed i mockParseNewsFeed, stąd odgałęzienie, które robisz dla mockParseNewsFeed, nie ma żadnego efektu po wywołaniu parseNewsFeed.getJsonFromResource(mockEvents). Używanie spy sugerowało, że David Wallace będzie działał, ale gdybym był tobą, przepisałbym kod nieco inaczej, aby jeszcze łatwiej go przetestować.

Jedna obserwacja jest taka, że ​​kod w loadNewsFeed() metoda nie wydaje się mieć silny związek z klasy ParseNewsFeed, tak by wyodrębnić ten kod do obiektu (np NewsFeedLoader), a następnie mieć tego obiektu jako zależność klasy ParseNewsFeed. Wtedy możesz łatwo ośmieszyć ten Loader (zwróć "" lub dowolny ciąg, który chcesz podać, przekazując również Context i prawdopodobnie również identyfikator R.raw.news_list). Dzięki tej klasie Loader można nawet przetestować ją osobno od ParseNewsFeed i można ją ulepszyć (np. Lepszy sposób odczytu surowego zasobu) bez wpływu na klasę ParseNewsFeed.

3

Użyj metod z twojego wyśmianego kontekstu. Jest to faktycznie opisane w przykładzie: official tutorial here.

@Mock 
Context mMockContext; 

@Test 
public void readStringFromContext_LocalizedString() { 
    // Given a mocked Context injected into the object under test... 
    when(mMockContext.getString(R.string.hello_word)) 
      .thenReturn(FAKE_STRING); 
    ClassUnderTest myObjectUnderTest = new ClassUnderTest(mMockContext); 

    // ...when the string is returned from the object under test... 
    String result = myObjectUnderTest.getHelloWorldString(); 

    // ...then the result should be the expected one. 
    assertThat(result, is(FAKE_STRING)); 
Powiązane problemy