使用FakeItEasy,是有可能创造一个类型,采用泛型类型参数的虚拟对象类型、是有、创造一个、对象

2023-09-05 03:18:09 作者:逃

我有如下的测试:

[Fact]
public void StartProgram_CallsZoneProgramStart()
{
    var zone = A.Fake<Zone>();
    zone.StartProgram();
    A.CallTo(() => zone.ZoneProgram.Start(null, A.Dummy<ActionBlock<InterruptInfo>>())).MustHaveHappened(Repeated.Exactly.Once);
}

这是创造型的虚拟 ActionBlock&LT; InterruptInfo&GT; 正在被传递到MustHaveHappened电话。 zone.StartProgram绝对卡列斯的zone.ZoneProgram.Start方法,但这一呼吁没有看到FakeItEasy。它返回以下错误消息:

It's creating a dummy of type ActionBlock<InterruptInfo> which is being passed into the MustHaveHappened call. zone.StartProgram definitely calles the zone.ZoneProgram.Start method, but this call is not seen by FakeItEasy. It returns the following error message:

Assertion failed for the following call:
  ZoneLighting.ZoneProgramNS.ZoneProgram.Start(<NULL>, ActionBlock\`1 Id=1)
Expected to find it exactly once but found it #0 times among the calls:
  1: ZoneLighting.ZoneProgramNS.ZoneProgram.Start(inputStartingValues: Faked ZoneLighting.ZoneProgramNS.InputStartingValues, interruptQueue: ActionBlock`1 Id=2)
  2: ZoneLighting.ZoneProgramNS.ZoneProgram.Start(inputStartingValues: <NULL>, interruptQueue: ActionBlock`1 Id=2)

如可从该错误消息可以看出,被比较的ActionBlocks标识是不同的(1和2),这就是为什么它们也无法看到该调用。我的问题是,为什么是空置的ActionBlock = 1的ID?我想作为一个虚拟对象,它不应该有任何具体细节,如身份证等。这是因为泛型类型不能被空置?

As can be seen from the error message, the ID on the ActionBlocks being compared are different (1 and 2), which is why it's not able to see that the call is made. My question is, why is the ID of the dummied ActionBlock = 1? I thought being a dummy object, it shouldn't have any concrete details in it like ID etc. Is this because generic types cannot be dummied?

我看到类似的东西在这里: https://github.com/FakeItEasy/FakeItEasy/issues/ 402

I saw something similar here: https://github.com/FakeItEasy/FakeItEasy/issues/402

但我无法弄清楚,如果这是在谈论同样的事情或没有。任何帮助将大大AP preciated。

But I wasn't able to figure out if that's talking about the same thing or not. Any help would be greatly appreciated.

推荐答案

我不熟悉的 ActionBlock S,所以我不知道在那里他们得到他们的编号的值,但我想我能够说明什么在您的测试正在进行的一些情况。

I am not familiar with ActionBlocks, so am not sure where they get their Id values from, but I think I can shed some light on what's going on in your test.

首先,我想你混淆了什么是。如果是这样,不心疼。他们可以是一个有点混乱。从虚拟文档,一个假人

First, I think you're confused about what a Dummy is. If so, don't feel bad. They can be a little confusing. From the Dummy documentation, a Dummy is

假人是一个对象,FakeItEasy可以提供当需要某种类型的对象,但对象的实际行为并不重要。

Dummy is an object that FakeItEasy can provide when an object of a certain type is required, but the actual behavior of the object is not important.

它们大多使用FakeItEasy时,它本身需要创建一个对象来养活类的构造函数(我们将看到更多关于这个版本),或者当它需要从方法或属性返回非fakeable对象。这是罕见的最终用户必须创建它们。

They are mostly used by FakeItEasy itself when it needs to create an object to feed to class constructors (we'll see more about that later), or when it needs to return a non-fakeable object from a method or property. It's rare for end users to have to create them.

一个虚拟是一个实际的对象(它必须是 - 否则,我们怎么会用它做什么?)。它必须有任何具体在它的细节,其类型有(在此情况下, ActionBlock&其中; InterruptInfo&GT; )。有反对Dummying泛型类型没有限制。

A Dummy is an actual object (it has to be—otherwise, how could we do anything with it?). It has to have whatever concrete details in it that its type has (in this case, ActionBlock<InterruptInfo>). There are no restrictions against Dummying generic types.

纵观文档的如何假人做,我们可以看到,因为 ActionBlock&LT; InterruptInfo&GT; 可能没有自定义的 IDummyDefinition 可用(你有吗?),它不是一个任务,这不是fakeable(因为类是密封的),那么假人由调用的 ActionBlock构造的,与正在做傻瓜来satifsy每个参数。

Looking at the docs for how a Dummy is made, we can see that since ActionBlock<InterruptInfo> probably doesn't have a custom IDummyDefinition available (do you have one?), and it's not a Task, and it's not fakeable (because the class is sealed), then the Dummy is made by invoking one of the ActionBlock constructors, with Dummies being made to satifsy each of the arguments.

我想这ActionBlocks有标识。他们是如何分配的,我不知道,但如果他们是一个很好的ID,那么它看起来像有两个不同的 ActionBlock&LT; InterruptInfo&GT; S:之一提供 zone.StartProgram ,和虚拟的检验。

I guess that ActionBlocks have IDs. How they're assigned, I have no idea, but if they're a good ID, then it looks like we have two different ActionBlock<InterruptInfo>s: one provided in zone.StartProgram, and the Dummy made in the test.

在ActionBlocks文件表明,它不会覆盖等于,所以引用比较将执行,两个ActionBlocks(假人和一个用于生产code)不匹配。这就是为什么FakeItEasy不能识别该呼叫。

The ActionBlocks documentation suggests that it doesn't override Equals, so a reference comparison will be performed, and the two ActionBlocks (the Dummy and the one used in the production code) don't match. This is why FakeItEasy doesn't recognize the call.

如果你只是想看看是否有任何调用 zone.ZoneProgram.Start 作出的第一个参数,第二个参数的一些ActionBlock,我想你可能已经打算使用:

If you were just trying to see if any call to zone.ZoneProgram.Start was made with the first argument null and the second argument some ActionBlock, I think you might've meant to use:

A.CallTo(() => zone.ZoneProgram.Start(null, A<ActionBlock<InterruptInfo>>.Ignored))
            .MustHaveHappened(Repeated.Exactly.Once);

忽略,也可以缩短到 _ 了解更多关于的忽略的参数值,如果你愿意。)

(Ignored can also be shortened to _. Read more about ignoring argument values if you're so inclined.)

这可能让你过去你的眼前的问题,虽然我有两件事情的关注:

That may get you past your immediate problem, although I have concerns about two things:

看起来 zone.ZoneProgram.Start 被调用了两次,而不是Exactly.Once,但我敢肯定,你就可以处理这个和 在一般情况下,根据测试伪造的对象被认为是一种抗图案。通常,一个提供假货依赖于生产code下测试。我不是说这是行不通的,但有时它会导致混乱。尽管如此,它可以是用于另一天的问题,后电流问题已经解决。 It looks like zone.ZoneProgram.Start is being called twice, not "Exactly.Once", but I'm sure you'll be able to deal with this, and In general, faking the object under test is considered to be an anti-pattern. Typically one supplies fake dependencies to the production code under test. I'm not saying it won't work, but sometimes it can lead to confusion. Still, it may be a problem for another day, after the current question has been resolved.

我希望帮助一点点。

哦,你问问题402这个问题是如何定义的定制类,将控制如何假人在创建时给用户带来更多的权力。除非你已经有了一个类扩展 IDummyDefinition DummyDefinition ,它可能不相关的,在这一点上。

Oh, and you asked about Issue 402. That issue is about giving users more power when defining customization classes that will control how dummies are created. Unless you've made a class extending IDummyDefinition or DummyDefinition, it's probably not relevant at this point.