x使用UI的单元测试方法



我是C#的新手。我一直在尝试为下面的方法编写xUnit测试("ExecuteExportCsvCommand(("(。但是,正如您在下面看到的,它有一些UI控件。因此,我不知道如何测试该功能。如果有人能解释,我会非常感激。

public void ExecuteExportCsvCommand()
{
ExecuteCancelPopOutWindowCommand();
var previousState = View.IsEnabled;
View.IsEnabled = false;
try
{
var exportDialog = new ExportPrintDataDialog();
var result = exportDialog.ShowDialog();
if (result.GetValueOrDefault())
{
var projectData = _projectService.GetProject3dObject(Project.Id);
IEnumerable<object> orderedList;
orderedList = projectData.Select((x, i) => new ExportData {
Index = i,
Time = x.Time,
LayerNumber = x.LayerNumber,
X = x.X,
Y = x.Y,
Z = x.Z,
ArcVoltage = x.ArcVoltage,
Current = x.Current,
WireFeedSpeed = x.WireFeedSpeed,
WireFeedSpeedB = x.WireFeedSpeedB,
RatioWireFeedSpeed = (x.WireFeedSpeed == 0) ? 0 : x.WireFeedSpeedB / x.WireFeedSpeed,
BuildHeight = x.WallHeight,
LeadTemperature = x.LeadTemperature,
TrailTemperature = x.TrailTemperature,
OxygenLevel = x.OxygenLevel,
GasFlowA = x.GasFlow.First(),
GasFlowB = x.GasFlow.Last(),
TempChannels = x.TempChannels //preferably last as array of 8 values
}).ToList();
//Define Column headers
var heightHeader = _options.CorriOptions.Value.UseWallHeight ? "Wall Height" : "Layer Height";
var wfsHeader = Project.ProcessEnum == EnumProjectProcess.CW_MIG 
? new []{ "Hot Wire Feed Speed", "Cold Wire Feed Speed" } 
: new[] { "Wire Feed Speed 1", "Wire Feed Speed 2" };
var headers = new Dictionary<string, string>()
{
{ "Index" , "Ordinal Number" },
{ "Time" , "Time" },
{ "LayerNumber" , "Layer Number" },
{ "X" , "Pos X" },
{ "Y" , "Pos Y" },
{ "Z" , "Pos Z" },
{ "ArcVoltage" , "Arc Voltage" },
{ "Current" , "Current" },
{ "WireFeedSpeed" , wfsHeader.First() },
{ "WireFeedSpeedB" , wfsHeader.Last()  },
{"RatioWireFeedSpeed", "R-Ratio"},
{ "BuildHeight" , heightHeader },
{ "LeadTemperature" , "Lead Temperature" },
{ "TrailTemperature" , "Trail Temperature" },
{ "OxygenLevel" , "Oxygen Level" },
{ "GasFlowA" , "GasFlow Channel A" },
{ "GasFlowB" , "GasFlow Channel B" },
{ "TempChannels" , "End Effector Temperature (Channels 1 - 8)" }
};
var saveFileDialog = new SaveFileDialog
{
Filter = "csv files (*.csv)|*.csv|All files (*.*)|*.*",
FilterIndex = 1,
RestoreDirectory = true,
FileName = string.Concat(DateTime.Now.ToString("yyyy-MM-ddTHHmm"), "_log"),
};
if (saveFileDialog.ShowDialog().GetValueOrDefault())
{
using var writer = new StreamWriter(saveFileDialog.FileName);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.Context.RegisterClassMap(new ExportDataMapper(headers));
csv.WriteRecords(orderedList);
}
}
}
finally
{
View.IsEnabled = previousState;
ExportDataBtnChecked = false;
}
}

测试环境已经设置好了,如下所示。我只需要为上面提到的方法编写测试。如果你需要更多的细节,请告诉我。谢谢

