Autofixture: 从基类或接口应用自定义在 4.0.0-rc1 中失败(回归)

创建于 2017-11-20  ·  10评论  ·  资料来源: AutoFixture/AutoFixture

以下测试在 AutoFixture 3.51.0成功,但在4.0.0-rc1失败:
(即使在 master 的最新提交中: c856cd6

``` c#
[事实]
public void ShouldApplyCustomizationsFromBaseClass()
{
var fixture = new Fixture();
夹具.定制(c => c.Without(bc => bc.Id));
var res = fixture.Create();
Assert.Equal(0, res.Id);
}

[事实]
public void ShouldApplyCustomizationsFromInterface()
{
var fixture = new Fixture();
夹具.定制(c => c.Without(bc => bc.Id));
var res = fixture.Create();
Assert.Equal(0, res.Id);
}

公共接口 IInterface
{
int ID { 获取; 放; }
}

公共类基类
{
公共 int Id { 获取; 放; }
}

公共类 ImplClass : BaseClass, IInterface
{

}
``

question

所有10条评论

谢谢你提出这个问题。 嗯,这是我们想要的变化,它是故意应用的。

以前我们遇到过一个问题,即自定义一个子类型可能会影响其他子类型。 例如:

```c#
class Base { public string Common { get; 放; } }
类 Child1:基础 { }
类 Child2:基础 { }

[事实]
公共无效确保CustomizationAreNotAffected()
{
var fixture = new Fixture();
夹具.定制(c => c.With(x => x.Common, "dummy"));

var result = fixture.Create<Child2>();

Assert.NotNull(result.Common);

}


In the v3 this test failed, causing a lot of confusion to people. As you might imagine, the scenarios were more complicated and it was very non-obvious why the particular members are not initialized. 

For instance, the following code will not work, which again proves that API is not designed for that.
```c#
fixture.Customize<Base>(c => c.With(x => x.Common, "foo"));

因此,为了解决这个问题,我们改变了方法,现在一种类型的定制不能影响其他类型,即使它们属于同一继承行。 正如你在那个 PR 中看到的那样,它修复了一堆可用性问题并增加了更多的清晰度(而变化确实是破坏性的)。

如果您仍然想省略 base/interface 属性,您可以使用以下代码段:
```c#
私有类 SamePropertySpecification : IRequestSpecification
{
私有只读类型_declaringType;
私有只读字符串_name;

public SamePropertySpecification(Type declaringType, string name)
{
    _declaringType = declaringType;
    _name = name;
}

public bool IsSatisfiedBy(object request)
{
    if (request is PropertyInfo pi)
    {
        return pi.DeclaringType == this._declaringType &&
               pi.Name.Equals(this._name, StringComparison.Ordinal);
    }

    return false;
}

}

[事实]
public void TestBasePropertyOmitting()
{
var fixture = new Fixture();

fixture.Customizations.Add(new Omitter(new SamePropertySpecification(typeof(Base), nameof(Base.Common))));

var result = fixture.Create<Child1>();
Assert.Null(result.Common);

}
``

如果您经常需要该功能,您可以为fixture创建自己的扩展方法。 但是,我会投票选择不要开箱即用的功能,因为它看起来非常混乱。

好吧,很高兴知道这是故意的。 我绝对同意它令人困惑,但如果可以明确应用它,这是一个很好的功能。 将尝试您的代码段(并且可能会制作扩展方法)。

顺便说一句:这是您要添加到重大更改列表中的内容吗? 可能会阻止被问到同样的问题。

对于所有自定义中的任何一个自定义,都无法选择加入旧行为,对吗?

这是您想添加到重大更改列表中的内容吗?

嗯,它在错误修复部分中提到。 这实际上并不是一个重大变化,因为以前的行为从未被声明为“功能”。 相反,这是一个不受欢迎的副作用,它终于被消除了 😉

对于所有自定义中的任何一个自定义,都无法选择加入旧行为,对吗?

不,我不相信这样的方法,因为我完全重新设计了该方法。

请测试代码段,如果有效 - 请随时关闭问题。 或者如果您有其他问题,请告诉我😃

我已经对它进行了调整,因此它也适用于接口并制作了一个扩展方法,以便它可以在“Fixture.Customize”中使用(...)'。 感觉比我想要的更笨拙,尤其是因为它在 v3 中的工作方式对我们来说是完美的。 但是由于我们唯一的用例是省略属性并且它(最重要的是 😉 )可以工作,它会起作用。 谢谢!

@nphmuller对不起,我破坏了你整洁的代码,现在你需要生存😄 但是我相信你的牺牲是为了我们所有人的利益,所以你不会过分责备我们😅

完全没有责备。 完全理解你的推理,甚至同意。 对我来说简直糟透了😉

以后可能会深入研究这个问题,看看有没有更简洁的方法。 现在还好。

只是出于好奇 - 您真正的用例是什么? 因为我从来没有遇到过这种功能的需求。。通常,当我编写测试时,我只需要自定义特定类型,而不是配置基类或接口类型。 正在编写某种全局定制吗?

在这种情况下,我们使用 AutoFixture 作为 Entity Framework 集成测试的测试数据生成器。

我们有几个基本类型或接口供我们的实体使用,它们包含公共属性。 ID,租户 ID(和导航属性),诸如此类。

例如,这些属性之一是创建对象的用户。 这个属性对于大多数测试来说并不是那么有趣,如果它被生成,它将创建一个巨大的图(用户类也有许多导航属性),必须生成并插入到数据库中。 这会使测试变慢甚至在 db 级别失败,因为该对象不应该以这种方式插入。

因此,在必要时简单地排除这些属性并选择加入会更容易。 在基类级别,因为否则必须为每个实体类型编写相同的省略规则。

@nphmuller感谢您的澄清,现在很清楚😉 是的,看起来这个场景是有道理的,但我必须承认它看起来像你很少需要的东西。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

Ridermansb picture Ridermansb  ·  4评论

ploeh picture ploeh  ·  7评论

ecampidoglio picture ecampidoglio  ·  7评论

DeafLight picture DeafLight  ·  5评论

JoshKeegan picture JoshKeegan  ·  6评论