AutoFixture Without()方法在单全参构造函数下失效的原因及解决办法
定义的Foo类代码如下:
public class Foo { public Foo(string id, string name, string? homeTown = null, IEnumerable<string>? someCollection = null) { Id = id; Name = name; HomeTown = homeTown; SomeCollection = someCollection; } public string Id { get; set; } public string Name {get; set;} public string? HomeTown {get; set;} public IEnumerable<string>? SomeCollection {get; set;} }
希望使用AutoFixture创建Foo实例时,不填充HomeTown和SomeCollection属性,但执行以下代码后,这两个属性仍被自动填充:
Fixture fixture = new Fixture(); var dto = fixture.Build<Foo>() .Without(x => x.HomeTown) .Without(x => x.SomeCollection) .Create();
不想为满足测试需求新增构造函数,询问该行为的原因、是否是Bug以及可行的解决办法。
AutoFixture的Build<T>().Without()方法默认仅对公共无参构造函数生成的对象生效,作用是阻止AutoFixture对属性进行后续赋值。但你的Foo类只有一个带可选参数的构造函数,AutoFixture的设计逻辑是优先选择参数数量最多的构造函数,并且会把可选参数视为必填项自动生成值传入构造函数——这时候HomeTown和SomeCollection的值已经在构造阶段被初始化了,后续的Without()规则根本不会触发。
这不是Bug,是AutoFixture的核心设计逻辑导致的:它的目标是尽可能生成"完整"的对象实例,所以会优先使用参数最多的构造函数,并且为所有构造函数参数(包括可选参数)自动生成值。Without()方法的初衷是排除那些通过属性setter赋值的成员,而非构造函数参数对应的属性。
以下几种方案都不需要新增构造函数:
方案1:关闭自动属性赋值,手动指定必填项
通过OmitAutoProperties()关闭所有属性的自动赋值逻辑,再手动指定需要填充的Id和Name,这样HomeTown和SomeCollection会保持构造函数的默认值null:
var fixture = new Fixture(); var dto = fixture.Build<Foo>() .OmitAutoProperties() .With(x => x.Id, fixture.Create<string>()) .With(x => x.Name, fixture.Create<string>()) .Create();
方案2:显式指定可选参数使用默认值
直接通过With()方法告诉AutoFixture,为HomeTown和SomeCollection对应的构造函数参数使用默认的null,覆盖自动生成行为:
var fixture = new Fixture(); var dto = fixture.Build<Foo>() .With(x => x.HomeTown, (string?)null) .With(x => x.SomeCollection, (IEnumerable<string>?)null) .Create();
方案3:全局配置忽略规则(适合重复使用场景)
如果需要在多个测试中复用这个规则,可以全局配置AutoFixture:
var fixture = new Fixture(); fixture.Customize<Foo>(c => c .Without(x => x.HomeTown) .Without(x => x.SomeCollection)); // 后续创建Foo实例都会应用该规则 var dto = fixture.Create<Foo>();
内容的提问来源于stack exchange,提问作者MiBuena

