Xamarin.UITest

중요

Visual Studio App Center는 2025년 3월 31일에 사용 중지될 예정입니다. Visual Studio App Center가 완전히 사용 중지될 때까지 계속 사용할 수 있지만 마이그레이션을 고려할 수 있는 몇 가지 권장 대안이 있습니다.

지원 타임라인 및 대안에 대해 자세히 알아보세요.

Xamarin.UITest 는 iOS 및 Android 앱에서 UI 수락 테스트용 NUnit 을 사용하는 C# 테스트 프레임워크입니다. Xamarin.iOS 및 Xamarin.Android 프로젝트와 긴밀하게 통합되지만 네이티브 iOS 및 Android 프로젝트에서도 사용할 수 있습니다. Xamarin.UITest는 Android 및 iOS 디바이스에서 NUnit 테스트를 실행할 수 있는 자동화 라이브러리 입니다. 테스트는 텍스트 입력, 단추 탭 및 제스처(예: 살짝 밀기)와 같은 사용자 인터페이스와 상호 작용합니다.

일반적으로 각 Xamarin.UITest는 라고 하는 메서드로 [Test]작성됩니다. 테스트를 [TestFixture]포함하는 클래스를 라고 합니다. 테스트 픽스처에는 단일 테스트 또는 테스트 그룹이 포함됩니다. 또한 픽스쳐는 테스트를 완료할 때 수행해야 하는 테스트 실행 및 정리를 설정해야 합니다. 각 테스트는 Arrange-Act-Assert 패턴을 따라야 합니다.

  1. 정렬: 테스트가 실행될 수 있도록 조건을 설정하고 작업을 초기화합니다.
  2. 작업: 테스트는 애플리케이션과 상호 작용하고, 텍스트를 입력하고, 단추를 누르는 등의 작업을 수행합니다.
  3. 어설션: 테스트는 법 단계에서 실행되는 작업의 결과를 검사하여 정확성을 확인합니다. 예를 들어 애플리케이션은 특정 오류 메시지가 표시되는지 확인할 수 있습니다.

Xamarin.UITest를 시작하기에 가장 좋은 시기는 모바일 애플리케이션 개발 중입니다. 자동화된 테스트는 다음 목록에 설명된 단계에 따라 기능이 개발되고 있는 것으로 기록됩니다.

  1. Android 또는 iOS 애플리케이션에서 기능을 개발합니다.
  2. 테스트를 작성하고 로컬로 실행하여 기능을 확인합니다.
  3. App Center 테스트에서 새 테스트 실행을 만들거나 기존 테스트 실행을 사용합니다.
  4. IPA 또는 APK를 컴파일한 다음 테스트와 함께 App Center 테스트에 업로드합니다.
  5. App Center 테스트에서 노출되는 문제 또는 버그를 해결합니다.
  6. 애플리케이션의 다음 기능으로 이동하여 프로세스를 반복합니다.

더 이상 활성 개발되지 않는 기존 애플리케이션의 경우 자동화된 테스트를 소급하여 추가하는 것이 비용 효율적이지 않을 수 있습니다. 대신 버그를 수정할 때 Xamarin.UITest를 사용하는 것이 더 좋습니다. 예를 들어 자동화된 테스트가 없고 사용자가 버그를 보고하는 애플리케이션을 생각해 보세요. 해당 버그를 수정하기 위해 할당된 개발자는 다음 작업의 일부(또는 전부)를 수행할 수 있습니다.

  • 버그 또는 회귀를 수동으로 확인합니다.
  • 버그를 보여 주는 Xamarin.UITest를 사용하여 테스트를 작성합니다.
  • App Center 테스트에 테스트를 제출하여 관련 디바이스에 대한 버그의 scope 및 영향에 대한 인사이트를 얻습니다.
  • 버그를 수정합니다.
  • 전달된 Xamarin.UITest로 버그가 수정되었음을 증명합니다.
  • 수정 사항을 제출하고 App Center 테스트에 테스트를 제출하여 관련 디바이스에서 버그가 수정되었는지 확인합니다.
  • 버전 제어에 대한 통과 테스트를 확인합니다.

