【问题标题】:Easy way to specify the value of a single constructor parameter?指定单个构造函数参数值的简单方法?
【发布时间】:2015-02-05 17:13:40
【问题描述】:

在 Autofixture 中是否有一些简单的方法可以使用它的构造函数来新建对象,但硬编码/指定要用于构造函数中单个参数的值?

我看到这可以通过属性来完成,使用类似的东西:

fixture.Build<MyObj>().With(x=x.Val,"hard coded value").Create()

【问题讨论】:

  • C# 并没有真正的类型安全语法来仅引用方法中的单个参数(包括构造函数),因此除非您将整个构造函数拉入,否则这实际上并不可行一个 lambda 表达式,此时您不妨直接使用构造函数创建对象,但不使用 AutoFixture。你想做这件事的动机是什么? IME,通常有一种以不同方式实现大多数相关目标的好方法,但您可能需要以不同的方式看待问题。
  • 希望有一些非类型安全但方便的方式,比如 Func ,其中输入是字符串形式的参数名称。我的一般动机是单元测试。通常我想更新一个对象,但我只关心一个参数。也许参数值驱动某种计算或转换。我希望值本身是随机的,但测试需要知道选择了什么随机值。鉴于此,单元测试可能会获取一个属性或调用一个在其操作中使用构造函数参数值的方法。
  • this answermy answer toward the bottom of this CodePlex discussion 的一些变化可能会让你开心
  • 就是这样! @MarkSeemann,有什么办法可以将这样的东西内置到 AutoFixture 中?这似乎是一个非常有用但基本的范例(选择构造函数值),不支持开箱即用。
  • @SFun28 我已经编辑了我的答案以添加另一个部分,解释如何使用提供的构建块做类似的事情;如果你想隐藏一些管道,我相信你可以弄清楚如何围绕它创建一个 API。

标签: c# autofixture


【解决方案1】:

来自 cmets 关于这个问题的讨论:

我经常想更新一个对象,但我只关心一个参数。也许参数值驱动某种计算或转换。我希望值本身是随机的,但测试需要知道选择了什么随机值。

假设这是根本问题,我会尝试解决这个问题。

询问对象

最简单的解决方案是在 AutoFixture 创建后简单地询问对象的值是什么:

var fixture = new Fixture();
var mc = fixture.Create<MyClass>();
var specialValue = mc.SpecialValue;
// Do something with specialValue...

如果您只需要知道特殊值是什么,但不需要它具有特定值,则可以使用此技术。

这显然需要您将构造函数参数中的值公开为(只读)属性,但这是a good idea to do anyway

创建后赋值

如果需要参数有特定的值,可以在创建后赋值:

var fixture = new Fixture();
var mc = fixture.Create<MyClass>();
mc.SpecialValue = "Some really special value";

这要求您将值作为可写属性提供。虽然我个人不太喜欢这种做法,因为这会使对象可变,但许多人已经以这种方式设计他们的对象,如果是这样,为什么不利用它呢?

创建后使用复制和更新

如果您希望您的对象是不可变的,您仍然可以使用上述技术的变体。在 F# 中,您可以使用所谓的copy and update expressions 来实现相同的目标。在 F# 中,它会是这样的:

let fixture = Fixture ()
let mc = {
    fixture.Create<MyRecord> ()
    with SpecialValue = "Some special value!" }

您可以在 C# 中通过为您的类提供 copy-and-update 方法来模拟这一点,这样您就可以编写如下内容:

var fixture = new Fixture();
var mc = fixture
    .Create<MyClass>()
    .WithSpecialValue("Some really special value");

这是我在 C# 代码中一直使用的技术。 AutoFixture 有an idiomatic assertion to test such copy and update methods

为参数注入值

如果您可以忍受为特定构造函数参数注入一个固定值,您可以使用 AutoFixture 内核的构建块来做到这一点。该测试演示了如何:

[Fact]
public void CustomizeParameter()
{
    var fixture = new Fixture();
    fixture.Customizations.Add(
        new FilteringSpecimenBuilder(
            new FixedBuilder("Ploeh"),
            new ParameterSpecification(
                typeof(string),
                "specialValue")));

    var actual = fixture.Create<MyClass>();

    Assert.Equal("Ploeh", actual.SpecialValue);
}

但是,这会导致该参数始终为该fixture 实例的“Ploeh”。它也是重构不安全的,因为它基于引用参数名称的字符串。

【讨论】:

  • 嗨,马克 - 非常感谢您的详细回复。这些都是很好的解决方法,权衡与您提出的完全一样。共同的主题是你必须调整你的对象,以便它可以被测试。这对我来说有点代码味道。似乎没有办法在不修改对象的情况下完成我想要做的事情。
  • 这叫做测试驱动开发。重点在于测试提供了有关 API 设计可用性的反馈。
  • 仅当您使用 TDD =) 我认为 Autofixture 在独立于 TDD 的情况下非常有用。无论如何,它在 TDD 中不仍然是一种气味吗?添加这些属性的唯一原因是 Autofixture 存在缺陷。它与TDD的原则无关。
  • 太好了;优秀软件的标志之一是人们以最初设计的其他方式使用它,这很酷,但它被非常明确地设计为 TDD 工具。
  • 自从引入 nameof 运算符 new ParameterSpecification( typeof(string), nameof(MyClass.SpecialValue))); 后,它现在实际上比以前更安全了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多