1

Environment: .NET 4.5. FakeItEasy : 4.0.0

I am trying to create a fake object of Kafka consumer, below is the syntax I use:

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>()))));

The code for Kafka client is here: https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/src/Confluent.Kafka/Consumer.cs

As you can see, I am invoking the Fake call with correct parameters required for the constructor. However I keep getting the follow error message : "No constructor matches the passed arguments for constructor.".

Any help is greatly appreciated.

Thank you

Edit:

at FakeItEasy.Creation.CastleDynamicProxy.CastleDynamicProxyGenerator.CreateProxyGeneratorResult(Type typeOfProxy, ProxyGenerationOptions options, IEnumerable1 additionalInterfacesToImplement, IEnumerable1 argumentsForConstructor, IFakeCallProcessorProvider fakeCallProcessorProvider) in C:\projects\fakeiteasy\src\FakeItEasy\Creation\CastleDynamicProxy\CastleDynamicProxyGenerator.cs:line 125 at FakeItEasy.Creation.CastleDynamicProxy.CastleDynamicProxyGenerator.GenerateProxy(Type typeOfProxy, ProxyGenerationOptions options, IEnumerable1 additionalInterfacesToImplement, IEnumerable1 argumentsForConstructor, IFakeCallProcessorProvider fakeCallProcessorProvider) in C:\projects\fakeiteasy\src\FakeItEasy\Creation\CastleDynamicProxy\CastleDynamicProxyGenerator.cs:line 86 at FakeItEasy.Creation.FakeObjectCreator.GenerateProxy(Type typeOfFake, IProxyOptions proxyOptions, IEnumerable1 argumentsForConstructor) in C:\projects\fakeiteasy\src\FakeItEasy\Creation\FakeObjectCreator.cs:line 113 at FakeItEasy.Creation.FakeObjectCreator.CreateFake(Type typeOfFake, IProxyOptions proxyOptions, DummyCreationSession session, IDummyValueResolver resolver, Boolean throwOnFailure) in C:\projects\fakeiteasy\src\FakeItEasy\Creation\FakeObjectCreator.cs:line 36 at FakeItEasy.Creation.DefaultFakeAndDummyManager.CreateFake(Type typeOfFake, Action1 optionsBuilder) in C:\projects\fakeiteasy\src\FakeItEasy\Creation\DefaultFakeAndDummyManager.cs:line 41 at FakeItEasy.A.Fake[T](Action`1 optionsBuilder) in C:\projects\fakeiteasy\src\FakeItEasy\A.cs:line 47

Nandu
  • 808
  • 7
  • 10
  • In the future, I recommend reading the entire error message. It may avoid the need to ask a question at all. And when asking a question, it's a good idea to include the entire error, as it can really help out the answerers. See https://stackoverflow.com/help/how-to-ask for more. – Blair Conrad Jul 30 '17 at 10:24
  • Oh, and an aside, this isn't documented that I can tell, but FakeItEasy will try first to use the parameterless constructor when making a Fake, and if there is none, will use constructors in order from most parameters to least. And it will provide Dummies as constructor arguments. Since Consumer has no parameterless constructor, your original code is equivalent to `var fakeconsumer = A.Fake>()`. Of course there may always be reasons to be explicit about the preferred constructor, even if you don't need to specify arguments, which you will need to do (see my answer). – Blair Conrad Jul 30 '17 at 10:51

3 Answers3

3

I believe I've reproduced your problem. Here's the full exception that I see:

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()

You can see that FakeItEasy itself encountered an exception while calling the Consumer class's 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.

This was thrown from the 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.");
}

It seems that

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

Has some requirements on its inputs that aren't being met. In particular, it seems it needs config to contain one element with the key "group.id". If I change your code to

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>()))));

The fake is created.

I notice that you cross-posted to FakeItEasy Issue 1176. I'll make a note there to come here for this answer.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • Thank you for posting the solution. The fake call works fine now. The call stack I got in exception handler was (as shown in above edit). It did not include the full kafka consumer stack. I really appreciate your help. – Nandu Jul 30 '17 at 16:45
3

Not 100% related to the original question but in my library (Silverback: https://github.com/BEagle1984/silverback) I have a mocked in-memory implementation of the Confluent.Kafka library, that allows for kinda sophisticated integration tests. See some simple examples: https://silverback-messaging.net/concepts/broker/testing.html.

Just to give you an idea:

[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");
}

The implementation is not that complex but it supports partitions and a simulation of the rebalance mechanism. See the implementation: https://github.com/BEagle1984/silverback/tree/master/src/Silverback.Integration.Kafka.Testing/Messaging/Broker/Kafka

Ramil Aliyev 007
  • 4,437
  • 2
  • 31
  • 47
BEagle1984
  • 61
  • 1
-1

I am no expert on the Consumer class for Kafka, but it looks like your are invoking it like:

Consumer<Null, string>

But the only constructors I can find in the code are:

public Consumer(
         IEnumerable<KeyValuePair<string, object>> config)

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

So there is no match. It looks like you want to use the first one, so you are missing the IEnumerable part.

Jocke
  • 2,189
  • 1
  • 16
  • 24