자동화된 UI 테스트는 화면의 보기를 찾고 상호 작용하는 데 크게 의존합니다. Xamarin.UITest는 서로 작동하는 두 가지 중요한 API 집합으로 이 요구 사항을 해결합니다.

  1. 보기에서 수행할 수 있는 작업 - Xamarin.UITest는 테스트를 통해 보기 탭, 텍스트 입력 또는 보기에서 살짝 밀기와 같은 일반적인 사용자 작업을 시뮬레이션할 수 있는 API를 제공합니다.
  2. 화면에서 보기를 찾는 쿼리 - Xamarin.UITest 프레임워크의 일부는 화면에서 보기를 찾는 API입니다. 쿼리는 뷰의 특성을 검사하고 작업이 작동할 수 있는 개체를 반환하여 런타임에 뷰를 찾습니다. 이러한 방식으로 쿼리하는 것은 화면 크기, 방향 또는 레이아웃에 관계없이 사용자 인터페이스에 대한 테스트를 작성할 수 있는 강력한 기술입니다.

테스트를 작성하는 데 도움이 되도록 Xamarin.UITest는 REPL(read-eval-print-loop)을 제공합니다. REPL을 사용하면 애플리케이션이 실행되는 동안 개발자와 테스터가 화면과 상호 작용할 수 있으며 쿼리 만들기가 간소화됩니다.

Xamarin.UITest API 소개

모바일 애플리케이션과의 모든 테스트 상호 작용은 의 Xamarin.UITest.IAppinstance 통해 발생합니다. 이 인터페이스는 테스트가 애플리케이션과 공동 작업하고 사용자 인터페이스와 상호 작용하는 데 중요한 메서드를 정의합니다. 이 인터페이스에는 두 가지 구체적인 구현이 있습니다.

  • Xamarin.UITest.iOS.iOSApp 이 클래스는 iOS에 대한 테스트를 자동화합니다.
  • Xamarin.UITest.Android.AndroidApp 이 클래스는 Android에서 테스트를 자동화하기 위한 것입니다.

iOSAppAndroidApp 개체는 직접 인스턴스화되지 않습니다. 대신 도우미 ConfigureApp 클래스를 사용하여 만들어집니다. 이 클래스는 또는 AndroidApp 가 제대로 인스턴스화되도록 하는 iOSApp 작성기입니다.

각 테스트에 새 IApp instance 사용하는 것이 좋습니다. 새 instance 한 테스트가 다른 테스트로 유출되는 것을 방지합니다. NUnit 테스트가 의 instance IApp초기화할 수 있는 두 위치가 있습니다.

  • 일반적으로 메서드에서 SetUp 테스트 픽스처는 관련 테스트의 논리적 그룹화이며, 각 테스트는 서로 독립적으로 실행됩니다. 이 시나리오에서는 메서드에서 를 IApp 초기화하여 각 테스트에 SetUpIApp 를 사용할 수 있도록 해야 합니다.
  • TestFixtureSetup 메서드에서 경우에 따라 단일 테스트에 자체 테스트 픽스처가 필요할 수 있습니다. 이 경우 메서드에서 개체를 한 번 초기화하는 IApp 것이 더 합리적일 TestFixtureSetup 수 있습니다.

구성되면 IApp 테스트가 테스트 중인 애플리케이션과 상호 작용하기 시작할 수 있습니다. 이렇게 하려면 화면에 표시되는 보기에 대한 참조를 가져와야 합니다. Xamarin.UITest의 많은 메서드는 매개 변수를 Func<AppQuery, AppQuery> 사용하여 뷰를 찾습니다. 예를 들어 다음 코드 조각은 단추를 탭하는 방법을 보여줍니다.

app.Tap(c=>c.Button("ValidateButton"));

Xamarin.UITest 프레임워크 내에는 두 가지 인터페이스 구현 IApp 이 있습니다. 하나는 iOS용이고 다른 하나는 Android용입니다.

iOS 애플리케이션용 IApp 초기화

Xamarin.UITest가 iOS에서 테스트를 실행하면 iOS 시뮬레이터의 instance 시작하고, 애플리케이션을 배포하고, 실행하고, 테스트 실행을 시작합니다. iOS 애플리케이션을 이미 빌드해야 합니다. Xamarin.UITest는 애플리케이션을 컴파일하고 앱 번들을 만들지 않습니다.

