테스트 프레임워크

pytest, Node.js, .NET 테스트를 위한 선택 도우미입니다. 폰 픽스처, 실패 리포트에 남는 최근 명령, 실패한 테스트마다 저장되는 스크린샷과 화면 트리, 병렬 작업자마다 한 대씩 배정되는 폰.

번역된 페이지입니다. 영어 문서와 내용이 다르면 영어 문서가 기준입니다. English

어떤 테스트 러너든 Droidline을 그대로 부를 수 있습니다. 이 페이지의 도우미는 선택 사항이며, 보통 직접 만들게 되는 것들을 더해 줍니다. 테스트마다 폰을 넘겨주는 픽스처, 테스트가 폰에 보낸 명령 기록, 테스트가 실패한 순간의 화면입니다. 켜기 전에는 아무것도 불러오지 않습니다.

pytest

한 번 실행할 때만 켜거나, conftest.py에서 프로젝트 전체에 켜세요.

pytest -p droidline.pytest_plugin
# conftest.py
pytest_plugins = ["droidline.pytest_plugin"]

그다음 phone 픽스처를 받으세요.

def test_log_in(phone):
    phone.launch("com.example.shop")
    phone.input("id", "email", "knife@example.com")
    phone.touch("text", "로그인")
    assert phone.exists("id", "greeting")
옵션하는 일
--droidline-device NAME테스트할 폰. 없으면 DROIDLINE_DEVICE, 그것도 없으면 온라인인 유일한 폰.
--droidline-lease세션 동안 빈 폰을 빌립니다. pytest -n 4(pytest-xdist)로 돌리면 작업자마다 폰을 하나씩 받고, 빈 폰이 없으면 최대 30초 기다립니다.
--droidline-artifacts DIR실패한 테스트마다 폰의 스크린샷, 화면 트리, 명령 기록을 DIR/<테스트>/에 저장합니다.

phone을 쓰는 테스트가 실패하면 리포트에 droidline commands 구역이 생기고, 그 테스트가 보낸 최근 요청이 나옵니다. 어느 단계에서 틀어졌는지 바로 보입니다.

---------------------------- droidline commands ----------------------------
14:02:11 launch {"package": "com.example.shop"} -> ok (812 ms)
14:02:13 touch {"by": "text", "value": "로그인"} -> NOT_FOUND (10013 ms)

--droidline-artifacts를 주면 폴더에 commands.txt, screen.json, screen.png가 생깁니다. 각 파일은 가능한 만큼 저장됩니다. 폰이 오프라인이어도 명령 기록은 남고, Android 9와 10에서는 스크린샷에 화면 캡처 허용이 필요합니다.

플러그인은 세션 전체에서 서버 연결 하나를 쓰는 droidline_client 픽스처도 제공합니다.

다른 Python 테스트 러너

같은 도우미를 pytest 없이 쓸 수 있습니다.

from droidline.testing import format_history, save_failure

try:
    run_steps(d)
except Exception:
    save_failure(d, "artifacts/checkout")   # 스크린샷, 트리, 명령 기록
    raise

print(format_history(d.history[-10:]))      # 최근 요청 10개를 글로

Node.js

도우미는 droidline/testing에 있습니다. 러너가 테스트 실패를 알려 주는 곳에서 saveFailure를 부르세요. 내장 node:test라면 이렇게 씁니다.

import { after, test } from "node:test";
import { connect } from "droidline";
import { saveFailure } from "droidline/testing";

const d = await connect();
after(() => d.close());

// 테스트 본문을 실행하고, 예외가 나면 증거를 남깁니다.
async function onPhone(t, body) {
  try {
    await body();
  } catch (err) {
    await saveFailure(d, `artifacts/${t.name}`);
    throw err;
  }
}

test("로그인", (t) =>
  onPhone(t, async () => {
    await d.launch("com.example.shop");
    await d.touch("text", "로그인");
  }));

Vitest나 Jest에서는 직접 만든 실패 처리 훅에서 saveFailure(d, folder)를 부르세요. formatHistory(d.history)는 명령 기록을 글로 돌려줍니다.

.NET

.NET용 플러그인 패키지는 없습니다. Droidline 패키지의 Testing 클래스에 같은 도우미가 있으니, 테스트 프레임워크에서 직접 부르세요. xUnit이라면 이렇게 씁니다.

using System.Runtime.CompilerServices;
using Droidline;
using Xunit;

// 클래스 안의 모든 테스트가 폰 하나를 함께 씁니다.
public sealed class PhoneFixture : IAsyncLifetime
{
    public Device Phone { get; private set; } = null!;

    public async Task InitializeAsync() => Phone = await DroidlineClient.ConnectAsync();

    public async Task DisposeAsync() => await Phone.DisposeAsync();
}

public class ShopTests : IClassFixture<PhoneFixture>
{
    private readonly Device d;

    public ShopTests(PhoneFixture fixture) => d = fixture.Phone;

    // 테스트 본문을 실행하고, 예외가 나면 증거를 남깁니다.
    private async Task OnPhone(Func<Task> body, [CallerMemberName] string test = "")
    {
        try
        {
            await body();
        }
        catch
        {
            await Testing.SaveFailureAsync(d, $"artifacts/{test}");
            throw;
        }
    }

    [Fact]
    public Task LogIn() => OnPhone(async () =>
    {
        await d.LaunchAsync("com.example.shop");
        await d.TouchAsync("text", "로그인");
    });
}

[CallerMemberName]이 테스트 메서드 이름을 채워 주므로, 실패한 테스트마다 폴더가 따로 생깁니다. NUnit이나 MSTest에서도 테스트 본문을 같은 방식으로 감싸면 됩니다. Testing.FormatHistory(d.History)는 명령 기록을 글로 돌려주므로, 예를 들어 xUnit의 ITestOutputHelper에 남길 수 있습니다.

xUnit은 테스트 클래스들을 병렬로 실행합니다. 클래스마다 폰을 따로 주려면 InitializeAsync에서 ConnectAsync() 대신 DroidlineClient.LeaseAsync(wait: 60)을 쓰세요. 폰을 dispose하면 돌려줍니다.

명령 기록

클라이언트마다 최근 요청 200개를 기억합니다. d.history는 그 폰에 보낸 요청을 오래된 순서로 보여 주며, 각각 cmd, params, ok, error, ms를 담습니다. .NET에서는 d.History이고, 각각 Cmd, Params, Ok, Error, Ms를 담습니다. 스크린샷 같은 긴 값은 길이만 남깁니다. 기록은 내 프로세스 안에만 있고 어디로도 보내지 않습니다.

여러 폰에서 동시에

테스트를 병렬로 돌리고 lease로 작업자마다 폰을 하나씩 주세요. pytest -n 4 --droidline-lease를 쓰거나, 직접 만든 준비 단계에서 lease()(.NET에서는 DroidlineClient.LeaseAsync())를 부르면 됩니다. 스크립트끼리 폰 나눠 쓰기를 보세요.