Пользователь
0,0
рейтинг
28 октября 2011 в 18:56

Разработка → Шаблоны проектирования при разработке под Android. Часть 2 — MVP и Unit tests. Путь Джедая

По началу я хотел только кратко рассказать что такое MVP, но кратко не получилось. Поэтому я выделил этот кусок в отдельную статью, которая мало относится к Android, но очень важна для понимания MVP и модульных тестов. Обещанные же статьи никуда не денутся.

Все разработчики знают что такое модульные тесты но мало кто может их использовать так, чтобы их применение сокращало время разработки программы, а не увеличивало. В их глазах те, кто с помощью тестов может делать задачи быстрее да еще и качественнее, выглядят как джедаи, которые могут с мечем в руках с закрытыми глазами покрошить в капусту роту автоматчиков.

Я попробую поставить вас на путь джедая и научить вас с закрытыми глазами крошить в капусту кучи багов с помощью модульных тестов и других технологий.


Что такое Model View Presenter (MVP)


Издалека расскажу что такое MVP зачем он нужен и какая с него польза.
В любом проекте наступает такой момент, когда разработчики хотят добавить модульных тестов, чтобы перед выпуском промежуточной версии убедиться, что последние изменения не сломали все что было сделано раньше.

Вроде понятно что должен делать тест: надо проверить, что после того как пользователь увидел форму, заполнил поля правильными данными и нажал «Сохранить», в обработчике этой кнопки, данные с формы проверились и попали в базу данных, или выдалось бы сообщение об ошибке пользователю.
Все примеры, которые есть в документациях для разных языков построены именно по этому простому принципу. Поэтому это весьма распространенное проектное решение.

Но тут появляется первая проблема, поскольку логика по проверке правильности полей и сохранению их в БД лежит в обработчике кнопки «Сохранить». Как в тесте проверить нажатие кнопки «Сохранить» на форме? Ведь в тесте нельзя создать экземпляр класса «форма», чаще всего потому что у этого класса нет публичного конструктора, другими словами форма создается специальными классами, которых в тестах нет, и которые, в свою очередь, тоже нельзя создать.

MVP позволяет красиво решить упомянутую проблему. Суть в том, что надо перенести весь код из обработчика кнопки сохранить в другой класс, который можно будет легко создавать. Этот класс называет presenter. А в форме, которая теперь называется View или представление, в обработчике кнопки «сохранить» останется только одна строчка, которая вызовет одноименный метод в presenter-е.
Обычно presenter создается в конструкторе представления и presenter-у передается ссылка на представление.

Далее в тесте мы создаем экземпляр presenter-а и имитируем вызов метода «сохранить» у presenter-а. Дальше мы ожидаем, что presenter заберет из представления значения полей формы, проверит их и передаст их в БД. Получается что presenter-у в тесте все равно нужна форма, да и еще БД в виде какого-то класса для работы с БД.

Форму и класс для работы с БД мы в тесте создать не можем, но можем сделать следующий финт ушами: пусть presenter, при создании, получает ссылки не на представление и класс для работы с БД, на их интерфейсы. То есть надо создать интерфейсы, один для представления, а другой для БД. При обычной работе под этими интерфейсами будут скрываться настоящие предстваление и БД, а в тестах мы сделаем классы заглушки, которые тоже будут поддерживать эти интерфейсы. В тестах мы создадим заглушку для представления, которая будет возвращать фиксированные значения для полей формы. А заглушка для БД будет просто сохранять те значения, которые ей пришли, чтобы потом мы могли проверить эти значения.

И теперь в тесте:
1. Создаем класс заглушку для представления и прописываем какие значения полей она будет возвращать.
2. Создаем класс заглушку для БД, который просто сохранит значения переданные ему при вызове метода «SaveToDB»
3. Создаем presenter и передаем ему созданные заглушки.
4. Вызываем у presenter метод «сохранить»
5. проверяем что в заглушке БД появились нужные значения, иначе в presenter-е ошибка.

Итак, зачем нужен MVP и какая от него польза.
MVP это шаблон проектирования, который позволяет отделить код с бизнес-логикой по обработке данных от кода с отображением данных на форме.
Бизнес-логика в presenter-е тестируется при каждом выпуске новой версии. Что позволяет говорить, что нет сложных ошибок в программе.
Код в представлении остается не протестирован, но эти ошибки легко обнаруживаются еще разработчиками, когда они видят, что кнопка «Сохранить» ничего не делает, и легко исправляются, поскольку сразу понятно, что в обработчике кнопки забыли вызвать метод presenter-а.