메서드를 AppBundle 사용하여 파일 시스템에서 앱 번들을 찾을 수 있는 위치를 지정할 수 있습니다. 이렇게 하는 방법에는 절대 경로 또는 상대 경로가 있습니다. 이 코드 조각은 앱 번들에 대한 절대 경로를 사용하는 방법을 보여줍니다.

IApp app = ConfigureApp
    .iOS
    .AppBundle("/path/to/iosapp.app")
    .StartApp();

부분 경로는 Xamarin.UITest 어셈블리를 기준으로 해야 합니다. 이 코드 조각은 다음 예제입니다.

IApp app = ConfigureApp
    .iOS
    .AppBundle("../../../iOSAppProject/bin/iPhoneSimulator/Debug/iosapp.app")
    .StartApp();

상대 경로 예제에서는 Xamarin.UITest 어셈블리에서 세 개의 디렉터리를 위로 이동한 다음 iOS 애플리케이션 프로젝트의 프로젝트 트리를 탐색하여 앱 번들을 찾도록 지시 AppBundle 합니다.

ConfigureApp 에는 를 구성하는 IApp데 도움이 되는 다른 메서드가 있습니다. 자세한 내용은 iOSAppConfigurator 클래스를 참조하세요. 더 흥미로운 방법 중 일부는 다음 표에 설명되어 있습니다.

메서드 Description
AppBundle 이 메서드는 테스트할 때 사용할 앱 번들에 대한 경로를 지정합니다.
Debug 이 메서드는 테스트 실행기에서 디버그 로깅 메시지를 사용하도록 설정합니다. 이 메서드는 시뮬레이터에서 애플리케이션을 실행하는 문제를 해결하는 데 유용합니다.
DeviceIdentifier 디바이스 식별자와 함께 사용할 디바이스를 구성합니다. 이 메서드는 아래에서 자세히 설명합니다.
EnableLocalScreenshots 로컬에서 테스트를 실행할 때 스크린샷을 사용하도록 설정합니다. 스크린샷은 클라우드에서 테스트가 실행될 때 항상 사용하도록 설정됩니다.

특정 iOS 시뮬레이터에서 iOS 테스트를 실행하는 방법에 대한 자세한 내용은 iOS 시뮬레이터에 대한 디바이스 ID 결정을 참조하세요.

Android 애플리케이션용 IApp 초기화

Xamarin.UITest는 기존 APK를 연결된 디바이스 또는 이미 실행 중인 Android 에뮬레이터의 instance 배포합니다. 앱이 시작되고 테스트가 실행됩니다. Xamarin.UITest는 APK를 빌드할 수 없으며 Android 에뮬레이터의 instance 시작할 수도 없습니다.

의 메서드 IAppApkFile APK를 찾을 수 있는 파일 시스템의 위치를 지정하는 데 사용됩니다. 이렇게 하는 방법에는 절대 경로 또는 상대 경로가 있습니다. 이 코드 조각은 APK에 대한 절대 경로를 사용하는 방법을 보여줍니다.

IApp app = ConfigureApp
    .Android
    .ApkFile("/path/to/android.apk")
    .StartApp();

부분 경로는 Xamarin.UITest 어셈블리를 기준으로 해야 합니다. 이 코드 조각은 다음 예제입니다.

IApp app = ConfigureApp
    .Android
    .ApkFile("../../../AndroidProject/bin/Debug/android.apk")
    .StartApp();

상대 경로 예제에서는 Xamarin.UITest 어셈블리에서 세 개의 디렉터리를 위로 이동한 다음 Android 애플리케이션 프로젝트의 프로젝트 트리를 탐색하여 apk 파일을 찾도록 지시 ApkFile 합니다.

둘 이상의 디바이스 또는 에뮬레이터가 연결된 경우 Xamarin.UITest는 테스트 실행을 중지하고 테스트에 의도한 대상을 resolve 수 없으므로 오류 메시지를 표시합니다. 이 경우 테스트를 실행하려면 디바이스 또는 에뮬레이터의 직렬 ID 를 제공해야 합니다. 예를 들어 컴퓨터에 연결된 모든 디바이스(또는 에뮬레이터)를 직렬 ID와 함께 나열하는 명령의 다음 출력 adb devices 을 고려합니다.

$ adb devices
List of devices attached
192.168.56.101:5555 device
03f80ddae07844d3    device

