Не так давно я закончил первую статью из цикла автоматизации мобильных приложений [http://automated-testing.info/t/nastrojka-sredy-dlya-razrabotki-android-prilozhenij-i-avtomatizaczii-na-robotium/2421][1]. Я попытался максимально полно дать информацию по установке требуемого окружения, для разработки и автоматизации тестирования NativeAndroid приложений, при помощи инструмента Robotium.
import com.example.android.notepad.NotesList;
import com.jayway.android.robotium.solo.Solo;
/**
- Add note test
- Example of simple Robotium test
- @author adzynia
*/
public class AddNoteTest extends ActivityInstrumentationTestCase2{
private static final String APP_PACKAGE_NAME = “com.example.android.notepad”;
protected Solo solo;
public AddNoteTest() {
super(APP_PACKAGE_NAME, NotesList.class);
}
public void setUp() throws Exception {
solo = new Solo(getInstrumentation(), getActivity());
}
public void testAddNote() throws Exception {
solo.clickOnMenuItem(“Add note”);
//Assert that NoteEditor activity is opened
solo.assertCurrentActivity(“Expected NoteEditor activity”, “NoteEditor”);
//In text field 0, add TestNote
String note = “TestNote”;
solo.enterText(0, note);
solo.goBack();
//Assert that TestNote is found
assertTrue(“Note 1 is not found”, solo.searchText(note));
}
@Override
public void tearDown() throws Exception {
//Robotium will finish all the activities that have been opened
solo.finishOpenedActivities();
}
}
<p>Ничего не понятно (или мало что понятно), скажете Вы и будете правы. Эти тесты выглядят только как вызов API инструмента. Набор функций, которые понимает Robotium, но не тестировщик. Что же стоит делать в таком случае? Как сделать тесты более читабельными, полезными и чтобы в них можно было как можно быстрее разобраться?</p><div>Давайте начнем с небольшой теории. Для тех, кто уже успешно занимался web автоматизацией, текст ниже можно не читать.</div><p><!--break--></p><div> </div><div>Существует некий подход по именованию классов и функций, который получил название DSL (DomainSpecificLanguage). В чем суть этого подхода? </div><div> </div><div>Wikipedia</div><div>Языки программирования предметной области, дополненные технологиями метапрограммирования, являются эффективным средством автоматизации разработки программного обеспечения и в настоящий момент находят широкое применение в области информационных технологий. </div><div>Процесс создания нового предметно-ориентированного языка состоит из трех шагов:</div><ol><li>Определение абстрактного синтаксиса.</li><li>Определение конкретного синтаксиса.</li><li>Определение правил трансформации.</li></ol><div> </div><div>Для тех, кто не понял: DSL это описание классов и функций в зависимости от предметного домена приложения, которое Вы разрабатываете. Например:</div><ul><li>DSL для подсчета зарплат.</li><li>DSL для оплаты счета.</li><li>DSL для продажи мультимедийного контента.</li></ul><div>Каждая из отраслей (доменов) содержит в себе функции с разным именованием, хотя, иногда, могут выполнять одну и ту же функциональность.</div><div> </div><div>Надеюсь с этим понятно.</div><div> </div><div>Что еще нам нужно знать для того чтобы писать хорошие тесты? Из web автоматизации большинству из нас известен паттерн PageObject (впервые его опубликовал Martin Fauler,но с немного другим названием WindowDriver).</div><div>Существует немало споров как лучше организовать эту модель и стоит ли делать ее вообще. Я поделюсь своим опытом, на основании десятка автоматизированных как веб, так и мобильных проектов. </div><div> </div><div>Так как мы будем говорить о мобильной разработке, то слово PageObject уже не подходит, так как у Native приложений не страницы, а экраны. Назовем, этот паттерн, условно, ScreenObject. </div><div>Теперь давайте представим, что экран со списком заметок (рис 1) это Java класс. </div><div> </div><div> <img alt="" src="/sites/default/files/images/mobile-app.jpg" style="width: 300px; height: 500px; "></div><div>Рис. 1</div><div>А все функции, которые можно выполнить на этой странице это JavaMethods. Получим, что-то вроде вот этого. </div><div> </div>
packagecom.jayway.screens;
importcom.example.android.notepad.NotesList;
importcom.example.android.notepad.R;
importcom.jayway.android.robotium.solo.Solo;
public class NotesListScreen extends BaseScreen {
publicNotesListScreen(Solo solo) {
super(solo, NotesList.class);
}
public void clickAddNote() {
//TODO: Add some logic here
}
publicbooleanisNotePresent(String noteName) {
//TODO: Add some logic here
return false;
}
public void clickNote(String note) {
//TODO: Add some logic here
}
public void removeNote(String noteName) {
//TODO: Add some logic here
}
}
<div> </div><div>То есть мы получили класс NotesListScreen с перечнем всех функций на этом экране. Функции получились пустышки, и нам нужно их реализовать. Давайте начнем с функции clickAddNote(). Добавляем Robotium вызов, который нажмет на эту кнопку. Оп, и что произошло? После нажатия на эту кнопку мы попали на совсем другой экран - AddNoteScreen. </div><div>Нам это так же нужно запрограммировать в методе</div><div> </div>
publicAddNoteScreenclickAddNote() {
getSolo().clickOnMenuItem(getSolo().getString(R.string.menu_insert));
returnnewAddNoteScreen(getSolo());
}
<div> </div><div>Что получилось, созданный метод будет возвращать объект уже нового экрана, у которого будут уже свои методы по работе только с экраном AddNoteScreen.</div><div> </div><div>Это было ключевое, дальше лишь дело техники.</div><div> </div><div>Как будет выглядеть конечный тест?</div>
packagecom.jayway.test;
importandroid.test.suitebuilder.annotation.Smoke;
importcom.example.android.notepad.R;
importcom.jayway.android.robotium.solo.Solo;
importcom.jayway.screens.AddNoteScreen;
importcom.jayway.screens.NotesListScreen;
public class NotePadTest extends BaseTestCase {
private static final String EDITED_TEST = "Edited Test";
private static final String NOTE_NAME = "Note 1";
@Smoke
public void testAddNote() throws Exception {
NotesListScreennoteListScreen = new NotesListScreen(solo);
assertTrue(noteListScreen.isCurrentActivityOpened());
AddNoteScreenaddNoteScreen = noteListScreen.clickAddNote();
addNoteScreen.assertCurrentActivity();
addNoteScreen.typeNote(NOTE_NAME);
noteListScreen = addNoteScreen.goBack();
noteListScreen.assertCurrentActivity();
assertTrue(NOTE_NAME + " is not found", noteListScreen.isNotePresent(NOTE_NAME));
}
}
<div> </div><div>Теперь тест стал намного читабельней, не так ли? Java код начинает выглядеть как обычный тест- кейс, при этом мы видим смену экранов по который "прыгает" пользователь.</div><div> </div><div>Видите как быстро, и не очень сложно мы разобрались с тем как писать правильные тесты на Robotium.</div><div> </div><div>Так же Вы могли заметить, что у меня появился некий базовый класс, от которого унаследован мой тест. Так же у каждого экрана есть унаследованный класс BaseScreen. Эти классы содержат в себе вспомогательные методы, для работы с тестом и экранами. Более детально мы сможем их разобрать в следующих уроках. </div><div> </div><div>Спасибо за внимание. </div>
[1]: http://automated-testing.info/t/nastrojka-sredy-dlya-razrabotki-android-prilozhenij-i-avtomatizaczii-na-robotium/2421