После вдохновения от ответа EpicPandaForce и некоторых исследований (см. эту статью ), я нашел решение, которым я доволен.
Я решилудалите Dagger 2 из моего проекта, потому что я слишком сильно его проектировал.Мое приложение опирается на класс репозитория и теперь реализацию ViewModelProvider.Factory
, которые необходимы сразу после запуска приложения.Я узнал достаточно о Dagger для собственного удовольствия, поэтому я чувствую себя комфортно, оставив его вне этого конкретного проекта и создав две зависимости в классе Application
.Эти классы выглядят так:
Класс My Application, который создает мою фабрику ViewModel
, предоставляет ей свой репозиторий и предоставляет метод getViewModelFactory()
для моей деятельности:
public class JourneyStoreApplication extends Application {
private final JourneyStoreViewModelFactory journeyStoreViewModelFactory;
{
// Instantiate my viewmodel factory with my repo here
final JourneyRepository journeyRepository = new JourneyRepositoryImpl();
journeyStoreViewModelFactory = new JourneyStoreViewModelFactory(journeyRepository);
}
@Override
public void onCreate() {
super.onCreate();
}
public JourneyStoreViewModelFactory getViewModelFactory(){
return journeyStoreViewModelFactory;
}
}
MyViewModel
Фабрика, которая создает новые ViewModel
с ссылкой на репозиторий.Я буду расширять его по мере добавления дополнительных классов Activity
и ViewModel
s:
public class JourneyStoreViewModelFactory implements ViewModelProvider.Factory {
private final JourneyRepository journeyRepository;
JourneyStoreViewModelFactory(JourneyRepository journeyRepository){
this.journeyRepository = journeyRepository;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if(modelClass == AddJourneyViewModel.class){
// Instantiates the ViewModels with their repository reference.
return (T) new AddJourneyViewModelImpl(journeyRepository);
}
throw new IllegalArgumentException(String.format("Requested class %s did not match expected class %s.", modelClass, AddJourneyViewModel.class));
}
}
My AddJourneyActivity
, которые используют AddJourneyViewModel
:
public class AddJourneyActivity extends AppCompatActivity {
private static final String TAG = AddJourneyActivity.class.getSimpleName();
private AddJourneyViewModel addJourneyViewModel;
private EditText departureTextField;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_journey);
JourneyStoreApplication app = (JourneyStoreApplication) getApplication();
addJourneyViewModel = ViewModelProviders
// Gets the ViewModelFactory instance and creates the ViewModel.
.of(this, app.getViewModelFactory())
.get(AddJourneyViewModel.class);
departureTextField = findViewById(R.id.addjourney_departure_addr_txt);
}
//...
}
Но это все еще оставляет вопрос тестирования, который был одной из моих главных проблем.Примечание: я сделал все свои классы ViewModel
абстрактными (только с помощью методов), а затем реализовал их для своего реального приложения и тестового кода.Это потому, что мне проще, чем extend
напрямую вводить ViewModel
, а затем пытаться переопределить их методы и скрывать их состояние для создания поддельной версии.
В любом случае я расширил свой класс JourneyStoreApplication
(противоречит самому себе, я знаю, но это небольшой класс, так что им легко управлять), и использовал его, чтобы создать место, чтобы предоставить мои поддельные ViewModel
s:
public class FakeJourneyStoreApplication extends JourneyStoreApplication {
private final JourneyStoreViewModelFactory fakeJourneyStoreViewModelFactory;
{ // Create my fake instances here for my tests
final JourneyRepository fakeJourneyRepository = new FakeJourneyRepositoryImpl();
fakeJourneyStoreViewModelFactory = new FakeJourneyStoreViewModelFactory(fakeJourneyRepository);
}
@Override
public void onCreate() {
super.onCreate();
}
public JourneyStoreViewModelFactory getViewModelFactory(){
return fakeJourneyStoreViewModelFactory;
}
}
Я сделал поддельные реализации моего ViewModel
ы и вернули их экземпляры из FakeJourneyStoreViewModelFactory
.Я мог бы упростить это позже, поскольку, вероятно, больше «поддельного» шаблона, чем нужно.
Уходя этого руководства (раздел 4.9), я расширил AndroidJUnitRunner
, чтобы предоставить fake Application
к моим тестам:
public class CustomTestRunner extends AndroidJUnitRunner {
@Override
public Application newApplication(ClassLoader cl, String className, Context context)
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return super.newApplication(cl, FakeJourneyStoreApplication.class.getName(), context);
}
}
И, наконец, я добавил пользовательский бегун тестов в свой файл build.gradle
:
android {
defaultConfig {
// Espresso
testInstrumentationRunner "com.<my_package>.journeystore.CustomTestRunner"
}
}
Я собираюсьчтобы оставить этот вопрос открытым еще на 24 часа, если у кого-то есть что-то полезное, я выберу его в качестве ответа.