메서드를 사용하여 디바이스를 지정할 수 있습니다.DeviceSerial

IApp app = ConfigureApp.Android.ApkFile("/path/to/android.apk")
                               .DeviceSerial("03f80ddae07844d3")
                               .StartApp();

사용자 인터페이스와 상호 작용

뷰와 상호 작용하기 위해 많은 IApp 메서드는 뷰를 Func<AppQuery, AppQuery> 찾기 위해 대리자를 사용합니다. 이 대리자는 Xamarin.UITest가 뷰를 찾는 방법의 핵심에 있는 를 사용합니다 AppQuery .

AppQuery 는 뷰를 찾기 위한 쿼리를 빌드하기 위한 흐름 인터페이스 입니다. 제공 하는 AppQuery 메서드 중 메서드 Marked 는 가장 간단 하 고 가장 유연한 중 하나입니다. 이 메서드는 추론을 사용하여 뷰를 찾으려고 시도하며 다음 섹션에서 자세히 설명합니다. 지금은 애플리케이션과 상호 작용하기 위한 많은 메서드가 있음을 IApp 이해하는 것이 중요합니다. 이러한 메서드는 를 Func<AppQuery, AppQuery> 사용하여 상호 작용할 뷰에 대한 참조를 가져옵니다. 에 의해 제공되는 AppQuery 더 흥미로운 방법 중 일부는 아래에 나열되어 있습니다.

메서드 Description
Button 화면에서 하나 이상의 단추를 찾습니다.
Class 지정된 클래스의 뷰를 찾으려고 시도합니다.
Id 지정된 ID를 사용하여 보기를 찾으려고 합니다.
Index . 일치하는 뷰 컬렉션에서 하나의 보기를 반환합니다. 일반적으로 다른 메서드와 함께 사용됩니다. 인덱스(0부터 시작)를 사용합니다.
Marked 아래에 설명된 추론에 따라 보기를 반환합니다.
Text 제공된 텍스트가 포함된 뷰와 일치합니다.
TextField Android EditText 또는 iOS UITextField와 일치합니다.

예를 들어 다음 메서드는 "SaveUserdataButton"이라는 단추를 탭하여 시뮬레이션하는 방법을 보여줍니다.

app.Tap(c=>c.Marked("SaveUserDataButton"));

AppQuery 는 흐름 인터페이스이므로 여러 메서드 호출을 함께 연결할 수 있습니다. 보기를 탭하는 보다 복잡한 예제를 살펴보겠습니다.

app.Tap(c=>c.Marked("Pending")
            .Parent()
            .Class("AppointmentListCell").Index(0));

여기서 는 AppQuery 먼저 로 표시된 Pending뷰를 찾은 다음, 형식인 해당 뷰 AppointmentListCell 의 첫 번째 부모를 선택합니다.

모바일 앱을 살펴보면 이러한 쿼리를 만드는 것이 까다로울 수 있습니다. Xamarin.UITest는 화면의 보기 계층 구조를 탐색하고, 쿼리 만들기를 실험하고, 이를 사용하여 애플리케이션과 상호 작용하는 데 사용할 수 있는 REPL을 제공합니다.

REPL 사용

REPL을 시작하는 유일한 방법은 기존 테스트 내에서 메서드를 IApp.Repl 호출하는 것입니다. 이렇게 하려면 메서드에서 사용할 수 있는 의 instance IApp 구성하는 NUnit TestFixtureTest 만들어야 합니다. 다음 코드 조각은 이 작업을 수행하는 방법의 예를 보여 주는 코드 조각입니다.

[TestFixture]
public class ValidateCreditCard
{
    IApp app;

    [SetUp]
    public void Setup()
    {
        app = ConfigureApp.Android.ApkFile("/path/to/application.apk").StartApp();
    }
    [Test]
    public void CreditCardNumber_TooLong_DisplayErrorMessage()
    {
        app.Repl();
    }
}

Visual Studio의 여백을 마우스 오른쪽 단추로 클릭하고 실행을 선택하여 테스트를 실행하려면:

테스트에 대한 실행 옵션이 있는 팝업 메뉴의 스크린샷

테스트가 실행되고 메서드가 Repl 호출되면 Xamarin.UITest는 다음 스크린샷과 같이 터미널 세션에서 REPL을 시작합니다.

