3

The question is pretty simple. I've Moq'ed an IDocumentClient (which is the main DocumentDB .NET client). I am Moq'ing the ExecuteStoredProcedureAsync() method. It returns a StoredProcedureResponse type (a concrete type), but its interface is very locked down.

I figured I could just create a StoredProcedureResponse and embed my payload into the Response property, but it's setter is private. Moreover, the only constructor is parameterless.

What am I missing here? It would be ideal if the method returned an interface type (IStoredProcedureResponse), but I don't have control over that. I realize I could write a wrapper around the IDocumentClient, but that's not feasible.

The only thing I can think of is to extend and forcefully override the property with the "new" keyword - BUT, in the actual calling code, I would have a terrible hack in which I check the runtime type and downcast in order to use the override Resource property.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Jmoney38
  • 3,096
  • 2
  • 25
  • 26
  • What are you trying to test? You have not mentioned that in your question. – CodingYoshi Jan 20 '17 at 22:34
  • I'm testing code that calls the IDocumentClient.ExecuteStoredProcedureAsync() method. Hence, I am trying to mock the response of this method which is the concrete type that I mentioned above. – Jmoney38 Jan 20 '17 at 22:41
  • I mock at the JavaScript level using node.js, so I've not tried to do what you are but can you just do what you need on the `StoredProcedureResponse.Response` property? – Larry Maccherone Jan 20 '17 at 22:49
  • Unfortunately, the property is publically immutable. – Jmoney38 Jan 20 '17 at 22:54
  • @Jmoney38. That is the problem with trying to mock/test types you don't own. abstract 3rd party types /services/implementations behind abstractions that you control. While you are hesitant to make those kind of changes, you are limiting your available options and creating more problems for yourself in the future when it comes to maintainability clean code. You've already identified the shortcomings of the current model yet willing to try and force a square peg in a round hole. Instead of wishing for the *ideal* method create your own. Because that is the only way you will have control. – Nkosi Jan 22 '17 at 20:16
  • @Jmoney38 have you found a satisfactory solution to this issue? – Jazaret Mar 19 '19 at 15:20

3 Answers3

1

Is the IDocumentClient interface required at your top level? If not, you could create a service interface:

public interface IDocumentService
{
    Task<IStoredProcedureResponse<T>> ExecuteStoredProcedureAsync<T>(string query, IEnumerable<object> parameters);
}

In this case, or one similar, you could then implement a live service that uses DocumentClient and a mock service that just returns a mocked IStoredProcedureResponse.

Incidentally, I find it odd that IDocumentClient.ExecuteStoredProcedureAsync returns a concrete instance that ALSO happens to inherit from an interface.

Eric
  • 1,737
  • 1
  • 13
  • 17
  • You are totally correct - but I really want to avoid ripping up the existing code that we have that is talking directly to the IDocumentClient interface. – Jmoney38 Jan 20 '17 at 22:44
  • If you are coupled as such, you could go forward with your inherit/new strategy or you could use a default StoredProcedureResponse instance and use Expression based reflection to update the property values. – Eric Jan 20 '17 at 22:48
1

By reading the comments I found a simple solution:

var response = new StoredProcedureResponse<T>();
response.GetType().InvokeMember("responseBody",
                BindingFlags.Instance | BindingFlags.SetField | BindingFlags.NonPublic, Type.DefaultBinder, response, new object[] {new T()});
Bagroy
  • 65
  • 6
0

Use reflection to create it:

Type type=typeof(StoredProcedureResponse);
var spr = (StoredProcedureResponse)Activator.CreateInstance(type,true);

and set the properties and whatever else you need using reflection as well:

spr.GetType().InvokeMember("PropertyName",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
Type.DefaultBinder, obj, "MyName");
CodingYoshi
  • 25,467
  • 4
  • 62
  • 64
  • I've tried that, but reflection doesn't allow me to set the property because no setter actually exists. Hence, I think they're overriding the getter to perform actual logic to derive the value being returned. Hence, I'm trying to reverse engineer the class, in a way, to figure out what I need to set privately via reflection. – Jmoney38 Jan 20 '17 at 23:12
  • Have you cracked the assembly open using Reflector, etc? – Eric Jan 20 '17 at 23:29
  • And also, the above code is asking for a public setter. There is a BindingFlags.NonPublic as well. – Eric Jan 20 '17 at 23:30