使用FakeItEasy无法设置列表类型返回值的问题求助
问题
我正在测试Web API控制器,但无法解决列表类型返回值的伪造问题。调试时发现ClassSql.ReadAllClassesAsync()的输出未被伪造,但测试单个值的ClassSql.ReadClassAsync()方法时伪造功能正常,相关测试都能通过。
控制器代码:
public class ClassController : ControllerBase { private readonly IClassSqlService ClassSql; private readonly ILogger<ClassController> Logger; public ClassController( IConfiguration config, ILogger<ClassController> logger, IDataAccessAbstractFactory<IClassSqlService> classSql) { string SMString = config.GetValue<string>(DataAccessName.SMCnx)!; Logger = logger; ClassSql = classSql.Create(SMString); } [HttpGet] [ProducesResponseType(200, Type = typeof(List<ClassModel>))] public async Task<ActionResult<List<ClassModel>>> ReadAllClasses( string? academic_year = null) { List<ClassModel> output; if(academic_year is not null) { bool isIdValid = int.TryParse(academic_year, out int year); if (isIdValid is false) return BadRequest(); output = await ClassSql.ReadAllClassesByAcademicYearAsync(academic_year); } else { output = await ClassSql.ReadAllClassesAsync(); } if (output.Any() is false) return NotFound(); return Ok(output); } }
单元测试代码:
public class ClassControllerTests { private readonly IClassSqlService ClassSql; private readonly ClassController sut; public ClassControllerTests() { var config = A.Fake<IConfiguration>(); var classSql = A.Fake<IDataAccessAbstractFactory<IClassSqlService>>(); var logger = A.Fake<ILogger<ClassController>>(); sut = new ClassController(config, logger, classSql); ClassSql = classSql.Create(string.Empty); } [Fact] public async Task ReadAllClassesShouldReturnOk() { // arrange var classes = A.CollectionOfFake<ClassModel>(5); A.CallTo(() => ClassSql.ReadAllClassesAsync()).Returns(classes.ToList()); // action var result = await sut.ReadAllClasses(); // assert result.Result.Should().NotBeNull(); result.Result.Should().BeOfType(typeof(OkObjectResult)); result.Value.Should().NotBeEmpty(); } }
我已经查阅FakeItEasy文档并参照伪造集合的示例操作,但问题仍未解决,希望得到帮助。
解决方案
核心问题是:控制器构造函数中调用classSql.Create(SMString)时使用的是从配置获取的连接字符串,而测试代码中调用classSql.Create(string.Empty)得到的是另一个Fake实例。FakeItEasy默认会根据调用参数的不同生成不同的Fake对象,导致控制器实际使用的IClassSqlService和你在测试中配置伪造调用的实例不是同一个,伪造的返回值自然不会生效。
修改测试代码,确保控制器和测试使用同一个Fake的IClassSqlService实例:
public class ClassControllerTests { private readonly IClassSqlService _classSql; private readonly ClassController _sut; public ClassControllerTests() { var config = A.Fake<IConfiguration>(); // 预先创建Fake的IClassSqlService实例 _classSql = A.Fake<IClassSqlService>(); var classSqlFactory = A.Fake<IDataAccessAbstractFactory<IClassSqlService>>(); var logger = A.Fake<ILogger<ClassController>>(); // 配置工厂:无论传入什么连接字符串,都返回预先创建的Fake实例 A.CallTo(() => classSqlFactory.Create(A<string>._)).Returns(_classSql); // 可选:如果需要验证连接字符串的读取,可以配置config的返回值 A.CallTo(() => config.GetValue<string>(DataAccessName.SMCnx)).Returns("test_connection_string"); _sut = new ClassController(config, logger, classSqlFactory); } [Fact] public async Task ReadAllClassesShouldReturnOk() { // arrange var fakeClasses = A.CollectionOfFake<ClassModel>(5).ToList(); A.CallTo(() => _classSql.ReadAllClassesAsync()).Returns(fakeClasses); // action var result = await _sut.ReadAllClasses(); // assert result.Result.Should().BeOfType<OkObjectResult>(); result.Value.Should().BeEquivalentTo(fakeClasses); } }
关键修改点:
- 提前创建Fake的
IClassSqlService实例,而不是通过工厂的Create方法获取 - 配置Fake工厂的
Create方法,使用A<string>._匹配任意字符串参数,确保无论控制器传入什么连接字符串,都返回同一个Fake实例 - (可选)配置config的返回值,模拟真实环境中连接字符串的读取
这样修改后,控制器使用的IClassSqlService和测试中配置伪造调用的实例是同一个,伪造的返回值会正常生效,测试就能通过了。
内容的提问来源于stack exchange,提问作者user22019886
相关产品推荐
相关产品推荐