Внимательный читатель на этом месте воскликнет «Да вы охренели! Раньше у меня был один класс который умещался в одном файле и в нем было удобно все отлаживать, а теперь мне надо добавить класс presenter, интерфейсы, две заглушки да и еще сам тест писать надо! Это займет у меня в два раза больше времени. Если начальство прикажет, то я, так и быть сделаю пару тестов, после того как напишу код, но я снимаю с себя ответственность за срыв сроков»

«Успокойся, о юный падаван»


Да, любой паттерн требует написания дополнительных строк кода, надо просто понять как извлечь из него выгоду.

В основном разработчики не используют модульные тесты, потому что им кажется, что это только трата дополнительного времени на написание теста, а пользу с этого получают тестеры, которым меньше тестировать. Так что экономия времени в проекте может получиться только за счет того что тестерам меньше работать и эта экономия времени не может быть больше затрат времени на написание тестов.

Так вот, таким образом модульные тесты использовать нельзя. Их надо использоваться так, чтобы они экономили время разработчику.

Дальше я расскажу как надо организовать процесс разработки с использованием тестов, так чтобы это приносило пользу именно разработчику.

В качестве примера возьмем ту же форму, где надо ввести данные, нажать кнопку «Сохранить», проверить данные, если есть ошибки, то показать их пользователю, если нет, то отправить их в БД.

Создание представления и заготовки для presenter-а


Первым делом надо создать форму (представление), в случае Android это наследник класса Activity. Надо поместить на нее контролы для ввода значений, вывода сообщения об ошибке и кнопку «Сохранить» с обработчиком.

Дальше надо создать интерфейс для представления. Интерфейс нужен чтобы presenter мог выводить данные на форму и забирать данные из формы, поэтому в интерфейсе должны быть методы вроде setName(string name) и string getName(). Естественно, что представление должно реализовывать этот интерфейс и в методах должно просто перекладывать значение из аргумента в свойство Text используемого контрола или обратно в случае метода getName.

Дальше надо создать presenter, который на вход получает ссылку на экземпляр интерфейса. Пока что в конструкторе presenter-а просто проставим произвольные значения на форме, чтобы проверить что presenter имеет полный доступ к форме.
Так же создадим метод «сохранить», в котором в локальные переменные получим текущие значения из полей представления и поставим в него Break Point, чтобы убедиться, что этот метод вызывается из представления.

В конструкторе представления создадим экземплят presenter-а и передадим ему ссылку на это представление. В представлении в методе обработчике кнопки «Сохранить» просто проставим вызов соответствующего метода в presenter-е.

Вот пример представления
public class View extends Activity implements IView
{
	Presenter presenter;
	public View(Bundle savedInstanceState)
	{
		presenter = new Presenter(this);
	}

	public void setName(String name)
	{
		tvName.setText(name);
	}

	public void getName()
	{
		return tvName.getText();
	}

	public void onSave(View v)
	{
		presenter.onSave();
	}
}

Вот пример presenter-а

public class presenter
{
	IView view;
	public presenter(IView view)
	{
		this.view = view;
		view.setName("example name");
	}

	public onSave()
	{
		string name = view.getName();
	}
}

Код для первого этапа готов.
Надо запустить программу в отладчике и убедиться что при открытии формы отображается правильное название а если ввести новое название и нажать «Сохранить», то presenter получит правильное значение.

На первом этапе нет никаких модульных тестов и особой экономии времени тоже нет, но и затраты не велики.

«Закрой глаза, о юный падаван»


Вот теперь начинается самое интересное. Дело в том, что при дальнейшем написании кода нам не надо запускать приложение, что бы смотреть как работает форма. Весь код мы будет развивать и отлаживать в тестах. Мы будем писать код как бы с «закрытыми глазами», как настоящие джедаи.

Секрет в том, что надо дописывать presenter маленькими кусочками и для проверки этого кусочка дописывать кусочек теста.

Наполним presenter функционалом. при создании он должен прочитать название из БД и передать его в представление. А при сохранении получить название из предстваления и сохранить его в БД.

