如何合并不同参数类型的测试验证方法以减少重复代码?
解决方案
针对你提到的重复代码问题,有几种可行的代码复用方案,具体如下:
1. 泛型方法统一实现(最推荐)
既然两个方法的验证逻辑完全一致,只是参数类型不同,用C#泛型可以直接将逻辑抽离成一个通用方法,再通过重载兼容原有调用(不需要修改TestSteps里的代码)。
实现步骤:
- 在BaseTest.cs中定义泛型验证方法
- 保留原有的两个方法作为重载,内部调用泛型方法(或者直接删除原有方法,让TestSteps直接调用泛型方法)
示例代码:
// 通用泛型验证方法 public void ValidateResponse<T>(List<T> actualResponse, List<T> expectedResponse, FilterParams filterParams) { // 这里写原来重复的验证逻辑,比如遍历对比属性、过滤参数处理等 foreach (var actualItem in actualResponse) { var expectedItem = expectedResponse.First(e => GetId(e) == GetId(actualItem)); Assert.AreEqual(GetValue(expectedItem), GetValue(actualItem)); // 其他验证逻辑... } // 若两个类型属性访问方式不同,可加辅助方法适配 int GetId(T item) => item switch { DataReadable dr => dr.Id, SensitiveDataReadable sdr => sdr.SensitiveId, _ => throw new ArgumentException("不支持的类型") }; string GetValue(T item) => item switch { DataReadable dr => dr.Content, SensitiveDataReadable sdr => sdr.SensitiveContent, _ => throw new ArgumentException("不支持的类型") }; } // 原有方法1,兼容旧调用 public void ValidateResponse(List<DataReadable> actualResponse, List<DataReadable> expectedResponse, FilterParams filterParams) { ValidateResponse<DataReadable>(actualResponse, expectedResponse, filterParams); } // 原有方法2,兼容旧调用 public void ValidateSensitiveResponse(List<SensitiveDataReadable> actualResponse, List<SensitiveDataReadable> expectedResponse, FilterParams filterParams) { ValidateResponse<SensitiveDataReadable>(actualResponse, expectedResponse, filterParams); }
这样TestSteps.cs里的原有调用完全不用改,同时实现了代码复用。如果两个类型的属性完全一致,甚至不需要辅助方法,直接在泛型方法里访问属性即可。
2. 抽象共同接口/基类
如果DataReadable和SensitiveDataReadable可以抽象出共同的接口或基类(比如包含所有验证需要的属性),可以直接将方法参数统一为这个接口/基类的集合。
实现步骤:
- 定义一个包含验证所需属性的接口(比如
IReadable) - 让
DataReadable和SensitiveDataReadable实现该接口 - 修改验证方法的参数为
IEnumerable<IReadable>(支持泛型协变,可直接接收两种List类型)
示例代码:
// 定义共同接口 public interface IReadable { int Id { get; } string Content { get; } // 其他验证需要的属性 } // 让两个类型实现接口 public class DataReadable : IReadable { /* 实现接口属性 */ } public class SensitiveDataReadable : IReadable { /* 实现接口属性 */ } // 统一验证方法 public void ValidateResponse(IEnumerable<IReadable> actualResponse, IEnumerable<IReadable> expectedResponse, FilterParams filterParams) { // 通用验证逻辑 foreach (var actualItem in actualResponse) { var expectedItem = expectedResponse.First(e => e.Id == actualItem.Id); Assert.AreEqual(expectedItem.Content, actualItem.Content); } }
这种方式更简洁,但需要修改原有类型的定义,如果有权限调整类型结构,这是最优解之一。
3. 类型转换/映射后调用原有方法
如果无法修改原有类型或使用泛型,可以在方法2中把SensitiveDataReadable转换为DataReadable(或反之),再调用方法1。
实现步骤:
- 手动映射或通过转换运算符将一个类型转为另一个类型
- 在方法2中完成转换后调用方法1
示例代码:
// 方法2调用方法1,通过类型映射 public void ValidateSensitiveResponse(List<SensitiveDataReadable> actualResponse, List<SensitiveDataReadable> expectedResponse, FilterParams filterParams) { // 手动映射属性 var convertedActual = actualResponse.Select(sdr => new DataReadable { Id = sdr.Id, Content = sdr.SensitiveContent // 其他属性对应映射 }).ToList(); var convertedExpected = expectedResponse.Select(sdr => new DataReadable { Id = sdr.Id, Content = sdr.SensitiveContent }).ToList(); // 调用原有方法1 ValidateResponse(convertedActual, convertedExpected, filterParams); }
这种方式适合临时兼容场景,但映射逻辑需要维护,如果两个类型结构变化,映射代码也要跟着改,优先级低于前两种方案。
内容的提问来源于stack exchange,提问作者Prateek Kumar
相关产品推荐
相关产品推荐

