<p><strong>Meta Description:</strong> Aprenda a testar aplicativos mobile: Appium, Espresso, XCTest, testes emulators vs dispositivos reais, e automação mobile para iOS e Android.</p>

<hr>
<h2>Introdução aos Testes Mobile</h2>

<p>Testar aplicativos mobile apresenta desafios únicos: múltiplas versões de OS, diferentes tamanhos de tela, conectividade variável e interação touch. Uma estratégia de teste mobile eficaz deve cobrir dispositivos, plataformas e condições de uso diversas.</p>

<h3>Panorama Mobile</h3>

<figure class="wp-block-table"><table>
<tr><th>Dado</th><th>Valor</th></tr>
<tr><td>Apps disponíveis</td><td>6.5+ milhões</td></tr>
<tr><td>Tempo médio em apps</td><td>4.8 horas/dia</td></tr>
<tr><td>Usuários mobile</td><td>7.33 bilhões</td></tr>
<tr><td>Fragmentação Android</td><td>24.000+ dispositivos</td></tr>
<tr><td>iOS versions ativas</td><td>4-5 simultâneas</td></tr>
</table></figure>

<hr>
<h2>Estratégia de Testes Mobile</h2>

<h3>Pirâmide de Testes Mobile</h3>

<pre><code>              /
             /           E2E Tests
            /____        (10{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442})
           /             Funcionalidade completa
          /              em dispositivos reais
         /__________
        /               Integration Tests
       /______________  (20{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442})
      /                 Componentes e módulos
     /__________________
    /                     Unit Tests
   /______________________ (70{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442})
   Componentes e lógica
</code></pre>

<h3>Dispositivos: Emuladores vs. Reais</h3>

<figure class="wp-block-table"><table>
<tr><th>Aspecto</th><th>Emuladores</th><th>Dispositivos Reais</th></tr>
<tr><td><strong>Custo</strong></td><td>Grátis</td><td>Dispositivos físicos</td></tr>
<tr><td><strong>Velocidade</strong></td><td>Mais lentos</td><td>Mais rápidos</td></tr>
<tr><td><strong>Acurácia</strong></td><td>Limitada</td><td>Real</td></tr>
<tr><td><strong>Hardware</strong></td><td>CPU/RAM limitados</td><td>Real performance</td></tr>
<tr><td><strong>Sensores</strong></td><td>Simulados</td><td>Reais (GPS, câmera)</td></tr>
<tr><td><strong>Rede</strong></td><td>Controlável</td><td>Real, variável</td></tr>
</table></figure>

<p><strong>Recomendação:</strong> Usar ambos - emuladores para CI/rapidez, dispositivos reais para QA final.</p>

<hr>
<h2>Ferramentas de Teste Mobile</h2>

<h3>1. Appium - Cross-Platform</h3>

<p><strong>Melhor para:</strong> Testes cross-platform (iOS + Android) com uma codebase.</p>

<h4>Instalação</h4>

<pre><code># macOS com Homebrew
brew install node
npm install -g appium
npm install -g appium-doctor
appium &amp;
</code></pre>

<h4>Configuração Android</h4>

<pre><code># SDK Android
export ANDROID_HOME=~/Android/Sdk
export PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools

<p># Start emulador<br>$ANDROID_HOME/emulator/emulator -avd Pixel_6_API_33<br></code></pre></p>

<h4>Script Appium (Python)</h4>

<pre><code>from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.common.touch_action import TouchAction
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

<p>class TestApp:<br>    @pytest.fixture<br>    def driver(self):<br>        capabilities = {<br>            "platformName": "Android",<br>            "deviceName": "Pixel_6_API_33",<br>            "app": "/path/to/app.apk",<br>            "automationName": "UiAutomator2",<br>            "autoGrantPermissions": True<br>        }</p>

<p>driver = webdriver.Remote(<br>            "http://localhost:4723/wd/hub",<br>            capabilities<br>        )<br>        yield driver<br>        driver.quit()</p>

<p>def test_login_flow(self, driver):<br>        # Aguardar elementos<br>        wait = WebDriverWait(driver, 10)</p>

<p># Clicar no campo email<br>        email_field = wait.until(<br>            EC.presence_of_element_located(<br>                (AppiumBy.ANDROID_UIAUTOMATOR, 'resourceId("com.app:id/email")')<br>            )<br>        )<br>        email_field.send_keys("usuario@test.com")</p>