public class presenter
{
	IView view;
	IDataBase dataBase;
	int Id;
	public presenter(IView view, IDataBase dataBase)
	{
		this.view = view;
		this.dataBase = dataDase;
		id = view.getId();
		string name = dataBase.loadFromDB(id);
		view.setName(name);
	}

	public onSave()
	{
		string name = view.getName();
		dataBase.saveToDB(id, name);
	}
}

Понятно, что при этом надо добавить в интерфейс и в представление новый метод для получения ID, но пока что мы даже не будем «открывать глаза», то есть запускать приложение, чтобы проверить представление. Это сделаем потом, и если будет ошибка, мы ее в секунду исправим.

Итак мы написали кусочек представления и теперь напишем кусочек теста.

Те заглушки, про которые я писал в первой части, необязательно создавать в виде классов, которые поддерживают интерфейс. Если различные Mock Framework-и, которые позволяют за три строчки создать создать экземпляр класса, который поддерживает заданный интерфейс и даже возвращает нужные значения при вызове его методов. Эти заглушки называются имитациями (перевод слова Mock), потому что они гораздо навороченне чем просто заглушки.

Пример метода теста

public void testOpenAndSave()
{
	IView view = createMock(IView.class);	//создаем имитацию для представления
	IDataBase dataBase = CreateMock(IDataBase.class);//создаем имитацию для БД
	
	expect(view.getId()).andReturn(1); //Когда presenter спросит что прочитать из базы вернем id 1
	expect(dataBase.LoadFromDB(1)).andReturn("source name"); //Когда presenter попытается загрузить название, вернем "source name"
	expect(view.setName("source name")); //ожидаем что правильная строка попадает на форму
	
	expect(view.getName()).andReturn("destination name"); //Когда presenter  при сохранении спросит, что ввел пользователь, вернем "destination name".
	expect(dataBase.SaveToDB(1, "destination name")); //Ожидаем, что в БД уйдет именно это название.
	
	Presenter presenter = new Presenter(view, dataBase); //Передаем в presenter имитации для представления и базы данных
	presenter.Save();//Имитируем нажатие кнопки "Сохранить";

	//проверим, что все нужные методы были вызваны с нужными аргументами
	verify(view);
	verify(dataBase);
}

Вот только не надо стонать, что кода в тесте столько же сколько и в presenter-е. Оно того стоит.

Запускаем тест и видим что все хорошо, или делаем так чтобы все стало хорошо.

При таком процессе разработки тест не является чем-то чужеродным по отношению к коду. Тест это часть кода, которую должен выдать разработчик при реализации задачи. Тест нужен, чтобы убедиться, что написанный код работает правильно.

Продумывая код в presenter-е надо сразу продумывать код в тесте, это инь и янь готовой задачи, неотделимые друг от друга части.

А вот и прибыль


А теперь я покажу где же прибыль в виде сокращения времени разработки и повышения качества.

Прибыль появляется пр дальнейшей разработке presenter-а.

Дальше надо добавить проверку данных с формы в метод «сохаранить». А в тесте надо проверить что правильные значения сохраняются в БД, а на неправильные выводится сообщение об ошибке.

Для этого добавляем необходимый код в presenter, чтобы проверить правильность названия и если оно неправильное, то вызываем метод setErrorText(), и не вызываем сохранение в БД.

Дальше просто копируем тест и правим имитацию для представления. Она теперь должна возвращать неправильное название при вызове getName() и ожидать вызов метода setErrorText() с правильным аргументом, таким образом будет проверено что и сообщение об ошибке правильное. А имитация для БД должна проверять что метод SaveToDB не был ни разу вызван, точнее вызван 0 раз.

Запускаем все тесты видим, новый тест работает нормально. А вот прошлый не проходит. Бъём себя по лбу за глупую ошибку и быстро исправляем ее.
И теперь оба теста работают.

Если бы не наш первый тест, то ошибку с сохранением правильных строк нашли бы тестеры и в результате время на ее исправление было бы в несколько раз больше чем с использованием модульных тестов. Надо было бы опять открывать нужное место в коде и вспоминать как работает алгоритм и вносить исправления. А потом, поскольку тестов нет, самому проверять все остальные тестовые случаи, чтобы убедиться что исправление этой ошибки не привело к другой.

