Closed daveoftheparty closed 1 year ago
After reviewing the code I have a few initial conclusions:
When I initially added generic support, I just modified the model of an interface: InterfaceDefinition
and added another property InterfaceNameWithGenerics
. I then used that as a direct string substitute in MockText... see the merge of PR #46
It might be easy enough to fix the mock text generation to output the following:
var stats = new Mock<IStats<int>>...
given
IStats<int>
by modifying the part of InterfaceStore that reads the generic args from the semantic model. That method is called GetInterfaceTypeArguments
and is simply returning a string that matches the declaration, with a string.Join
over all the generic type arguments.
By changing that InterfaceStore method to instead return a list of type arguments, and passing them to MockText, through the InterfaceDefinition
model we can still easily do the text replacement.
What might make all this ridiculously hard is the interaction with Diagnoser.cs
-- we'd have to be able to identify the generic type arguments passed by the user when they are trying to get the Generate Moq Setups
prompt, and we'd have to be able to pass the user-supplied generic type arguments into MockText
Diagnoser.cs
side first... capturing the user generic type arguments, and successfully bouncing that off of interfaceStore.Exists()
-- because all the other trappings around converting the string of generic args in InterfaceStore to a proper list model won't matter if the Diagnoser stuff can't be done/is too hard and interest is lost in solving this problem through code.Also, there is a workaround, though not exactly pretty:
given the interface:
public interface IGenericService<TSource, TResult>
{
IEnumerable<TResult> TransformSource(IEnumerable<TSource> items);
}
if a user inputs this in a test file (note lack of angle brackets or generic type arguments):
[Test]
public void Go()
{
IGenericService
}
they will get this, which produces a compilation error due to TSource
, TResult
being undefined:
[Test]
public void Go()
{
var genericService = new Mock<IGenericService<TSource, TResult>>();
Expression<Func<IGenericService<TSource, TResult>, IEnumerable<TResult>>> transformSource = x =>
x.TransformSource(It.IsAny<IEnumerable<TSource>>());
genericService
.Setup(transformSource)
.Returns((IEnumerable<TSource> items) =>
{
return default;
});
genericService.Verify(transformSource, Times.Once);
}
and yet, IT CAN BE FIXED WITH using statements:
using TSource=System.Int32;
using TResult=System.Int32;
private interface IService
{
void DoIt();
}
private interface IService<T>
{
T DoIt();
}
private interface IService<T, K, V>
{
T DoIt(K key, V value);
}
Problem Statement
Currently, the only way to generate setups for the generic:
is to put this on a single line:
IStats<int>
won't give the code tip/generation option.Then, when the Moq setups are generated, they all end up as
Potential Solutions
Multi-cursor
generate as-is, and switch editor to multi-cursor mode over
T
which may not be available on all editorsFull implementation
actually implement the mock setups with T, so that
IStats<int>
becomesWith this, will need to deal with multiple generic arguments such as