将,我有一个包含MainFragment的MainActivity, MainFragment实现了RecipeDataFetcher回调接口,该接口通过RecipeDataFetcher Class中的Volley / network获取数据。 MainFragment有一个Recyclerview,可以显示几张食谱卡
- 我需要在RecyclerView项目上执行测试
这是我的主要片段`公共类
MainFragment extends Fragment implements RecipesRecyclerAdapter.OnRecipeItemSelected,
RecipesDataFetcher.RecipesFetcherDataListener {
private View v;
private RecipesDataFetcher recipesDataFetcher;
private OnFragmentInteractionListener mListener;
private RecyclerView recyclerView;
private RecipesRecyclerAdapter recipesRecyclerAdapter;
private Recipes mRecipes;
private RecyclerView.LayoutManager layoutManager;
private SimpleIdlingResource mIdlingResource;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v = inflater.inflate(R.layout.fragment_main, container, false);
mRecipes = new Recipes();
recyclerView = (RecyclerView) v.findViewById(R.id.rv_recipes);
if (mTwoPanel) {
layoutManager = new GridLayoutManager(getActivity(), 2);
} else {
layoutManager = new LinearLayoutManager(getActivity());
}
recipesRecyclerAdapter = new RecipesRecyclerAdapter(this, mRecipes);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(recipesRecyclerAdapter);
return v;
}
@VisibleForTesting
@NonNull
public SimpleIdlingResource getIdlingResource() {
if (mIdlingResource == null) {
mIdlingResource = new SimpleIdlingResource();
}
return mIdlingResource;
}
@Override
public void onSaveInstanceState(Bundle outState) {
// super.onSaveInstanceState(outState);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onItemSelected(int itemPosition) {
mListener.onFragmentInteraction(mRecipes.get(itemPosition), mRecipes.size());
}
@Override
public void onResume() {
super.onResume();
if (mRecipes.isEmpty()) {
getData();
}
}
@Override
public void onConnectionFailure() {
if (!checkOnlineState(getActivity())) {
Logging.shortToast(getActivity(), getString(R.string.internet_error));
} else {
Logging.shortToast(getActivity(), getString(R.string.server_error));
}
}
private void getData() {
recipesDataFetcher = new RecipesDataFetcher(getActivity(), this);
recipesDataFetcher.getRecipes(mIdlingResource);
}
@Override
public void onConnectionDone(Recipes recipes) {
mRecipes.addAll(recipes);
recipesRecyclerAdapter.notifyDataSetChanged();
//TODO: Do bulk inset in a background thread and for once
ContentValues[] contentValues = new ContentValues[recipes.size()];
for (int i = 0; i < recipes.size(); i++) {
ContentValues contentValue = new ContentValues();
Gson gson = new Gson();
String ingredients = gson.toJson(recipes.get(i).getIngredients());
String steps = gson.toJson(recipes.get(i).getSteps());
contentValue.put(COLUMN_RECIPE_INGREDIENTS, ingredients);
contentValue.put(COLUMN_RECIPE_STEPS, steps);
contentValue.put(COLUMN_RECIPE_SERVINGS, recipes.get(i).getServings());
contentValue.put(COLUMN_RECIPE_IMAGE, recipes.get(i).getImage());
contentValue.put(COLUMN_RECIPE_NAME, recipes.get(i).getName());
contentValues[i] = contentValue;
}
getContext().getContentResolver().bulkInsert(CONTENT_URI, contentValues);
if(mIdlingResource!=null){
mIdlingResource.setIdleState(true);
}
}
public interface OnFragmentInteractionListener {
void onFragmentInteraction(Recipe recipe, int size);
}`
这是我的dataFetching类
public class RecipesDataFetcher extends BaseDataFetcher {
public RecipesDataFetcher(Context context, BaseDataFetcherListener mListener) {
super(context, mListener);
}
public void getRecipes(SimpleIdlingResource simpleIdlingResource) {
if(simpleIdlingResource!=null){
simpleIdlingResource.setIdleState(false);}
String URL = BaseURL;
Logging.log("getCountries: " + URL);
JsonArrayRequest jsonObjReq = new JsonArrayRequest(URL, (JSONArray jsonArr) -> {
Logging.log("getCountries response: " + jsonArr.toString());
Recipes recipes = new Recipes(jsonArr);
((RecipesFetcherDataListener) mListener).onConnectionDone(recipes);
if(simpleIdlingResource!=null){
simpleIdlingResource.setIdleState(true);
}
}, this.errorListener);
retryPolicy(jsonObjReq);
getReQ().add(jsonObjReq);
}
public interface RecipesFetcherDataListener extends BaseDataFetcherListener {
void onConnectionDone(Recipes recipes);
}
}
这是我的测试类
@RunWith(AndroidJUnit4.class)
public class MyFragmentTest {
private SimpleIdlingResource mIdlingResource;
@Rule
public FragmentTestRule<MainFragment> mFragmentTestRule = new FragmentTestRule<>(MainFragment.class);
@Before
public void registerIdlingResource() {
// Launch the activity to make the fragment visible
mFragmentTestRule.launchActivity(null);
mIdlingResource = mFragmentTestRule.getFragment().getIdlingResource();
Espresso.registerIdlingResources(mIdlingResource);
}
@Test
public void fragment_can_be_instantiated() {
// Then to test item in position 0
onView(withId(R.id.rv_recipes))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
}
// unregister resources
@After
public void unregisterIdlingResource() {
if (mIdlingResource != null) {
Espresso.unregisterIdlingResources(mIdlingResource);
}
}
}
最后here是整个代码项目,请让我知道我到底做错了什么:D,我的帮助将受到高度赞赏。
答案 0 :(得分:1)
错误说明
在您的存储库中浏览并运行 MyFragmentTest 测试类
我遇到了两个错误
这是因为在 FragmentTestRule 类中,您扩展了ActivityTestRule&lt; TestActivity &gt;其中 TestActivity 没有实现OnFragmentInteractionListener所以解决这个问题的方法是你必须扩展已经实现了这个接口的 MainActivity ,所以这个类应该是这样的:
public class FragmentTestRule<F extends Fragment> extends ActivityTestRule<MainActivity> {
private final Class<F> mFragmentClass;
private F mFragment;
public FragmentTestRule(final Class<F> fragmentClass) {
super(MainActivity.class, true, false);
mFragmentClass = fragmentClass;
}
.....
这是因为您的 MyFragmentTest 在任何开始之前调用注释 @Before 导致你的片段尚未创建,使得这条线没有意义
mIdlingResource = mFragmentTestRule.getFragment().getIdlingResource();
所以在我看来: 不要使用碎片。活动更容易测试。您可以自己测试每个活动。在大多数情况下,碎片没有优于活动的优势。片段只会使实现和测试变得更加困难。
使用排球运行空转资源的替代解决方案
(a)在 MainActivity.java
中添加这些行
@Nullable
public SimpleIdlingResource mIdlingResource;
@VisibleForTesting
@NonNull
public SimpleIdlingResource getIdlingResource() {
if (mIdlingResource == null) {
mIdlingResource = new SimpleIdlingResource();
}
return mIdlingResource;
}
并在 onCreate 中调用此方法 getIdlingResource(); 从idlingResouce获取实例。
(b)在MainFragment.java
中(c)最后这是新的 MainActivityTest
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> mActivityTestRule =
new ActivityTestRule<>(MainActivity.class);
private IdlingResource mIdlingResource;
// Registers any resource that needs to be synchronized with Espresso before the test is run.
@Before
public void registerIdlingResource() {
mIdlingResource = mActivityTestRule.getActivity().getIdlingResource();
// To prove that the test fails, omit this call:
Espresso.registerIdlingResources(mIdlingResource);
}
@Test
public void idlingResourceTest() {
onView(withId(R.id.rv_recipes)).check(matches(isDisplayed()));
onView(withId(R.id.rv_recipes))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
}
// Remember to unregister resources when not needed to avoid malfunction.
@After
public void unregisterIdlingResource() {
if (mIdlingResource != null) {
Espresso.unregisterIdlingResources(mIdlingResource);
}
}
}
很抱歉,希望我帮忙:) :)