Когда алгоритмы сложные и приходится проверять кучу тестовых случаев, вот тогда экономя времени становится гораздо заметнее. Потому что без тестов надо еще и потратить время на запуск всего приложения, потом пройти несколько окон, чтобы добраться до своего, потом воспроизвести тестовый случай, а это может требовать многих усилий.

Но у нас есть тесты. Поэтому мы можем легко имитировать различные тестовые случаи и пачками исправлять баги, потому, что все прошлые тестовые случаи тоже будут проверены.

В результате у нас получился качественный код, а время сэкономлено за счет того что не придется отвлекать на ошибки, которые нашли тестеры, а потом еще и самому долго проверять, что все остальные тестовые случаи не сломались после исправления ошибки.

И вот, через несколько дней, когда весь presenter готов и оттестирован для всех случаев, мы запускаем приложение и с удивлением видим, что оно работает сразу правильно для всех тестовых случаем. И чувствуем себя настоящим джедаем.

Джедаем стать не просто.


Да. Настоящий джедай должен знать еще много приемов написания тестов, чтобы поддержка тестов, при изменении требований, не сожрала всю ту выгоду, что получилась при первоначальной разработке.

На самом деле копировать тесты это так же плохо как и копировать код в приложении. Надо делать тест, который работает со структурой данных описывающей тестовый случай. Эта структура данных содержит входные данные и ожидаемые выходные данные или ожидаемые ошибки.
в таком случае новый тест это вызов одного и того же метода просто с другой структурой данных. А тест должен развиваться так, чтобы он мог обработать все структуры.

Полагаю в интернете можно найти много таких приемчиков которые позволят вам стать настоящим мастер-джедаем.

Главное что вы уже встали на путь джедая и знаете как по нему идти :)

Читайте в других статьях