Xamarin.UITest REPL을 실행하는 macOS 터미널의 스크린샷

REPL은 애플리케이션과 상호 작용하는 라는 appIApp instance 초기화했습니다. 가장 먼저 해야 할 작업 중 하나는 사용자 인터페이스를 탐색하는 것입니다. REPL에는 tree 이를 위한 명령이 있습니다. 표시된 화면에서 보기의 계층 구조를 출력합니다. 예를 들어 애플리케이션의 다음 스크린샷을 고려합니다.

iPhone에서 실행되는 샘플 애플리케이션의 스크린샷

명령을 사용하여 tree 이 화면의 다음 계층 구조를 표시할 수 있습니다.

App has been initialized to the 'app' variable.
Exit REPL with ctrl-c or see help for more commands.

>>> tree
[UIWindow > UILayoutContainerView]
  [UINavigationTransitionView > ... > UIView]
    [UITextView] id: "CreditCardTextField"
      [_UITextContainerView]
    [UIButton] id: "ValidateButton"
      [UIButtonLabel] text: "Validate Credit Card"
    [UILabel] id: "ErrorrMessagesTestField"
  [UINavigationBar] id: "Credit Card Validation"
    [_UINavigationBarBackground]
      [_UIBackdropView > _UIBackdropEffectView]
      [UIImageView]
    [UINavigationItemView]
      [UILabel] text: "Credit Card Validation"
>>>

이 보기에는 ValidateButton의 가 있는 가 id 있음을 알 UIButton 수 있습니다. 명령에서 표시하는 정보를 사용하여 뷰를 tree 찾고 상호 작용하는 데 필요한 쿼리를 만들 수 있습니다. 예를 들어 다음 코드는 단추의 탭을 시뮬레이션합니다.

app.Tap(c=>c.Marked("ValidateButton"))

명령이 입력되면 버퍼의 REPL에서 기억됩니다. REPL은 이 버퍼의 내용을 클립보드에 복사하는 명령을 제공합니다 copy . 이렇게 하면 테스트를 프로토타입으로 만들 수 있습니다. REPL에서 수행된 작업을 로 클립보드 copy에 복사한 다음, 에 해당 명령을 붙여넣을 [Test]수 있습니다.

표시를 사용하여 보기 찾기

AppQuery.Marked 메서드는 화면에서 보기를 쿼리하는 편리하고 강력한 방법입니다. 화면의 보기에 대한 보기 계층 구조를 검사하고 보기의 속성을 제공된 문자열과 일치시키려고 시도하여 작동합니다. Marked 운영 체제에 따라 다르게 작동합니다.

표시된 iOS 보기 찾기

iOS 보기는 다음 특성 중 하나를 사용하여 배치됩니다.

  • AccessibilityIdentifier 보기의 입니다.
  • AccessibilityLabel 보기의 입니다.

예를 들어 를 만들고 UILabel 를 설정하는 다음 C# 코드 조각을 고려합니다 AccessibilityLabel.

UILabel errorMessagesTextField = new UILabel(new RectangleF(10, 210, 300, 40));
errorMessagesTextField.AccessibilityLabel = "ErrorMessagesTextField";
errorMessagesTextField.Text = String.Empty;

이 보기는 다음 쿼리를 통해 확인할 수 있습니다.

AppResult[] results = app.Marked("ErrorMessagesTextField");

표시된 Android 보기 찾기

Android 보기는 다음 속성 중 하나를 기반으로 합니다.

  • Id 보기의 입니다.
  • ContentDescription 보기의 입니다.
  • Text 보기의 입니다.

예를 들어 다음 단추가 정의된 Android 레이아웃을 고려해 보세요.

<Button
    android:text="Action 1"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/action1_button"
    android:layout_weight="1"
    android:layout_marginLeft="5dp" />

이 단추의 android:idaction1_button 작업 1임을 android:text 확인할 수 있습니다. 다음 두 쿼리 중 하나에서 화면에서 단추를 찾습니다.

  • app.Query(c=>c.Marked("action1_button"));
  • app.Query(c=>c.Marked("Action 1"));

Xamarin.UITest.IApp을 사용하여 애플리케이션 제어

