如何在SpecFlow步骤定义表格中为未提供的列设置默认值
如何在SpecFlow步骤定义表格中为未提供的列设置默认值?
场景示例
特性文件(Feature File):
@regression @add-a-new-cat Feature: Add a new cat As a User I should be able to add a cat # Vocabulary: # User: An authorized application user @positive @smoke-test Scenario: Add a new cat Given the User is authorized to add a new cat When the User adds new cat | CatName | Age | Breed | | Kitty | 5 | Indian | | Lucy | 5 | Persian | Then the cat is added successfully
理想的步骤定义:
[When(@"the User adds new cat")] public async void WhenTheUserAddsNewCatEndpointAsync(Table table) { var addCats = table.CreateSet<CatDetails>(); var request = new RestRequest("cats"); request.AddBody(addCats); restResponse = await restSharpRetryDriver.restClient.ExecutePostAsync(request); }
需求说明
当特性文件的数据表格未包含所有列时,需要为缺失的列自动填充默认值。比如表格只提供CatName和Age时,Breed列要自动使用预设的默认值。
已知的基础方案
方案一:遍历集合手动赋值
[When(@"the User adds new cat")] public async void WhenTheUserAddsNewCatEndpointAsync(Table table) { var addCats = table.CreateSet<CatDetails>(); foreach (var row in addCats) { if (row.Breed == null) { row.Breed = "persian"; // 当特性文件未传入该列时赋值默认值 } } var request = new RestRequest("cats"); request.AddBody(addCats); restResponse = await restSharpRetryDriver.restClient.ExecutePostAsync(request); }
方案二:在实体类的属性getter中设置默认值
public class AddCatSteps { public class CatDetails { [TableAliases("Name", "CatName")] public string Name { get; set; } public int Age { get; set; } string _breed; public string Breed { get { return _breed ?? "newBreed"; } set { _breed = value; } } } [When(@"the User adds new cat")] public async void WhenTheUserAddsNewCatEndpointAsync(Table table) { var addCats = table.CreateSet<CatDetails>(); var request = new RestRequest("cats"); request.AddBody(addCats); restResponse = await restSharpRetryDriver.restClient.ExecutePostAsync(request); } }
以上两种方案在涉及多列需要设置默认值时,会产生大量重复代码,不够简洁,下面是更优的解决方案:
更优解决方案
方案三:利用实体类的默认构造函数初始化默认值
在CatDetails的构造函数中直接为属性设置默认值,SpecFlow的CreateSet<T>方法在创建实例时会调用默认构造函数,未被表格赋值的属性会保留构造函数中的默认值。
public class CatDetails { [TableAliases("Name", "CatName")] public string Name { get; set; } public int Age { get; set; } public string Breed { get; set; } public string Color { get; set; } // 新增需要默认值的列 // 默认构造函数初始化默认值 public CatDetails() { Breed = "persian"; Color = "white"; } }
步骤定义无需修改,保持最初的简洁写法即可:
[When(@"the User adds new cat")] public async void WhenTheUserAddsNewCatEndpointAsync(Table table) { var addCats = table.CreateSet<CatDetails>(); var request = new RestRequest("cats"); request.AddBody(addCats); restResponse = await restSharpRetryDriver.restClient.ExecutePostAsync(request); }
方案四:自定义Table扩展方法,批量注入默认值
如果需要更灵活的默认值配置(比如不同场景用不同默认值),可以编写一个扩展方法,统一处理表格转换后的实体默认值填充:
public static class TableExtensions { public static IEnumerable<T> CreateSetWithDefaults<T>(this Table table, Action<T> defaultInitializer) where T : new() { var items = table.CreateSet<T>(); foreach (var item in items) { defaultInitializer(item); } return items; } }
使用时在步骤定义中指定默认值逻辑:
[When(@"the User adds new cat")] public async void WhenTheUserAddsNewCatEndpointAsync(Table table) { var addCats = table.CreateSetWithDefaults<CatDetails>(cat => { cat.Breed = cat.Breed ?? "persian"; cat.Color = cat.Color ?? "white"; // 其他列的默认值逻辑 }); var request = new RestRequest("cats"); request.AddBody(addCats); restResponse = await restSharpRetryDriver.restClient.ExecutePostAsync(request); }
这种方式的优势是可以针对不同步骤场景设置不同的默认值,同时避免在实体类中硬编码默认值,更具灵活性。
方案五:使用AutoMapper映射表格数据并设置默认值
如果项目中已经使用AutoMapper,可以通过配置映射规则来处理默认值:
- 配置AutoMapper映射:
var configuration = new MapperConfiguration(cfg => { cfg.CreateMap<TableRow, CatDetails>() .ForMember(dest => dest.Breed, opt => opt.MapFrom(src => src["Breed"] ?? "persian")) .ForMember(dest => dest.Color, opt => opt.MapFrom(src => src["Color"] ?? "white")) .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src["CatName"] ?? src["Name"])); }); IMapper mapper = configuration.CreateMapper();
- 步骤定义中使用AutoMapper转换表格数据:
[When(@"the User adds new cat")] public async void WhenTheUserAddsNewCatEndpointAsync(Table table) { var addCats = table.Rows.Select(row => mapper.Map<CatDetails>(row)); var request = new RestRequest("cats"); request.AddBody(addCats); restResponse = await restSharpRetryDriver.restClient.ExecutePostAsync(request); }
这种方式适合复杂的映射场景,同时可以统一管理所有实体的映射规则和默认值逻辑。
内容的提问来源于stack exchange,提问作者deepalee khare
相关产品推荐
相关产品推荐

