2017-07-02 50 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