<p># Clicar no campo senha<br>        driver.find_element(<br>            AppiumBy.ANDROID_UIAUTOMATOR,<br>            'resourceId("com.app:id/password")'<br>        ).send_keys("senha123")</p>

<p># Clicar em Login<br>        driver.find_element(<br>            AppiumBy.ANDROID_UIAUTOMATOR,<br>            'text("Entrar")'<br>        ).click()</p>

<p># Verificar redirecionamento para dashboard<br>        wait.until(<br>            EC.presence_of_element_located(<br>                (AppiumBy.ANDROID_UIAUTOMATOR, 'text("Dashboard")')<br>            )<br>        )</p>

<p>def test_swipe_gesture(self, driver):<br>        # Obter tamanho da tela<br>        size = driver.get_window_size()<br>        start_x = size['width'] * 0.5<br>        start_y = size['height'] * 0.8<br>        end_y = size['height'] * 0.2</p>

<p># Swipe up<br>        TouchAction(driver).press(start_x, start_y).move_to(0, -500).release().perform()</p>

<p>def test_scroll_to_element(self, driver):<br>        # Scroll até encontrar elemento<br>        driver.find_element(<br>            AppiumBy.ANDROID_UIAUTOMATOR,<br>            'new UiScrollable(new UiSelector()).scrollIntoView(text("Settings"))'<br>        )<br></code></pre></p>

<h3>2. Espresso - Android Nativo</h3>

<p><strong>Melhor para:</strong> Projetos Android com Kotlin/Java.</p>

<h4>Dependências (build.gradle)</h4>

<pre><code>androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
</code></pre>

<h4>Script Espresso (Kotlin)</h4>

<pre><code>package com.example.app

<p>import androidx.test.ext.junit.rules.ActivityScenarioRule<br>import androidx.test.ext.junit.runners.AndroidJUnit4<br>import androidx.test.filters.LargeTest<br>import org.junit.Rule<br>import org.junit.Test<br>import org.junit.runner.RunWith<br>import static androidx.test.espresso.Espresso.*<br>import static androidx.test.espresso.action.ViewActions.*<br>import static androidx.test.espresso.assertion.ViewAssertions.*<br>import static androidx.test.espresso.matcher.ViewMatchers.*</p>

