简体   繁体   中英

How to inject a fake implementation of a class providing no interface and no virtual methods?

I'm using the Discord.Net package offering me the DiscordSocketClient class, which currently is a Singleton in my DI container. I need several methods from the instance

  • Task LoginAsync(TokenType tokenType, string token, bool validateToken = true)
  • Task StartAsync()
  • Task LogoutAsync()
  • Task StopAsync()

Given this example class consuming the methods

public sealed class ConsumingClass
{
    private readonly DiscordSocketClient _discordSocketClient;
    
    public ConsumingClass(DiscordSocketClient discordSocketClient)
    {
        _discordSocketClient = discordSocketClient;
    }
    
    public override async Task Initialize(CancellationToken cancellationToken)
    {
        await _discordSocketClient.LoginAsync(TokenType.Bot, "my-token");
        await _discordSocketClient.StartAsync();
    }
    
    public override async Task TearDown(CancellationToken cancellationToken)
    {
        await _discordSocketClient.LogoutAsync();
        await _discordSocketClient.StopAsync();
    }
}

I want to create tests using XUnit and Moq to ensure the instance of ConsumingClass works as expected.

Unfortunately DiscordSocketClient does not offer an interface so I can't inject a mock implementation to my tests.

Also the methods are not virtual, so it is not possible to create an instance of Mock<DiscordSocketClient> and use .Setup() to setup mock implementations.

So how can I verify in my testcase, that the client methods have been called once? Eg _discordSocketClientMock.Verify(discordSocketClient => discordSocketClient.LoginAsync(), Times.Once());

Do I have to create an interface IMyDiscordSocketClient providing those methods and a class another class MyDiscordSocketClient inheriting from DiscordSocketClient and implementing the interface and use that? Or are there any better ways?

If DiscordSocketClient does not have (public) virtual members, you cannot really derive from it either. Your last suggestion is however probably still the best possibility you have, except that MyDiscordSocketClient should not derive from DiscordSocketClient , but aggregate one.


public class MyDiscordSocketClient: IMyDiscordSocketClient
{
    private DiscordSocketClient _client;
    public MyDiscordSocketClient(DiscordSocketClient client)
    {
        _client = _client;
    }
    
    public async Task Initialize(CancellationToken cancellationToken)
    {
         _client.Initialize(cancellationToken);
    }

    // etc...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM