【问题标题】:Faking Confluent .NET Kafka consumers伪造 Confluent .NET Kafka 消费者
【发布时间】:2021-03-16 20:49:43
【问题描述】:

环境:.NET 4.5。 FakeItEasy:4.0.0

我正在尝试创建一个假的 Kafka 消费者对象,下面是我使用的语法:

var fakeconsumer = A.Fake<Consumer<Null, string>>((x => x.WithArgumentsForConstructor(() => new Consumer<Null, string>(A.Dummy<IEnumerable<KeyValuePair<string, object>>>(), A.Dummy<IDeserializer<Null>>(), A.Dummy<StringDeserializer>()))));

Kafka客户端的代码在这里:https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/src/Confluent.Kafka/Consumer.cs

如您所见,我正在使用构造函数所需的正确参数调用 Fake 调用。但是我不断收到以下错误消息:“没有构造函数与传递的构造函数参数匹配。”。

非常感谢任何帮助。

谢谢

编辑:

在 C:\projects\fakeiteasy\src\FakeItEasy\Creation\CastleDynamicProxy\CastleDynamicProxyGenerator.cs 中的 FakeItEasy.Creation.CastleDynamicProxy.CastleDynamicProxyGenerator.CreateProxyGeneratorResult(类型 typeOfProxy,ProxyGenerationOptions 选项,IEnumerable1 additionalInterfacesToImplement, IEnumerable1 argumentsForConstructor,IFakeCallProcessorProvider fakeCallProcessorProvider):第 125 行 在 C:\projects\fakeiteasy\src\FakeItEasy\Creation\CastleDynamicProxy\CastleDynamicProxyGenerator.cs:line 86 在 C:\projects\fakeiteasy\src\FakeItEasy\Creation\DefaultFakeAndDummyManager.cs:line 41 在 C:\projects\fakeiteasy\src\FakeItEasy\A.cs:line 47 中的 FakeItEasy.A.Fake[T](Action`1 optionsBuilder) 处

【问题讨论】:

  • 以后,我建议阅读整个错误信息。它可以完全避免提问。在提出问题时,最好包含整个错误,因为它确实可以帮助回答者。请参阅stackoverflow.com/help/how-to-ask 了解更多信息。
  • 哦,顺便说一句,这没有记录,我可以说,但是 FakeItEasy 在制作 Fake 时会首先尝试使用无参数构造函数,如果没有,将按顺序使用构造函数从大多数参数到最少。它将提供 Dummies 作为构造函数参数。由于 Consumer 没有无参数构造函数,因此您的原始代码等效于 var fakeconsumer = A.Fake&lt;Consumer&lt;Null, string&gt;&gt;()。当然,可能总是有理由明确首选构造函数,即使您不需要指定参数,但您需要这样做(请参阅我的回答)。

标签: c# unit-testing apache-kafka kafka-consumer-api fakeiteasy


【解决方案1】:

我相信我已经重现了您的问题。这是我看到的完整例外:

FakeItEasy.Core.FakeCreationException : 
  Failed to create fake of type Confluent.Kafka.Consumer`2[Confluent.Kafka.Null,System.String] with the specified arguments for the constructor:
    No constructor matches the passed arguments for constructor.
    An exception of type System.ArgumentException was caught during this call. Its message was:
    'group.id' configuration parameter is required and was not specified.
       at Confluent.Kafka.Consumer..ctor(IEnumerable`1 config)
       at Confluent.Kafka.Consumer`2..ctor(IEnumerable`1 config, IDeserializer`1 keyDeserializer, IDeserializer`1 valueDeserializer)
       at Castle.Proxies.Consumer`2Proxy..ctor(IInterceptor[] , IEnumerable`1 , IDeserializer`1 , IDeserializer`1 )
    at FakeItEasy.Core.DefaultExceptionThrower.ThrowFailedToGenerateProxyWithArgumentsForConstructor(Type typeOfFake, String reasonForFailure)
    at FakeItEasy.Creation.FakeObjectCreator.AssertThatProxyWasGeneratedWhenArgumentsForConstructorAreSpecified(Type typeOfFake, ProxyGeneratorResult result, IProxyOptions proxyOptions)
    at FakeItEasy.Creation.FakeObjectCreator.CreateFake(Type typeOfFake, IProxyOptions proxyOptions, DummyCreationSession session, IDummyValueResolver resolver, Boolean throwOnFailure)
    at FakeItEasy.Creation.DefaultFakeAndDummyManager.CreateFake(Type typeOfFake, Action`1 optionsBuilder)
    at FakeItEasy.A.Fake[T](Action`1 optionsBuilder)
    Kafka.cs(14,0): at FakeItEasyQuestions2015.Kafka.MakeConsumer()

可以看到,FakeItEasy 本身在调用Consumer 类的构造函数时遇到了异常:

An exception of type System.ArgumentException was caught during this call. Its message was:
    'group.id' configuration parameter is required and was not specified.

这是从Consumer constructor on line 756 抛出的:

if (config.FirstOrDefault(prop => string.Equals(prop.Key, "group.id", StringComparison.Ordinal)).Value == null)
{
    throw new ArgumentException("'group.id' configuration parameter is required and was not specified.");
}

好像

Consumer(IEnumerable<KeyValuePair<string, object>> config,
         IDeserializer<TKey> keyDeserializer,
         IDeserializer<TValue> valueDeserializer)`

对其输入有一些未满足的要求。特别是,它似乎需要config 来包含一个键为“group.id”的元素。如果我将您的代码更改为

var fakeconsumer = A.Fake<Consumer<Null, string>>(
    (x => x.WithArgumentsForConstructor(
        () => new Consumer<Null, string>(new [] { new KeyValuePair<string, object>("group.id", "hippo")},
        A.Dummy<IDeserializer<Null>>(),
        A.Dummy<StringDeserializer>()))));

造假了。

我注意到您交叉发布到FakeItEasy Issue 1176。我会在那里记下这个答案。

【讨论】:

  • 感谢您发布解决方案。假电话现在工作正常。我在异常处理程序中得到的调用堆栈是(如上面的编辑所示)。它不包括完整的 kafka 消费者堆栈。非常感谢您的帮助。
【解决方案2】:

与原始问题不是 100% 相关,但在我的库中(Silverback:https://github.com/BEagle1984/silverback)我有一个 Confluent.Kafka 库的模拟内存实现,它允许进行复杂的集成测试。看一些简单的例子:https://silverback-messaging.net/concepts/broker/testing.html。

只是给你一个想法:

[Fact]
public async Task SampleTest()
{
    // Arrange
    var testingHelper = _factory.Server.Host.Services
        .GetRequiredService<IKafkaTestingHelper>();

    var producer = testingHelper.Broker
        .GetProducer(new KafkaProducerEndpoint("tst-topic"));

    // Act
    await producer.ProduceAsync(new TestMessage { Content = "abc" });

    await testingHelper.WaitUntilAllMessagesAreConsumedAsync();

    // Assert
    testingHelper.Spy.OutboundEnvelopes.Should().HaveCount(1);
    testingHelper.Spy.InboundEnvelopes.Should().HaveCount(1);
    testingHelper.Spy.InboundEnvelopes[0].Message.As<TestMessage>
        .Content.Should().Be("abc");
}

实现并不复杂,但它支持分区和重新平衡机制的模拟。 查看实现:https://github.com/BEagle1984/silverback/tree/master/src/Silverback.Integration.Kafka.Testing/Messaging/Broker/Kafka

【讨论】:

    【解决方案3】:

    我不是 Kafka 消费者类的专家,但看起来您正在像这样调用它:

    Consumer<Null, string>
    

    但我能在代码中找到的唯一构造函数是:

    public Consumer(
             IEnumerable<KeyValuePair<string, object>> config)
    
    public Consumer(
                IEnumerable<KeyValuePair<string, object>> config,
                IDeserializer<TKey> keyDeserializer,
                IDeserializer<TValue> valueDeserializer)
    

    所以有 no 匹配。看起来您想使用第一个,所以您缺少 IEnumerable 部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2020-05-23
      • 1970-01-01
      • 2018-06-06
      • 1970-01-01
      • 2019-11-22
      相关资源
      最近更新 更多