<p>@RunWith(AndroidJUnit4::class)<br>@LargeTest<br>class LoginTest {</p>

<p>@get:Rule<br>    val activityRule = ActivityScenarioRule(LoginActivity::class.java)</p>

<p>@Test<br>    fun testLoginWithValidCredentials() {<br>        // Preencher email<br>        onView(withId(R.id.email_input))<br>            .perform(typeText("usuario@test.com"), closeSoftKeyboard())</p>

<p>// Preencher senha<br>        onView(withId(R.id.password_input))<br>            .perform(typeText("senha123"), closeSoftKeyboard())</p>

<p>// Clicar em Login<br>        onView(withId(R.id.login_button))<br>            .perform(click())</p>

<p>// Verificar navegação para Dashboard<br>        onView(withId(R.id.dashboard_title))<br>            .check(matches(isDisplayed()))<br>    }</p>

<p>@Test<br>    fun testLoginWithInvalidCredentials() {<br>        onView(withId(R.id.email_input))<br>            .perform(typeText("invalido@test.com"))<br>            .perform(typeText("senhaerrada"), closeSoftKeyboard())</p>

<p>onView(withId(R.id.login_button)).perform(click())</p>

<p>// Verificar mensagem de erro<br>        onView(withId(R.id.error_message))<br>            .check(matches(withText("Credenciais inválidas")))<br>    }</p>

<p>@Test<br>    fun testScrollThroughList() {<br>        // Scroll down<br>        onView(withId(R.id.product_list))<br>            .perform(<br>                RecyclerViewActions.scrollTo(<br>                    hasDescendant(withText("Product 50"))<br>                )<br>            )</p>

<p>// Clicar no item<br>        onView(withText("Product 50"))<br>            .perform(click())<br>    }<br>}<br></code></pre></p>

<h3>3. XCTest - iOS Nativo</h3>

<p><strong>Melhor para:</strong> Projetos iOS com Swift/Objective-C.</p>

<h4>Script XCTest (Swift)</h4>

<pre><code>import XCTest

<p>class LoginTests: XCTestCase {</p>

<p>var app: XCUIApplication!</p>

<p>override func setUp() {<br>        super.setUp()<br>        continueAfterFailure = false<br>        app = XCUIApplication()<br>        app.launchArguments = ["--uitesting"]<br>        app.launch()<br>    }</p>

<p>func testLoginWithValidCredentials() {<br>        // Acessar campos<br>        let emailField = app.textFields["email_input"]<br>        let passwordField = app.secureTextFields["password_input"]<br>        let loginButton = app.buttons["login_button"]</p>

<p>// Preencher credenciais<br>        emailField.tap()<br>        emailField.typeText("usuario@test.com")</p>

<p>passwordField.tap()<br>        passwordField.typeText("senha123")</p>

<p>// Clicar em Login<br>        loginButton.tap()</p>

<p>// Verificar Dashboard<br>        XCTAssertTrue(<br>            app.staticTexts["dashboard_title"].waitForExistence(timeout: 5)<br>        )<br>    }</p>

<p>func testPullToRefresh() {<br>        // Encontrar UITableView<br>        let tableView = app.tables["product_list"]</p>

<p>// Pull to refresh<br>        let firstCell = tableView.cells.element(boundBy: 0)<br>        let cellCenter = firstCell.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))<br>        let pullPoint = cellCenter.withOffset(CGVector(dx: 0, dy: -100))</p>

<p>cellCenter.press(forDuration: 0, thenDragTo: pullPoint)</p>

<p>// Verificar refresh indicator<br>        XCTAssertTrue(app.activityIndicators["refresh_indicator"].exists)<br>    }</p>

<p>func testSwipeToDelete() {<br>        let tableView = app.tables["product_list"]<br>        let cell = tableView.cells.element(boundBy: 0)</p>

<p>// Swipe left<br>        cell.swipeLeft()</p>

<p>// Clicar em Delete<br>        app.buttons["delete_button"].tap()</p>

<p>// Verificar confirmação<br>        app.alerts["Confirma exclusão?"].buttons["Excluir"].tap()<br>    }<br>}<br></code></pre></p>

<hr>
<h2>Testes em Dispositivos Reais</h2>

<h3>Serviços de Cloud Testing</h3>

<figure class="wp-block-table"><table>
<tr><th>Serviço</th><th>Dispositivos</th><th>Preço</th><th>Melhor Para</th></tr>
<tr><td><strong>BrowserStack</strong></td><td>3000+</td><td>$29+/mês</td><td>Cobertura completa</td></tr>
<tr><td><strong>Sauce Labs</strong></td><td>1000+</td><td>$39+/mês</td><td>Enterprise</td></tr>
<tr><td><strong>AWS Device Farm</strong></td><td>数百</td><td>Sob demanda</td><td>AWS integration</td></tr>
<tr><td><strong>Firebase Test Lab</strong></td><td>数百</td><td>Pay-per-use</td><td>Android focused</td></tr>
</table></figure>

<h3>Setup BrowserStack</h3>

<pre><code># pytest + BrowserStack
from appium import webdriver

<p>def get_browserstack_driver():<br>    desired_caps = {<br>        "platformName": "android",<br>        "deviceName": "Samsung Galaxy S23",<br>        "os_version": "13.0",<br>        "app": "bs://APP_ID",<br>        "browserstack.user": "USER",<br>        "browserstack.key": "ACCESS_KEY",<br>        "browserstack.debug": True,<br>        "browserstack.networkLogs": True<br>    }</p>

<p>return webdriver.Remote(<br>        "http://hub-cloud.browserstack.com/wd/hub",<br>        desired_caps<br>    )<br></code></pre></p>

<hr>
<h2>Cenários de Teste Mobile</h2>

<h3>1. Testes de Interface</h3>

<pre><code>def test_responsividade_telas():
    """Testa layouts em diferentes tamanhos de tela"""
    sizes = [
        (360, 640),  # Small phone
        (390, 844),  # iPhone 12/13
        (412, 915),  # Large phone
        (600, 1024), # Tablet portrait
    ]

<p>for width, height in sizes:<br>        driver.execute_script(f"setWindowSize({width}, {height})")</p>

<p># Verificar elementos não se sobrepõem<br>        assert not elements_overlap(header, footer)<br>        assert text_is_readable(header)<br></code></pre></p>

<h3>2. Testes de Conectividade</h3>

<pre><code>def test_offline_mode():
    """Testa comportamento sem internet"""
    # Desligar rede
    driver.set_network_connection(0)  # Airplane mode

<p># Tentar acessar conteúdo offline<br>    assert app.has_cached_content()<br>    assert app.can_show_cached_products()</p>

<p># Tentar ação que requer internet<br>    with pytest.raises(NetworkError):<br>        app.submit_order()</p>

<p># Reconectar<br>    driver.set_network_connection(6)  # WiFi</p>

<p># Sincronizar<br>    app.sync_pending_orders()<br>    assert order.was_synced()<br></code></pre></p>

<h3>3. Testes de Performance</h3>

<pre><code>@Test
fun testListScrollPerformance() {
    val recyclerView = espresso.onView(
        withId(R.id.product_list)
    )

<p>// Medir FPS durante scroll<br>    val monitor = FrameMetricsMonitor()</p>

<p>recyclerView.perform(<br>        scrollToPosition(100),<br>        scrollToPosition(50),<br>        scrollToPosition(0)<br>    )</p>

<p>val metrics = monitor.getMetrics()</p>

<p>// Verificar 60 FPS (16.67ms por frame)<br>    assertThat(metrics.averageFrameTime).isLessThan(20.0)<br>    assertThat(metrics.droppedFrames).isLessThan(5)<br>}<br></code></pre></p>

<hr>
<h2>Appium + CI/CD</h2>

<h3>GitHub Actions</h3>

<pre><code>name: Mobile Tests

<p>on: [push, pull_request]</p>

<p>jobs:<br>  android-tests:<br>    runs-on: macos-latest</p>

<p>steps:<br>      - uses: actions/checkout@v4</p>

<p>- name: Setup Java<br>        uses: actions/setup-java@v4<br>        with:<br>          java-version: '17'</p>

<p>- name: Setup Android SDK<br>        uses: android-actions/setup-android@v2</p>

<p>- name: Cache Appium<br>        uses: actions/cache@v3<br>        with:<br>          path: node_modules<br>          key: ${{ runner.os }}-appium</p>

<p>- name: Install Dependencies<br>        run: |<br>          npm ci<br>          pip install pytest pytest-appium</p>

<p>- name: Run Tests on BrowserStack<br>        env:<br>          BROWSERSTACK_USER: ${{ secrets.BS_USER }}<br>          BROWSERSTACK_KEY: ${{ secrets.BS_KEY }}<br>        run: |<br>          pytest tests/mobile/ <br>            --device="Samsung Galaxy S23" <br>            --platform="Android 13" <br>            --hub="http://hub.browserstack.com/wd/hub"<br></code></pre></p>

<hr>
<h2>Conclusão</h2>

<p>Testes mobile requerem estratégia bem definida cobrindo múltiplas plataformas, dispositivos e condições. As chaves para sucesso são:<br><ol><li><strong>Automação estratégica</strong> - Appium para cross-platform, nativo para performance</li><br><li><strong>Device matrix</strong> - Priorizar por uso real</li><br><li><strong>CI/CD integrado</strong> - Testes em cloud para escalabilidade</li><br><li><strong>Performance monitorada</strong> - FPS, memória, bateria</li><br><li><strong>Testes de condições reais</strong> - Offline, network switching</li><br></ol><br><hr><br><h3>FAQ</h3></p>

<p><strong>P: Appium é mais lento que Espresso/XCTest?</strong>  <br>R: Sim, mas permite código único para iOS e Android.</p>

<p><strong>P: Quantos dispositivos testar?</strong>  <br>R: Foque nos top 10-20 dispositivos por mercado (iOS, Android).</p>

<p><strong>P: Como testar notificações push?</strong>  <br>R: Use emuladores para automação, dispositivos reais para validação final.</p>

<p><strong>P: Testes mobile podem ser 100{6727d158e4474b0847515bd23a8ee4bebd5f1aac7a18bc968e51d8985dccb442} automatizados?</strong>  <br>R: Não. Alguns testes (usabilidade, exploratory) requerem testers humanos.</p>