구성되고 초기화되면 IApp 테스트가 애플리케이션과 상호 작용하기 시작할 수 있습니다. 를 사용하는 Func<AppQuery, AppQuery> 메서드의 한 가지 예는 메서드입니다 IApp.Query() . 이 메서드는 쿼리를 실행하고 결과를 반환합니다. 가장 간단한 예제는 화면에 표시되는 모든 보기 목록을 반환하는 다음 코드 조각에 나와 있습니다.

AppResult[] results = app.Query(c=>c.All())

다음 표에서는 를 사용하여 AppQuery 화면에서 보기를 찾는 몇 가지 다른 예제를 보여 줍니다.

구문 결과
app.Query(c=>c.Class("UILabel")) 메서드는 .Class() iOS 의 하위 클래스인 뷰를 쿼리합니다 UILabel.
app.Query(c=>c.Id("txtUserName")) 메서드는 .Id()txtUserName의 를 Id 사용하여 뷰를 쿼리합니다.
app.Query(c=>c.Class("UILabel").Text("Hello, World")) "Hello, World" 텍스트가 있는 모든 UILabel 클래스를 찾습니다.
results = app.Query(c=>c.Marked("ValidateButton")) 지정된 텍스트로 표시된 모든 보기를 반환합니다. 메서드 Marked 는 쿼리를 간소화할 수 있는 유용한 방법입니다. 다음 섹션에서 설명합니다.

다음 표에는 화면에서 보기와 상호 작용하거나 조작하는 데 사용할 수 있는 에서 제공하는 IApp 메서드 중 일부(전부는 아님)가 나열되어 있습니다.

예제 Description
PressEnter 앱에서 Enter 키를 누릅니다.
Tap 일치하는 요소에 대한 탭/터치 제스처를 시뮬레이션합니다.
EnterText 보기에 텍스트를 입력합니다. iOS 애플리케이션에서 Xamarin.UITest는 소프트 키보드를 사용하여 텍스트를 입력합니다. 반면 Xamarin.UITest는 Android 키보드를 사용하지 않으며 보기에 텍스트를 직접 입력합니다.
WaitForElement 보기가 화면에 나타날 때까지 테스트 실행을 일시 중지합니다.
Screenshot(String) 현재 상태의 애플리케이션 스크린샷을 가져와 디스크에 저장합니다. 스크린샷에 FileInfo 대한 정보가 포함된 개체를 반환합니다.
Flash 이 메서드를 사용하면 선택한 보기가 화면에서 "플래시" 또는 "깜박임"으로 표시됩니다.

인터페이스에 IApp 대한 자세한 내용은 , AndroidAppiOSApp에 대한 IAppAPI 설명서를 참조하세요.

이러한 메서드를 사용하는 방법의 예로 위에 표시된 스크린샷에 대해 다음 테스트를 고려합니다. 이 테스트는 텍스트 필드에 크레딧 카드 대한 17자리 숫자를 입력한 다음 화면에서 단추를 탭합니다. 그런 다음, 사용자에게 번호가 유효한 크레딧 카드 번호가 되기에는 너무 길다는 것을 알리는 오류 메시지를 화면에 검사합니다.

[Test]
public void CreditCardNumber_TooLong_DisplayErrorMessage()
{
    /* Arrange - set up our queries for the views */
    // Nothing to do here, app has been instantiated in the [SetUp] method.

    /* Act */
    app.EnterText(c => c.Marked("CreditCardTextField"), new string('9', 17));
    // Screenshot can be used to break this test up into "steps".
    // The screenshot can be inspected after the test run to verify
    // the visual correctness of the screen.
    app.Screenshot("Entering a 17 digit credit card number.");

    app.Tap(c => c.Marked("ValidateButton"));
    app.Screenshot("The validation results.");

    /* Assert */
    AppResult[] result = app.Query(c => c.Class("UILabel").Text("Credit card number is too long."));
    Assert.IsTrue(result.Any(), "The error message isn't being displayed.");
}

또한 이 테스트는 메서드를 Screenshot 사용하여 테스트 실행 중에 주요 지점에서 사진을 찍습니다. 이 테스트를 실행하면 App Center에서 스크린샷을 가져와서 테스트 결과에 표시합니다. 메서드를 사용하면 테스트를 단계로 분리하고 스크린샷에 대한 설명을 제공할 수 있습니다.