public class ProjectPrintViewModelTests
{
private readonly ProjectPrintViewModel _sut;
private readonly IHost _host;
private readonly Mock<IPermissionChecker> _permissionChecker = new Mock<IPermissionChecker>();
private readonly Mock<ILogService> _logService = new Mock<ILogService>();
private readonly Mock<IAuditService> _auditService = new Mock<IAuditService>();
private readonly Mock<IProjectService> _projectService = new Mock<IProjectService>();
public ProjectPrintViewModelTests()
{
var colorList = new List<string> {
"DefaultMinColor",
"DefaultLowColor",
"DefaultNormColor",
"DefaultHighColor",
"DefaultMaxColor"
};
var app = new Application();
colorList.ForEach(x => app.Resources.Add(x, Color.FromRgb(255, 255, 255)));
_host = MockEnvironment.BuildHost();
IoCContainer.SetServiceProvider(_host.Services);

var options = _host.Services.GetRequiredService<OptionsContainer>();
var valueService = _host.Services.GetRequiredService<IValuePointService>();
var project = new ProjectViewModel(options);

_sut = new ProjectPrintViewModel(
options,
project,
_logService.Object,
_permissionChecker.Object,
_auditService.Object,
_projectService.Object,
valueService,
false);
}
/// Write Tests Below Here
[Fact]
public void ExecuteCsvFileCommand_WhenValueAreValid()
{
//Assemble
//Act
_sut.ExecuteExportCsvCommand();
//Assert

这是设计糟糕和架构不干净的诸多缺点之一。。将UI代码与业务逻辑分离可以帮助您很好地测试业务逻辑。

请记住,单元测试应该尽可能快地运行/返回。显然,显示SaveFileDialog将停止测试,直到用户选择路径,这是错误的!

您应该将所有与UI相关的代码提取到一个单独的组件中,该组件提供UI服务,例如:

public interface IUiServices
{
void CancelPopOutWindow();
void SaveViewState();
void RestoreViewState();
bool ShowExportDialog();
string ShowSaveFileDialog();
}

最重要的是,在测试时,你会嘲笑UIServices不做任何真正的UI工作(例如,在SaveFileDialog中,它只会返回一个有效的测试路径来保存文件(。。所以ExecuteExportCsvCommand可以是这样的。。


public void ExecuteExportCsvCommand()
{
_uiServices.CancelPopOutWindow();
_uiServices.SaveViewState();
try
{
var b = _uiServices.ShowExportDialog();

if (b)
{
var projectData = _projectService.GetProject3dObject(Project.Id);
IEnumerable<object> orderedList;
orderedList = projectData.Select((x, i) => new ExportData {
Index = i,
Time = x.Time,
LayerNumber = x.LayerNumber,
X = x.X,
Y = x.Y,
Z = x.Z,
ArcVoltage = x.ArcVoltage,
Current = x.Current,
WireFeedSpeed = x.WireFeedSpeed,
WireFeedSpeedB = x.WireFeedSpeedB,
RatioWireFeedSpeed = (x.WireFeedSpeed == 0) ? 0 : x.WireFeedSpeedB / x.WireFeedSpeed,
BuildHeight = x.WallHeight,
LeadTemperature = x.LeadTemperature,
TrailTemperature = x.TrailTemperature,
OxygenLevel = x.OxygenLevel,
GasFlowA = x.GasFlow.First(),
GasFlowB = x.GasFlow.Last(),
TempChannels = x.TempChannels //preferably last as array of 8 values
}).ToList();
//Define Column headers
var heightHeader = _options.CorriOptions.Value.UseWallHeight ? "Wall Height" : "Layer Height";
var wfsHeader = Project.ProcessEnum == EnumProjectProcess.CW_MIG 
? new []{ "Hot Wire Feed Speed", "Cold Wire Feed Speed" } 
: new[] { "Wire Feed Speed 1", "Wire Feed Speed 2" };
var headers = new Dictionary<string, string>()
{
{ "Index" , "Ordinal Number" },
{ "Time" , "Time" },
{ "LayerNumber" , "Layer Number" },
{ "X" , "Pos X" },
{ "Y" , "Pos Y" },
{ "Z" , "Pos Z" },
{ "ArcVoltage" , "Arc Voltage" },
{ "Current" , "Current" },
{ "WireFeedSpeed" , wfsHeader.First() },
{ "WireFeedSpeedB" , wfsHeader.Last()  },
{"RatioWireFeedSpeed", "R-Ratio"},
{ "BuildHeight" , heightHeader },
{ "LeadTemperature" , "Lead Temperature" },
{ "TrailTemperature" , "Trail Temperature" },
{ "OxygenLevel" , "Oxygen Level" },
{ "GasFlowA" , "GasFlow Channel A" },
{ "GasFlowB" , "GasFlow Channel B" },
{ "TempChannels" , "End Effector Temperature (Channels 1 - 8)" }
};
string path = _uiServices.ShowSaveFileDialog();

if (!string.IsNullOrEmpty(path))
{
using var writer = new StreamWriter(path);
using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
csv.Context.RegisterClassMap(new ExportDataMapper(headers));
csv.WriteRecords(orderedList);
}
}
}
finally
{
_uiServices.RestoreViewState();
ExportDataBtnChecked = false;
}
}

最新更新