Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock third-party interface with return type that has readonly props & internal ctor

I'm trying to mock a third-party interface that I'm using (EventStore ClientAPI/IEventStoreConnection), in particular this method:

Task<StreamEventsSlice> ReadStreamEventsForwardAsync(string stream, long start, int count, bool resolveLinkTos, UserCredentials userCredentials = null);

The problem I'm having is that the return type StreamEventsSlice has readonly fields and an internal constructor i.e.

public class StreamEventsSlice
{
    public readonly string Stream;
    //other similar fields

    internal StreamEventsSlice(string stream) //missing other fields
    {
        Stream = stream;
    }
}

In my test code I'm mocking the event store connection using Moq, setting up the ReadStreamEventsForwardAsyncMethod, and trying to set the return type like so:

var connection = new Mock<IEventStoreConnection>();
connection.Setup(s => s.ReadStreamEventsForwardAsync(It.IsAny<string>(), It.IsAny<long>(), It.IsAny<int>(), It.IsAny<bool>(), It.IsAny<UserCredentials>())
    .ReturnsAsync(new StreamsEventSlice { props here });

But I can't set the properties, or call the constructor instead of that (I only actually need to set two properties)

I've tried making a stub that extends the original class, and returning that instead. Although I can hide the readonly properties I get an error on the class saying 'StreamEventsSlice does not have a constructor that takes 0 arguments'. Giving it a constructor doesn't work as I can't call the base ctor since it's internal.

How can I mock a method on an interface, when I can't instantiate the return type?

like image 725
J Lewis Avatar asked Nov 07 '22 14:11

J Lewis


1 Answers

@MindSwipe linked two good answers, unfortunately I couldn't use this one for constructing an object as one of the parameters for the constructor was also set to internal. Instead I had to use this method, and use their other suggestion to set the properties.

I kept most of mock setup code the same, other then adding and using the following method to instantiate StreamEventsSlice

internal StreamEventsSlice GetStreamEventSlice(ResolvedEvent[] events = null, bool isEndOfStream = true)
{
    events = events ?? new ResolvedEvent[0];
    var type = typeof(StreamEventsSlice);
    var slice = (StreamEventsSlice) FormatterServices.GetUninitializedObject(type);
    type.GetField("Events").SetValue(slice, events);
    type.GetField("IsEndOfStream").SetValue(slice, isEndOfStream);

    return slice;
}
like image 180
J Lewis Avatar answered Nov 12 '22 18:11

J Lewis