AvaloniaUI 앱에서 UI 테스트를 자동화하는 방법
UI 테스트가 필요한 이유
UI 테스트를 자동화해야 하는 이유는 무엇입니까? 수동 테스트에 비해 시간이 절약되기 때문입니다. 더 정확하고 회귀 버그를 찾는 데 도움이 됩니다. 여기서 유일한 단점은 테스트 작성 및 초기 설정에 소요되는 시간입니다.
UI 테스트가 할 수 있는 일
UI 테스트는 사용자가 하는 모든 것을 에뮬레이트할 수 있습니다. 마우스 이동, 클릭, 키 누르기 등. 수동 테스트에서와 같이 테스트에서 단일 테스트 케이스/시나리오를 확인하는 것이 좋습니다. 내 앱의 예: 디렉토리 생성 대화 상자를 열고 디렉토리 이름을 입력하고 생성하고 생성되었는지 관찰합니다. 이 테스트는 쉽게 자동화될 수 있습니다. 스모크 테스트에서 자동화를 시작하고 전체 회귀 테스트로 마무리하는 것이 좋습니다.
AvaloniaUI에서 UI 테스트
AvaloniaUI에는 앱 설정 후 몇 초 후에 사용자 정의 코드를 실행할 수 있는 기능이 있습니다. 거기에 테스트 코드를 추가하고 시나리오를 실행할 수 있습니다. AvaloniaUI 0.10.0은 헤드리스 플랫폼도 도입했습니다. 헤드리스 앱은 UI가 없으므로 GUI가 아닌 OS에서도 시작할 수 있습니다. GUI가 없는 서버에서 테스트를 실행하는 데 유용할 수 있습니다. github 작업을 통해 Github에서 UI 테스트를 실행하므로 이 옵션이 유용합니다. 실제 앱을 실행하고 테스트를 실행하는 것을 선호하지만 전체 앱을 실행하지 않고 컨트롤 등을 테스트할 수도 있습니다.
내부의 Avalonia 앱은 정적 필드 등을 사용하므로 테스트당 앱의 새 인스턴스를 생성할 수 없습니다. 단일 인스턴스를 생성하고 테스트 전체에서 재사용해야 하므로 UI 테스트 프로젝트에서 테스트의 병렬 실행을 비활성화해야 합니다. 또한 모든 테스트에는 적절한 정리가 있어야 합니다. 그렇지 않으면 다른 테스트가 중단될 수 있습니다.
UI 테스트를 위한 인프라 설정
테스트를 위해 Xunit을 사용하는데 UI 테스트에서는 기본적으로 작동하지 않습니다. Xunit에 내 러너를 추가해야 했습니다.
private class Runner : XunitTestAssemblyRunner
{
// constructor
public override void Dispose()
{
AvaloniaApp.Stop(); // cleanups existing avalonia app instance
base.Dispose();
}
// this method is called only if test parallelization is enabled. I had to enable it and set max parallelization limit to 1 in order to avoid parallel tests execution
protected override void SetupSyncContext(int maxParallelThreads)
{
var tcs = new TaskCompletionSource<SynchronizationContext>();
var thread = new Thread(() =>
{
try
{
// DI registrations
AvaloniaApp.RegisterDependencies();
AvaloniaApp
.BuildAvaloniaApp()
.AfterSetup(_ =>
{
// sets sync context for tests. avalonia UI runs in it's own single thread, updates from other threads are not allowed
tcs.SetResult(SynchronizationContext.Current);
})
.StartWithClassicDesktopLifetime(new string[0]); // run app as usual
}
catch (Exception e)
{
tcs.SetException(e);
}
})
{
IsBackground = true
};
thread.Start();
SynchronizationContext.SetSynchronizationContext(tcs.Task.Result);
}
}
Full code
One more headless tests example
또한 실제 앱의 래퍼인 AvaloniaApp
클래스를 추가했습니다.
public static class AvaloniaApp
{
// DI registrations
public static void RegisterDependencies() =>
Bootstrapper.Register(Locator.CurrentMutable, Locator.Current);
// stop app and cleanup
public static void Stop()
{
var app = GetApp();
if (app is IDisposable disposable)
{
Dispatcher.UIThread.Post(disposable.Dispose);
}
Dispatcher.UIThread.Post(() => app.Shutdown());
}
public static MainWindow GetMainWindow() => (MainWindow) GetApp().MainWindow;
public static IClassicDesktopStyleApplicationLifetime GetApp() =>
(IClassicDesktopStyleApplicationLifetime) Application.Current.ApplicationLifetime;
public static AppBuilder BuildAvaloniaApp() =>
AppBuilder
.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.UseHeadless(); // note that I run app as headless one
}
UI 테스트
이제 테스트를 작성할 시간입니다! 다음은 F1
를 통해 대화 상자를 여는 테스트의 예입니다.
public class OpenAboutDialogFlow : IDisposable
{
private AboutDialog _dialog;
[Fact(DisplayName = "Open about dialog")]
public async Task TestAboutDialog()
{
var app = AvaloniaApp.GetApp();
var window = AvaloniaApp.GetMainWindow();
// wait for initial setup
await Task.Delay(100);
Keyboard.PressKey(window, Key.Tab); // hack for focusing file panel, in headless tests it's not focused by default
Keyboard.PressKey(window, Key.Down);
Keyboard.PressKey(window, Key.F1); // press F1
await Task.Delay(100); // UI is not updated immediately so I had to add delays everywhere
_dialog = app
.Windows
.OfType<AboutDialog>()
.SingleOrDefault();
Assert.NotNull(_dialog);
await Task.Delay(100);
var githubButton = _dialog.GetVisualDescendants().OfType<Button>().SingleOrDefault();
Assert.NotNull(githubButton);
Assert.True(githubButton.IsDefault);
Assert.True(githubButton.Command.CanExecute(null));
}
public void Dispose() => _dialog?.Close(); // cleanup: close dialog
}
간단해 보이죠? 지연을 위해 몇 가지 추가 코드를 추가해야 했지만 실제로 테스트는 간단합니다. 다음은 지연 서비스의 예입니다.
public static class WaitService
{
public static async Task WaitForConditionAsync(Func<bool> condition, int delayMs = 50, int maxAttempts = 20)
{
for (var i = 0; i < maxAttempts; i++)
{
await Task.Delay(delayMs);
if (condition())
{
break; // stop waiting for condition
}
}
}
}
More tests examples
Official example
결론
AvalononiaUI는 UI 테스트를 실행하기 위한 좋은 인프라를 제공합니다. 많은 시간을 절약할 수 있기 때문에 충분히 복잡한 경우 프로젝트에서 사용하는 것이 좋습니다. 프로젝트에서 UI 테스트를 사용하고 있습니까? 댓글로 알려주세요
Reference
이 문제에 관하여(AvaloniaUI 앱에서 UI 테스트를 자동화하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/ingvarx/how-to-automate-ui-testing-in-avaloniaui-app-4an4
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
UI 테스트는 사용자가 하는 모든 것을 에뮬레이트할 수 있습니다. 마우스 이동, 클릭, 키 누르기 등. 수동 테스트에서와 같이 테스트에서 단일 테스트 케이스/시나리오를 확인하는 것이 좋습니다. 내 앱의 예: 디렉토리 생성 대화 상자를 열고 디렉토리 이름을 입력하고 생성하고 생성되었는지 관찰합니다. 이 테스트는 쉽게 자동화될 수 있습니다. 스모크 테스트에서 자동화를 시작하고 전체 회귀 테스트로 마무리하는 것이 좋습니다.
AvaloniaUI에서 UI 테스트
AvaloniaUI에는 앱 설정 후 몇 초 후에 사용자 정의 코드를 실행할 수 있는 기능이 있습니다. 거기에 테스트 코드를 추가하고 시나리오를 실행할 수 있습니다. AvaloniaUI 0.10.0은 헤드리스 플랫폼도 도입했습니다. 헤드리스 앱은 UI가 없으므로 GUI가 아닌 OS에서도 시작할 수 있습니다. GUI가 없는 서버에서 테스트를 실행하는 데 유용할 수 있습니다. github 작업을 통해 Github에서 UI 테스트를 실행하므로 이 옵션이 유용합니다. 실제 앱을 실행하고 테스트를 실행하는 것을 선호하지만 전체 앱을 실행하지 않고 컨트롤 등을 테스트할 수도 있습니다.
내부의 Avalonia 앱은 정적 필드 등을 사용하므로 테스트당 앱의 새 인스턴스를 생성할 수 없습니다. 단일 인스턴스를 생성하고 테스트 전체에서 재사용해야 하므로 UI 테스트 프로젝트에서 테스트의 병렬 실행을 비활성화해야 합니다. 또한 모든 테스트에는 적절한 정리가 있어야 합니다. 그렇지 않으면 다른 테스트가 중단될 수 있습니다.
UI 테스트를 위한 인프라 설정
테스트를 위해 Xunit을 사용하는데 UI 테스트에서는 기본적으로 작동하지 않습니다. Xunit에 내 러너를 추가해야 했습니다.
private class Runner : XunitTestAssemblyRunner
{
// constructor
public override void Dispose()
{
AvaloniaApp.Stop(); // cleanups existing avalonia app instance
base.Dispose();
}
// this method is called only if test parallelization is enabled. I had to enable it and set max parallelization limit to 1 in order to avoid parallel tests execution
protected override void SetupSyncContext(int maxParallelThreads)
{
var tcs = new TaskCompletionSource<SynchronizationContext>();
var thread = new Thread(() =>
{
try
{
// DI registrations
AvaloniaApp.RegisterDependencies();
AvaloniaApp
.BuildAvaloniaApp()
.AfterSetup(_ =>
{
// sets sync context for tests. avalonia UI runs in it's own single thread, updates from other threads are not allowed
tcs.SetResult(SynchronizationContext.Current);
})
.StartWithClassicDesktopLifetime(new string[0]); // run app as usual
}
catch (Exception e)
{
tcs.SetException(e);
}
})
{
IsBackground = true
};
thread.Start();
SynchronizationContext.SetSynchronizationContext(tcs.Task.Result);
}
}
Full code
One more headless tests example
또한 실제 앱의 래퍼인 AvaloniaApp
클래스를 추가했습니다.
public static class AvaloniaApp
{
// DI registrations
public static void RegisterDependencies() =>
Bootstrapper.Register(Locator.CurrentMutable, Locator.Current);
// stop app and cleanup
public static void Stop()
{
var app = GetApp();
if (app is IDisposable disposable)
{
Dispatcher.UIThread.Post(disposable.Dispose);
}
Dispatcher.UIThread.Post(() => app.Shutdown());
}
public static MainWindow GetMainWindow() => (MainWindow) GetApp().MainWindow;
public static IClassicDesktopStyleApplicationLifetime GetApp() =>
(IClassicDesktopStyleApplicationLifetime) Application.Current.ApplicationLifetime;
public static AppBuilder BuildAvaloniaApp() =>
AppBuilder
.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.UseHeadless(); // note that I run app as headless one
}
UI 테스트
이제 테스트를 작성할 시간입니다! 다음은 F1
를 통해 대화 상자를 여는 테스트의 예입니다.
public class OpenAboutDialogFlow : IDisposable
{
private AboutDialog _dialog;
[Fact(DisplayName = "Open about dialog")]
public async Task TestAboutDialog()
{
var app = AvaloniaApp.GetApp();
var window = AvaloniaApp.GetMainWindow();
// wait for initial setup
await Task.Delay(100);
Keyboard.PressKey(window, Key.Tab); // hack for focusing file panel, in headless tests it's not focused by default
Keyboard.PressKey(window, Key.Down);
Keyboard.PressKey(window, Key.F1); // press F1
await Task.Delay(100); // UI is not updated immediately so I had to add delays everywhere
_dialog = app
.Windows
.OfType<AboutDialog>()
.SingleOrDefault();
Assert.NotNull(_dialog);
await Task.Delay(100);
var githubButton = _dialog.GetVisualDescendants().OfType<Button>().SingleOrDefault();
Assert.NotNull(githubButton);
Assert.True(githubButton.IsDefault);
Assert.True(githubButton.Command.CanExecute(null));
}
public void Dispose() => _dialog?.Close(); // cleanup: close dialog
}
간단해 보이죠? 지연을 위해 몇 가지 추가 코드를 추가해야 했지만 실제로 테스트는 간단합니다. 다음은 지연 서비스의 예입니다.
public static class WaitService
{
public static async Task WaitForConditionAsync(Func<bool> condition, int delayMs = 50, int maxAttempts = 20)
{
for (var i = 0; i < maxAttempts; i++)
{
await Task.Delay(delayMs);
if (condition())
{
break; // stop waiting for condition
}
}
}
}
More tests examples
Official example
결론
AvalononiaUI는 UI 테스트를 실행하기 위한 좋은 인프라를 제공합니다. 많은 시간을 절약할 수 있기 때문에 충분히 복잡한 경우 프로젝트에서 사용하는 것이 좋습니다. 프로젝트에서 UI 테스트를 사용하고 있습니까? 댓글로 알려주세요
Reference
이 문제에 관하여(AvaloniaUI 앱에서 UI 테스트를 자동화하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/ingvarx/how-to-automate-ui-testing-in-avaloniaui-app-4an4
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
테스트를 위해 Xunit을 사용하는데 UI 테스트에서는 기본적으로 작동하지 않습니다. Xunit에 내 러너를 추가해야 했습니다.
private class Runner : XunitTestAssemblyRunner
{
// constructor
public override void Dispose()
{
AvaloniaApp.Stop(); // cleanups existing avalonia app instance
base.Dispose();
}
// this method is called only if test parallelization is enabled. I had to enable it and set max parallelization limit to 1 in order to avoid parallel tests execution
protected override void SetupSyncContext(int maxParallelThreads)
{
var tcs = new TaskCompletionSource<SynchronizationContext>();
var thread = new Thread(() =>
{
try
{
// DI registrations
AvaloniaApp.RegisterDependencies();
AvaloniaApp
.BuildAvaloniaApp()
.AfterSetup(_ =>
{
// sets sync context for tests. avalonia UI runs in it's own single thread, updates from other threads are not allowed
tcs.SetResult(SynchronizationContext.Current);
})
.StartWithClassicDesktopLifetime(new string[0]); // run app as usual
}
catch (Exception e)
{
tcs.SetException(e);
}
})
{
IsBackground = true
};
thread.Start();
SynchronizationContext.SetSynchronizationContext(tcs.Task.Result);
}
}
Full code
One more headless tests example
또한 실제 앱의 래퍼인
AvaloniaApp
클래스를 추가했습니다.public static class AvaloniaApp
{
// DI registrations
public static void RegisterDependencies() =>
Bootstrapper.Register(Locator.CurrentMutable, Locator.Current);
// stop app and cleanup
public static void Stop()
{
var app = GetApp();
if (app is IDisposable disposable)
{
Dispatcher.UIThread.Post(disposable.Dispose);
}
Dispatcher.UIThread.Post(() => app.Shutdown());
}
public static MainWindow GetMainWindow() => (MainWindow) GetApp().MainWindow;
public static IClassicDesktopStyleApplicationLifetime GetApp() =>
(IClassicDesktopStyleApplicationLifetime) Application.Current.ApplicationLifetime;
public static AppBuilder BuildAvaloniaApp() =>
AppBuilder
.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.UseHeadless(); // note that I run app as headless one
}
UI 테스트
이제 테스트를 작성할 시간입니다! 다음은 F1
를 통해 대화 상자를 여는 테스트의 예입니다.
public class OpenAboutDialogFlow : IDisposable
{
private AboutDialog _dialog;
[Fact(DisplayName = "Open about dialog")]
public async Task TestAboutDialog()
{
var app = AvaloniaApp.GetApp();
var window = AvaloniaApp.GetMainWindow();
// wait for initial setup
await Task.Delay(100);
Keyboard.PressKey(window, Key.Tab); // hack for focusing file panel, in headless tests it's not focused by default
Keyboard.PressKey(window, Key.Down);
Keyboard.PressKey(window, Key.F1); // press F1
await Task.Delay(100); // UI is not updated immediately so I had to add delays everywhere
_dialog = app
.Windows
.OfType<AboutDialog>()
.SingleOrDefault();
Assert.NotNull(_dialog);
await Task.Delay(100);
var githubButton = _dialog.GetVisualDescendants().OfType<Button>().SingleOrDefault();
Assert.NotNull(githubButton);
Assert.True(githubButton.IsDefault);
Assert.True(githubButton.Command.CanExecute(null));
}
public void Dispose() => _dialog?.Close(); // cleanup: close dialog
}
간단해 보이죠? 지연을 위해 몇 가지 추가 코드를 추가해야 했지만 실제로 테스트는 간단합니다. 다음은 지연 서비스의 예입니다.
public static class WaitService
{
public static async Task WaitForConditionAsync(Func<bool> condition, int delayMs = 50, int maxAttempts = 20)
{
for (var i = 0; i < maxAttempts; i++)
{
await Task.Delay(delayMs);
if (condition())
{
break; // stop waiting for condition
}
}
}
}
More tests examples
Official example
결론
AvalononiaUI는 UI 테스트를 실행하기 위한 좋은 인프라를 제공합니다. 많은 시간을 절약할 수 있기 때문에 충분히 복잡한 경우 프로젝트에서 사용하는 것이 좋습니다. 프로젝트에서 UI 테스트를 사용하고 있습니까? 댓글로 알려주세요
Reference
이 문제에 관하여(AvaloniaUI 앱에서 UI 테스트를 자동화하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/ingvarx/how-to-automate-ui-testing-in-avaloniaui-app-4an4
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
public class OpenAboutDialogFlow : IDisposable
{
private AboutDialog _dialog;
[Fact(DisplayName = "Open about dialog")]
public async Task TestAboutDialog()
{
var app = AvaloniaApp.GetApp();
var window = AvaloniaApp.GetMainWindow();
// wait for initial setup
await Task.Delay(100);
Keyboard.PressKey(window, Key.Tab); // hack for focusing file panel, in headless tests it's not focused by default
Keyboard.PressKey(window, Key.Down);
Keyboard.PressKey(window, Key.F1); // press F1
await Task.Delay(100); // UI is not updated immediately so I had to add delays everywhere
_dialog = app
.Windows
.OfType<AboutDialog>()
.SingleOrDefault();
Assert.NotNull(_dialog);
await Task.Delay(100);
var githubButton = _dialog.GetVisualDescendants().OfType<Button>().SingleOrDefault();
Assert.NotNull(githubButton);
Assert.True(githubButton.IsDefault);
Assert.True(githubButton.Command.CanExecute(null));
}
public void Dispose() => _dialog?.Close(); // cleanup: close dialog
}
public static class WaitService
{
public static async Task WaitForConditionAsync(Func<bool> condition, int delayMs = 50, int maxAttempts = 20)
{
for (var i = 0; i < maxAttempts; i++)
{
await Task.Delay(delayMs);
if (condition())
{
break; // stop waiting for condition
}
}
}
}
AvalononiaUI는 UI 테스트를 실행하기 위한 좋은 인프라를 제공합니다. 많은 시간을 절약할 수 있기 때문에 충분히 복잡한 경우 프로젝트에서 사용하는 것이 좋습니다. 프로젝트에서 UI 테스트를 사용하고 있습니까? 댓글로 알려주세요
Reference
이 문제에 관하여(AvaloniaUI 앱에서 UI 테스트를 자동화하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ingvarx/how-to-automate-ui-testing-in-avaloniaui-app-4an4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)