2017-07-02 89 views
0
Android Studio 2.3 

我有以下方法我想我的模型類的內部測試:嘲笑我的測試類中的方法

public class RecipeListModelImp implements RecipeListModelContract { 

    private Subscription subscription; 
    private RecipesAPI recipesAPI; 
    private RecipeSchedulers recipeSchedulers; 

@Inject 
public RecipeListModelImp(@NonNull RecipesAPI recipesAPI, @NonNull RecipeSchedulers recipeSchedulers) { 
    this.recipesAPI = Preconditions.checkNotNull(recipesAPI); 
    this.recipeSchedulers = Preconditions.checkNotNull(recipeSchedulers); 
} 

@Override 
public void getRecipesFromAPI(final RecipeGetAllListener recipeGetAllListener) { 
    subscription = recipesAPI.getAllRecipes() 
      .subscribeOn(recipeSchedulers.getBackgroundScheduler()) 
      .observeOn(recipeSchedulers.getUIScheduler()) 
      .subscribe(new Subscriber<List<Recipe>>() { 
       @Override 
       public void onCompleted() { 
       } 

       @Override 
       public void onError(Throwable e) { 
        recipeGetAllListener.onRecipeGetAllFailure(e.getMessage()); 
       } 

       @Override 
       public void onNext(List<Recipe> recipe) { 
        recipeGetAllListener.onRecipeGetAllSuccess(recipe); 
       } 
      }); 
} 

@Override 
public void shutdown() { 
    if(subscription != null && !subscription.isUnsubscribed()) { 
     subscription.unsubscribe(); 
    } 
} 
} 

在我的測試類我測試這樣的:

public class RecipeListModelImpTest { 

    @Mock Subscription subscription; 
    @Mock RecipesAPI recipesAPI; 
    @Mock RecipeListModelContract.RecipeGetAllListener recipeGetAllListener; 
    @Mock List<Recipe> recipes; 

    @Inject RecipeSchedulers recipeSchedulers; 

    private RecipeListModelContract recipeListModel; 

    @Before 
    public void setup() { 

     TestBusbyComponent testBusbyComponent = DaggerTestBusbyComponent.builder() 
       .mockRecipeSchedulersModule(new MockRecipeSchedulersModule()) 
       .build(); 

     testBusbyComponent.inject(RecipeListModelImpTest.this); 

     MockitoAnnotations.initMocks(RecipeListModelImpTest.this); 
     recipeListModel = new RecipeListModelImp(recipesAPI, recipeSchedulers); 
    } 

    @Test(expected = NullPointerException.class) 
    public void testShouldThrowExceptionOnNullParameter() { 
     recipeListModel = new RecipeListModelImp(null, null); 
    } 

    @Test 
    public void testRecipeListModelShouldNotBeNull() { 
     assertNotNull(recipeListModel); 
    } 

    @Test 
    public void testShouldGetRecipesFromAPI() { 
     when(recipesAPI.getAllRecipes()).thenReturn(Observable.just(recipes)); 

     recipeListModel.getRecipesFromAPI(recipeGetAllListener); 

     verify(recipesAPI, times(1)).getAllRecipes(); 
     verify(recipeGetAllListener, times(1)).onRecipeGetAllSuccess(recipes); 
     verify(recipeGetAllListener, never()).onRecipeGetAllFailure(anyString()); 
    } 

    @Test 
    public void testShouldFailToGetRecipesFromAPI() { 
     when(recipesAPI.getAllRecipes()) 
       .thenReturn(Observable.<List<Recipe>>error(
         new Throwable(new RuntimeException("Failed to get recipes")))); 

     recipeListModel.getRecipesFromAPI(recipeGetAllListener); 

     verify(recipesAPI, times(1)).getAllRecipes(); 
     verify(recipeGetAllListener, times(1)).onRecipeGetAllFailure(anyString()); 
     verify(recipeGetAllListener, never()).onRecipeGetAllSuccess(recipes); 
    } 

    @Test 
    public void testShouldShutdown() { 
     when(subscription.isUnsubscribed()).thenReturn(false); 
     final Field subscriptionField; 

     try { 
      subscriptionField = recipeListModel.getClass().getDeclaredField("subscription"); 
      subscriptionField.setAccessible(true); 
      subscriptionField.set(recipeListModel, subscription); 
     } catch(NoSuchFieldException e) { 
      e.printStackTrace(); 
     } 
     catch(IllegalAccessException e) { 
      e.printStackTrace(); 
     } 

     recipeListModel.shutdown(); 

     verify(subscription, times(1)).unsubscribe(); 
    } 
} 

但是,問題是我的模型類中的訂閱始終爲空,因此永遠不會進入if blook。有什麼方法可以用Mockito或spys來測試嗎?

非常感謝您的任何建議,

+1

當然。做你所做的事情,但一定要將模擬訂閱注入RecipeListModel。如果不瞭解代碼,就很難提供建議。 –

回答

1

你應該測試recipeListModel類,那就是你有shutdown()方法,設置模擬到這個班。

如果你沒有設置方法爲認購recipeListModel,或構造PARAM ....),你可以設置模擬對象與像反射:

@Test 
public void testShouldShutdown() { 
    Subscription subscription = mock(Subscription.class); 
    when(subscription.isUnsubscribed()).thenReturn(false); 

    Field subscriptionField = recipeListModel.getClass().getDeclaredField("subscription"); 
    subscriptionField.setAccessible(true); 
    subscriptionField.set(recipeListModel, subscriptionMock); 

    recipeListModel.shutdown(); 

    verify(subscription, times(1)).unsubscribe(); 
} 

你更新後

,如果你不能改變創作方式,你應該嘲笑它像(創建全路),我不知道您的API,所以它只是想法:

Subscription subscription = mock(Subscription.class); 
when(subscription.isUnsubscribed()).thenReturn(false); 

// preparation mock for create Subscription 
//for recipesAPI.getAllRecipes() 
Object mockFor_getAllRecipes = mock(....); 
when(recipesAPI.getAllRecipes()).thenReturn(mockFor_getAllRecipes); 

//for subscribeOn(recipeSchedulers.getBackgroundScheduler()) 
Object mockFor_subscribeOn = mock(); 
when(mockFor_getAllRecipes.subscribeOn(any())).thenReturn(mockFor_subscribeOn); 

//for .observeOn(recipeSchedulers.getUIScheduler()) 
Object mockFor_observeOn = mock(); 
when(mockFor_subscribeOn .observeOn(any())).thenReturn(observeOn); 


// for .subscribe 
when(observeOn.subscribe(any()).thenReturn(subscription); 
+0

+ sbavateam感謝您的回答。我使用完整的源代碼編輯了我的問題。我喜歡設置方法的想法。但是它使得代碼在異常處理中看起來非常難看。有沒有其他的方式來做到這一點。看我的完整課程?謝謝, – ant2009

+0

我增加了想法。請參閱我的編輯塊 – xyz

+0

感謝您的更新。但是,嘲笑API已經完成。當我爲getAllRecipes注入模擬。但是,我只是在詢問有關subscription.unsubscribe()的驗證。只是想知道是否有這樣做。這就是爲什麼我發佈了我所有的代碼,以便能夠看到更大的圖片。我只是想知道替代解決方案,而不是getDeclaredField(「...」)方法。 – ant2009