8.7 KiB
8.7 KiB
Testing Strategy
本节定义控件库的测试方法和组织策略,确保核心功能的稳定性和可靠性。
Testing Pyramid
E2E Tests (手动测试)
/ \
Integration Tests (可选)
/ \
Unit Tests (核心逻辑)
测试重点分配:
- Unit Tests (70%):核心业务逻辑、数据模型、工具类
- Integration Tests (可选):控件与 ReactiveUI 的集成、主题系统
- Manual Tests (必须):UI 渲染、主题切换、用户交互
MVP 阶段不包括:
- UI 自动化测试(Avalonia 提供 Headless 测试包,但 MVP 阶段依赖手动测试)
- 性能基准测试(除非发现明显性能问题)
- 跨平台兼容性测试(仅在 Windows 上测试)
Test Organization
测试项目结构:
Penguin.AvaloniaUI.Tests/
├── Controls/
│ ├── PropertyGridTests.cs
│ ├── UserGuideTests.cs
│ └── ...
├── Layouts/
│ └── TwoColumnLayoutTests.cs
├── Themes/
│ └── ThemeManagerTests.cs
├── Utils/
│ ├── ReflectionHelperTests.cs
│ └── Converters/
│ └── TypeToEditorConverterTests.cs
└── TestHelpers/
├── MockObjects.cs
└── TestBase.cs
命名约定:
- 测试类:
{ClassName}Tests - 测试方法:
{MethodName}_{Scenario}_{ExpectedResult}
Unit Test Examples
示例 1:测试 PropertyGrid 的反射逻辑
namespace Penguin.AvaloniaUI.Tests.Controls;
public class PropertyGridTests
{
[Fact]
public void RefreshProperties_WithValidObject_ShouldParseAllPublicProperties()
{
// Arrange
var testObject = new TestSettings
{
Name = "Test",
Age = 25,
IsEnabled = true
};
var propertyGrid = new PropertyGrid
{
SelectedObject = testObject
};
// Act
var properties = propertyGrid.Properties;
// Assert
Assert.Equal(3, properties.Count);
Assert.Contains(properties, p => p.Name == "Name" && p.PropertyType == typeof(string));
Assert.Contains(properties, p => p.Name == "Age" && p.PropertyType == typeof(int));
Assert.Contains(properties, p => p.Name == "IsEnabled" && p.PropertyType == typeof(bool));
}
[Fact]
public void RefreshProperties_WithCategoryAttribute_ShouldGroupByCategory()
{
// Arrange
var testObject = new CategorizedSettings();
var propertyGrid = new PropertyGrid
{
SelectedObject = testObject
};
// Act
var properties = propertyGrid.Properties;
var generalCategory = properties.Where(p => p.Category == "General").ToList();
var advancedCategory = properties.Where(p => p.Category == "Advanced").ToList();
// Assert
Assert.NotEmpty(generalCategory);
Assert.NotEmpty(advancedCategory);
}
[Fact]
public void RefreshProperties_WithNullObject_ShouldClearProperties()
{
// Arrange
var propertyGrid = new PropertyGrid
{
SelectedObject = new TestSettings()
};
// Act
propertyGrid.SelectedObject = null;
// Assert
Assert.Empty(propertyGrid.Properties);
}
}
示例 2:测试 ThemeManager
namespace Penguin.AvaloniaUI.Tests.Themes;
public class ThemeManagerTests
{
[Fact]
public void ApplyTheme_WithLightTheme_ShouldLoadLightResources()
{
// Arrange
var app = CreateTestApplication();
// Act
ThemeManager.ApplyTheme(ThemeType.Light);
// Assert
var textPrimary = app.Resources["TextPrimary"] as SolidColorBrush;
Assert.NotNull(textPrimary);
// 浅色主题的主要文本应为深色
Assert.True(textPrimary.Color.R < 128);
}
[Fact]
public void ApplyTheme_SwitchingThemes_ShouldTriggerThemeChangedEvent()
{
// Arrange
var eventTriggered = false;
ThemeManager.ThemeChanged += (sender, type) => eventTriggered = true;
// Act
ThemeManager.ApplyTheme(ThemeType.Dark);
// Assert
Assert.True(eventTriggered);
}
private Application CreateTestApplication()
{
// 创建测试用的 Application 实例
return new Application();
}
}
示例 3:测试 UserGuide 步骤流转
namespace Penguin.AvaloniaUI.Tests.Controls;
public class UserGuideTests
{
[Fact]
public void NextStepCommand_WhenNotLastStep_ShouldIncrementIndex()
{
// Arrange
var userGuide = new UserGuide();
userGuide.Steps.Add(new GuideStep { Title = "Step 1", Order = 0 });
userGuide.Steps.Add(new GuideStep { Title = "Step 2", Order = 1 });
userGuide.Steps.Add(new GuideStep { Title = "Step 3", Order = 2 });
// Act
userGuide.NextStepCommand.Execute(null);
// Assert
Assert.Equal(1, userGuide.CurrentStepIndex);
}
[Fact]
public void NextStepCommand_WhenLastStep_ShouldTriggerCompleteEvent()
{
// Arrange
var userGuide = new UserGuide();
userGuide.Steps.Add(new GuideStep { Title = "Step 1", Order = 0 });
var completedTriggered = false;
userGuide.Completed += (sender, args) => completedTriggered = true;
// Act
userGuide.NextStepCommand.Execute(null);
// Assert
Assert.True(completedTriggered);
}
[Fact]
public void PreviousStepCommand_WhenFirstStep_ShouldNotExecute()
{
// Arrange
var userGuide = new UserGuide();
userGuide.Steps.Add(new GuideStep { Title = "Step 1", Order = 0 });
// Act
var canExecute = userGuide.PreviousStepCommand.CanExecute(null);
// Assert
Assert.False(canExecute);
}
}
Manual Testing Checklist
以下场景必须通过示例应用手动测试:
PropertyGrid:
- 绑定包含 6 种基础类型的对象,所有编辑器正确显示
- 修改属性值,SelectedObject 对象同步更新
- 只读属性禁用编辑器或显示为灰色
- 属性按 Category 分组显示,分组标题清晰
- 主题切换时,PropertyGrid 颜色正确更新
- 50 个属性的生成时间 < 200ms(使用秒表或 Stopwatch 测量)
UserGuide:
- 引导流程正确显示 Overlay 遮罩
- 目标控件区域挖空显示(清晰可见)
- 提示框在目标控件附近正确定位(Top/Bottom/Left/Right)
- "下一步"/"上一步"按钮功能正常
- 步骤进度显示正确(如 "2/5")
- "跳过"和"完成"按钮触发完成事件
主题系统:
- 主题切换在 100ms 内完成,无闪烁
- 所有控件的颜色正确响应主题变化
- 浅色和暗色主题的对比度舒适(文本清晰可读)
- 主题切换时无控件错位或布局异常
跨功能集成:
- 在 PropertyGrid 编辑时切换主题,无崩溃
- 在 UserGuide 进行中修改 PropertyGrid,无异常
- 窗口缩放时,所有控件布局正确调整
Test Helpers
创建测试辅助类减少重复代码:
namespace Penguin.AvaloniaUI.Tests.TestHelpers;
public static class MockObjects
{
public class TestSettings
{
public string Name { get; set; } = "Default";
public int Age { get; set; } = 0;
public bool IsEnabled { get; set; } = false;
}
public class CategorizedSettings
{
[Category("General")]
public string Name { get; set; } = "Default";
[Category("Advanced")]
public int Timeout { get; set; } = 5000;
}
}
public class TestBase
{
protected Application CreateTestApplication()
{
var app = new Application();
// 加载测试用主题资源
return app;
}
}
Running Tests
# 运行所有测试
dotnet test
# 运行特定测试类
dotnet test --filter "FullyQualifiedName~PropertyGridTests"
# 运行特定测试方法
dotnet test --filter "Name~RefreshProperties_WithValidObject"
# 生成代码覆盖率报告
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=html
Visual Studio 测试运行器:
- 打开 Test Explorer(测试 → 测试资源管理器)
- 点击"运行所有测试"或右键运行特定测试
- 查看测试结果和失败详情