beakona / AutoInterface

C# interface-to-member source generator
MIT License
73 stars 9 forks source link

Can this project be used to generate interfaces + 'proxy-calls' for existing classes? #4

Closed StefH closed 2 years ago

StefH commented 2 years ago

I've this struct and class, which are external (I cannot modify):

public struct Test
{
    public int Id { get; set; }

    public Clazz C { get; }
}

public sealed class Clazz
{
    public string Name { get; set; }
}

What I would like is that from these two above, the following interfaces should be generated:

public interface ITest
{
    int Id { get; set; }

    public IClazz C { get; }
}

public interface IClazz
{
    public string Name { get; set; }
}

And the following two 'proxy' classes should be created:

public class TestMock : ITest
{
    private Test _instance;

    private IClazz _clazz;

    public TestMock(Test instance)
    {
        _instance = instance;
        _clazz = new ClazzMock(_instance.C);
    }

    public int Id
    {
        get => _instance.Id;
        set => _instance.Id = value;
    }

    public IClazz C => _clazz;
}

public class ClazzMock : IClazz
{
    private Clazz _instance;

    public ClazzMock(Clazz instance)
    {
        _instance = instance;
    }

    public string Name
    {
        get => _instance.Name; 
        set => _instance.Name = value;
    }
}

Which makes it possible to 'wrap' the real code in an interface, which makes it easier to mock struct Test and class Clazz.

beakona commented 2 years ago

Your mocking example is doable by creating new c# source generator. Source generator is right approach for the task you described but unfortunately AutoInterface does not deal with your needs. Can you fit within some existing mock source generator? https://github.com/amis92/csharp-source-generators

As I understood you want to mark existing struct, class and record and optionally define custom implementation for each mocking member.

For example:

//external assembly
public struct Test
{
    public int Id { get; set; }

    public Clazz C { get; }
}

public sealed class Clazz
{
    public string Name { get; set; }
}

//our assembly
[AutoMock(typeof(Test), "ITest")]
public partial struct TestMock
{
    //optionally customize some properties by writing c# code or scriban
}

[AutoMock(typeof(Clazz), "IClazz")]
public partial struct ClazzMock
{
}

Detection which member should be mapped to its mocking proxy should be automatic. I can think of two mechanisms:

StefH commented 2 years ago

I was thinking maybe in this direction:

// our assembly
[ProxyMock(typeof(Test))]
public partial interface ITest
{
}

[ProxyMock(typeof(Clazz))]
public partial interface IClazz
{
}

However I would be cool if I only need to annotate the first/base type:

[ProxyMock(typeof(Test), AutoGenerateRelated = true)]
public partial interface ITest
{
}

And that the generator automatically create also all related interfaces + classes.

beakona commented 2 years ago

Your approach is way more natural. Did you find any of existing mocking source generators that can do that? At the moment I am unable to start that project because of summer, vacations.. maybe in next 2-3 months...

StefH commented 2 years ago

I did not find any generater yet which implements the logic I need.

I will start a proof of concept in the next days and look how far I can implement it.

Probably I'll tag you in my project so that you can take a look and review/advice.

beakona commented 2 years ago

That's great.. in the mean time I've verified that partial interface is valid syntax. As I understood, you want to recursively implement Bridge pattern in reverse order (generate an interface from implementation) and then make Proxy pattern... all that fused in one simple and usable step.

StefH commented 2 years ago

@beakona In case you are interested : see my project here: https://github.com/StefH/ProxyInterfaceSourceGenerator

beakona commented 2 years ago

@StefH wow, you've nailed it.. I've tried it, do you like I run some variations like simple tests?

StefH commented 2 years ago

I've created NuGet 0.0.3, as soon as that is available, please use that one.

Yes please. If you can run some more tests, that would be great. (Just add an issue to my project)

(I think there are still some small issues, or some corner-cases not covered...)