Fixture.Customize 对比 Fixture.Register

Fixture.Customize vs Fixture.Register

请解释自定义和注册之间的功能差异,何时使用其中一个。下面的 TestCustomize 示例失败,TestRegister 通过。我希望自定义脚本能够正常工作。它用英语读给我: "When generating an HttpClient, use a post processing lambda on it before providing the specimin".

但我得到的是一个带有 guid 的 http 地址,显然是由 AutoFixture 生成的。

[Fact]
public void TestCustomize()
{
    var fixture = new Fixture();
    fixture.Customize<HttpClient>(c =>
    {
        //c.OmitAutoProperties(); makes no difference
        c.Do(x => x.BaseAddress = new Uri("http://myval"));
        return c;
    });
    var client = fixture.Create<HttpClient>();
    Assert.Equal("http://myval/", client.BaseAddress.ToString());
}

[Fact]
public void TestRegister()
{
    var fixture = new Fixture();
    fixture.Register(() => new HttpClient
    {
        BaseAddress = new Uri("http://myval")
    });
    var client = fixture.Create<HttpClient>();
    Assert.Equal("http://myval/", client.BaseAddress.ToString());
}

这与 CustomizeRegister 无关。事实上,如果您查看 source code for Register,您会发现它只是 Customize.

的一种便捷方法

问题出在Do的使用上。如果您查看 Do 的签名,您会发现它具有以下类型:

IPostprocessComposer<T> Do(Action<T> action)

请注意方法 return 是一个值。在函数式风格中,或者,如果你愿意,遵循 Command-Query Separation,该方法不会改变定义它的实例,而是 return 一个具有改变行为的新对象。

当一个人写:

c.Do(x => x.BaseAddress = new Uri("http://myval"));

一个人立即丢弃具有更改行为的 return 值。在 F# 或 Haskell 等语言中,如果您编写这样的代码,您会收到编译时通知,告诉您您将忽略函数调用的 return 值,但 C# 不会不要那样做。

无论如何,AutoFixture 的 API 都被设计成 fluent interfaces。目的是将方法调用链接在一起:

fixture.Customize<HttpClient>(c => c
    .Without(x => x.BaseAddress)
    .Do(x => x.BaseAddress = new Uri("http://myval")));

您仍然需要 Without(或者,如果您愿意,OmitAutoProperties)来关闭默认的自动 属性 行为,否则,BaseAddress 将是被自动 属性 功能覆盖。

本版本测试通过:

[Fact]
public void TestCustomize()
{
    var fixture = new Fixture();
    fixture.Customize<HttpClient>(c => c
        .Without(x => x.BaseAddress)
        .Do(x => x.BaseAddress = new Uri("http://myval")));

    var client = fixture.Create<HttpClient>();

    Assert.Equal("http://myval/", client.BaseAddress.ToString());
}