Введение
— MVP и Unit tests. Путь Джедая
Пользовательский интерфейс, тестирование, AndroidMock
Сохранение данных. Domain Model, Repository, Singleton и BDD
— Реализация серверной части, RoboGuice, тестирование
— Небольшие задачи, настройки, логирование, ProGuard
Andrey @AndreyFedorov
карма
16,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (25)

  • +4
    Чем вам не угодила архитектура которую предлагает гугл?
    • –2
      Похоже, я еще не очень хорошо изучил Android, поскольку не знаю что за архитектуру предлагает гугл.
      Расскажи подробнее.
      • 0
        Он и так предлагает полный MVP. View это xml файлы. Activity это Presenter. Модель это контент провайдеры. Не нужно изобретать велосипеды.
        • 0
          То что вы описали это не полный MVP.
          В такой ситуации у View или Presenter-а нет интерфейса, который можно симитировать.
          А контент провайдер не является моделью, потому что туда нельзя добавить методы бизнес-логики, которые обычно и живут в модели.
          Контент провайдер можно рассматривать как репозиторий, который достает модель из БД.

          Поэтому я и затеял этот цикл статей, чтобы показать как существующие в Android компоненты увязать в MVP.
          • +2
            Если вы не забыли android это мобильная ОС и ресурсы тут ограничены. Так что не надо плодить интерфейсы.
            • –1
              Не согласен с тем, что не надо плодить лишние интерфейсы.
              Как показывает практика, рост размеров проекта и замедление работы приложения, при использовании MVP, на глаз заметить не возможно.
              А вот скорость разработки и качество очень даже заметны.
              • 0
                это верно, для обычных приложениях, а не на мобильных платформах
                • –1
                  а цифры есть?
                • –1
                  У меня есть мобильное приложение, сделанное с использованием MVP.
                  Если отбросить 550кб звукового файла, то ее размер примерно 250 кб.
                  Без использования MVP и вспомогательных технологий, полагаю ее размер был бы 50 кб.

                  Итого.
                  1. Увеличение объема на 200 кб из-за дополнительных библиотек (а не в 5 раз, как может показаться)
                  2. Приложение работает на эмуляторе и на моем Galaxy S работает быстро, во всяком случае я быстрее могу настроить необходимые параметры, чем в других аналогичных программах. Расходов по скорости нет.

                  То есть расходы 200 Кб.

                  Зато доходы:
                  1. Качественная разработка. Для написания движка я написал порядка 30 тестов, и при отладке часто через тесты обнаруживал, что сломал предыдущую часть алгоритма.
                  2. Без тестов у меня бы ушло примерно в два-три раза больше времени на полную отладку алгоритма, а ошибки, при этом, находили бы пользователи, что очень плохо.

                  То есть доходы примерно двоекратное сокращение времени разработки и репутация качественного софта.

                  Для меня доходы многократно превышают расходы.
                  • +3
                    Тесты это хорошо. Но паттерны вы по моему мнению используете не по назначению.

                    Если я не ошибаюсь, ваше приложение это будильник. А теперь представьте, что вам нужно написать приложении с интерфейсом посложнее, содержащее 5+ активити с не самой простой логикой перехода между ними. В этом случае, исходя из ваших двух статей, вам придется городить пакеты под каждую активити, писать презентеры и интерфейсы. Количество классов быстро станет over 9000. И работать такой энтерпрайз монстр станет гораздо медленне.

                    Вам следует поглубже изучить платформу. Инженеры в гугле очень хорошие. Они разрабатывали андроид, следуя многим общеизвестным паттернам. Вот только применяли их по назначению. Паттернов андроида лучше придерживаться и в своих приложениях, не стоит конструировать паятиколесный велосипед.

                    Для тестов UI есть несколько хороших фреймворков, посмотрите в сторону Robotium и Robolectric. Также мне советовали вот эту книгу.

                    P.S. Окончательный размер приложения зависит больше от его ресурсов, чем от лишних классов. Без использования MVP и вспомогательных технологий размер вашего приложения стал бы ненамного меньше.
                    • 0
                      Даже если это большое приложение, то в проект приложения на одну активность надо добавить 1 интерфейс для View, 1 presenter, 1 интерфейс для модели и 1 класс модели.
                      Конечно файлов 5 файлов больше чем один, но кода в этих файлах больше всего лишь на 5-10%

                      Это не огромное кол-во файлов, с ним можно нормально работать. Поскольку эти файлы относятся к одной активности и находятся в одном пакете, нет никаких проблем с поиском нужного файла.

                      Еще раз скажу, что на скорости работы это заметно не скажется.

                      Зато в большом приложении гораздо важнее возможность разрабатывать части приложения, когда еще не готовы другие части, которые необходимы для разработки первой части.
                      Необходимые части можно заменить в тестах имитациями и отладить полностью свою часть.
                      Для менеджера проекта это очень важное свойство MVP, потому что менеджер получает гораздо большее пространство для маневра при распределении ресурсов по задачам.
                      И в конце концов это позволяет сократить срок разработки проекта.

                      Поэтому в большом проекте использование MVP наоборот принесет еще больше пользы.

                      Спасибо за ссылки на тестовые framework-и, я их обязательно посмотрю.
                      Но польза от тестирования UI принебрежима мала по сравнению с возможностью тестировать части приложения изолированно от других, а это можно делать и через встроенный в Eclipse JUnit.

                      Если тестировать приложение через UI, то нужно уже готовое приложение. Получается нельзя отладить код движка, пока не готов UI для него. Так же нельзя поручать разработку движка и UI разным людям, потому что пока не готов UI разработчик движка даже не может приступить к работе, потому что не сможет запустить свой движок.

                      Использование MVP решает все эти проблемы.

                      Скорость разработки и качество возрастают гораздо значительнее чем увеличивается размер программы.

                      P.S.
                      Не согласен что отказ от MVP сделал бы мое приложение намного меньше. В мое приложение добавилось 200кб библиотек для RoboGuice. Но в остальном MVP требует только 5-10% увеличение кода что не является критичным для работы программы.
                      • 0
                        RoboJuice — фреймворк, позволяющий использовать аннотации и dependency injection? В чем было его преимущество для вашего проекта? Я не работал с этим фреймворком, но похоже что внутри него лежит рефлекшн, а это довольно затратный механизм.

                        Если вы будете создавать 1 пакет и 5 классов на каждую активити, поверьте, ваш проект станет очень громоздким. Подобные методы проектирования, по моему, больше подходят для энтерпрайза.

                        Ничто не мешает вам тестировать UI отдельно от логики. Тестируйте UI специализированными фреймворками либо встроенными в сдк классами, как это описано здесь, а для логики пишите unit testы.
                        • 0
                          Да, основная задача RoboGuice это Dependency Injection.
                          Так же я его использую для инициализации экземпляров классов.
                          Он действительно использует рефлексию.
                          Вот только это нельзя назвать «довольно затратным». Видимых просадок производительности нет. Лишняя 0.1 секунды при старте активности это не затратно.

                          Никак не могу понять почему приложение станет громоздким. Если у меня 20 активностей, которые сосредоточены в 20 пакетах, это разве громоздко? Какой критерий при определении громоздкости вы используете?

                          Как мне в тесте отделить UI от логики? Я знаю как это можно сделать через Dependency Injection. Расскажите другой способ.

                          И вообще, расскажите мне, какую выгоду я получу, если откажусь от MVP и буду использовать один класс на одну активность? Дайте оценку на сколько быстрее будет работать мое приложение. Укажите в чем будет выражаться то, что такое приложение менее громоздко.

                          Тестировать UI вместе с логикой — плохо, потому что нельзя разрабатывать логику и UI по одтельности.

                          Ктому же встроенный в SDK JUnit Test Framework обладает существенным недостаткам: он не поддерживает изоляцию тестов. То есть первый тест поднимает процесс приложения и тестирует первую активность. Второй тест берет приложение в том состоянии, которое осталось после первого теста и тестирует вторую активность. Если у вас есть в приложении многопоточноть то вы будете полуать завершение потоков из перой активности во время тестирования второй. Так же некторые события UI задерживаются и приходят во вторую активность.

                          Поэтому я отказался от тестирования UI встроенным JUnit-ом.
                          • 0
                            Разделять UI и логику, а также использовать Dependency Injection можно ведь не только слепо следуя MVP и создавая интерфейс на каждый класс. Повторюсь, я считаю затратным для мобильной платформы использование рефлекшна и большого количаства классов прослоек. Естественно, точных цифр я вам не приведу.

                            Возможно мне стоит попробовать ваш метод, но мне все же удобнее хранить классы активити в одном пакете .activity. Это ведь лишь малая часть всего приложения. И если она разрастется на двадцать пакетов, лично мне это немного затруднит ориентацию по проекту.

                            Почему нельзя вынести логику в отдельные классы, не зависящие от интерфейса и тестировать их юнит тестами?

                            • 0
                              Согласен, что при проектировании можно расположить UI и логику в разных классах и передавать имитации в тестируемый класс логики через конструктор или еще как-то.

                              Но с использованием RoboGuice все это делается за меньшее количество строк кода. Самый яркий пример, мне не надо передавать контекст во все мои классы с логикой, я его получаю через RoboGuice.
                              согласен, что из-за этого мое приложение увеличиться на 200кб и будет на долю секунды медленнее работать. Но я считаю что я получаю больше в виде сокращения объемов моего кода и повышения его читаемости.

                              Кстати, а как вы в своих тестах имитируете окружение при тестах классов с логикой?

                              По поводу хранения классов, полагаю стоит делать пакет Activity, в котором располагать пакеты для каждой активности. Таки образом не будет проблем с ориентацией.

                              Признаю, что я был не прав когда говорил, что нельзя тестировать логику без UI. Но способы имитации окружения, вроде передачи имитаций через конструктор выглядят для меня очень неудобными. Слишком много нужно дополнительного кода, по сравнению с использованием Dependency Injection
                              • 0
                                Ну я как бы стараюсь не передавать контекст в классы логики. Пытаюсь делать их изолированными от UI.
      • +1
        Не очень хорошо изучить Android и написать статью о разработке под Android — это путь на тёмную сторону силы, о юный джедай :)
  • +3
    Джедай тегом source пользоваться должен.
    • 0
      Спасибо тебе, добрый человек. Теперь поправил.
  • 0
    А что с Mock Frameworkами? Или вы тестируете не андроид java-машине?
    • +1
      Для тестов с помощью android-junit проекта есть Android-Mock — порт Easymock'а.
    • 0
      Я использую AndroidMock, который, как заметил helions8, является подгонкой EasyMock под андроид.
      В результате я тестирую приложения в Dalvik на эмуляторе, или же на своем смартфоне.
      • 0
        А другие не пробовали? Вроде PowerMock?
        • 0
          PowerMock я не пробовал использовать.
          Но думаю попробовать в следующем проекте, потому что у AndroidMock есть несколько серьезных недостатков, да и не развивается он.
    • 0
      Я использую AndroidMock, который, как заметил helions8, является подгонкой EasyMock под андроид.
      В результате я тестирую приложения в Dalvik на эмуляторе, или же на своем смартфоне.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.