繁体   English   中英

从部分模拟对象返回模拟对象不起作用

[英]Returning mocked object from partially mocked object not working

我正在编写一个单元测试,试图在其中部分模拟服务。 我的意思是,我希望服务的一种方法返回不同的模拟对象,而另一种方法表现正常。 这是我正在测试的方法:

public async Task<List<string>> GetDeletedRecordIds<T>(DateTime startDate)
        where T : ISalesForceObject
    {
        List<string> result;
        try
        {
            var client = await this.GetForceClient();
            var init = await client.GetDeleted<DeletedRecordRootObject>(typeof(T).Name, startDate, DateTime.Now);
            result = init?.DeletedRecords.Select(d => d.Id).ToList();
        }
        catch (Exception e)
        {
            this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, "GetDeletedRecordIds");
            throw;
        }

        return result;
    }

这是我需要返回模拟对象的方法:

public async Task<IForceClient> GetForceClient()
    {
        ForceClient forceClient = null;
        try
        {
            var auth = new AuthenticationClient();

            var consumerKey = this._settingService.GetSetting("SalesForceConsumerKey");
            var consumerSecret = this._settingService.GetSetting("SalesForceConsumerSecret");
            var password = this._settingService.GetSetting("SalesForcePassword");
            var securityToken = this._settingService.GetSetting("SalesForceSecurityToken");
            var username = this._settingService.GetSetting("SalesForceUsername");
            var tokenUrl = $"{this._settingService.GetSetting("SalesForceUrl")}/services/oauth2/token";

            await auth.UsernamePasswordAsync(
                consumerKey,
                consumerSecret,
                username,
                password + securityToken,
                tokenUrl);

            forceClient = new ForceClient(auth.InstanceUrl, auth.AccessToken, auth.ApiVersion);
        }
        catch (Exception e)
        {
            this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"GetForceClient");
            throw;
        }

        return forceClient;
    }

这是我目前在单元测试中所拥有的:

        var mockForceClient = new Mock<IForceClient>();
        mockForceClient
            .Setup(
                i => i.GetDeleted<DeletedRecordRootObject>(
                    It.IsAny<string>(),
                    It.IsAny<DateTime>(),
                    It.IsAny<DateTime>())).ReturnsAsync(deletedRecordRootObject);

        var mockService = new Mock<IForceDotComService>();
        mockService.Setup(m => m.GetDeletedRecordIds<sf.Account>(It.IsAny<DateTime>()))
            .Returns(async (DateTime d) => await this._service.GetDeletedRecordIds<sf.Account>(d));
        mockService.Setup(m => m.GetForceClient())
            .ReturnsAsync(mockForceClient.Object);

当前,测试以GetDeletedRecordIds运行,直到命中对GetForceClient方法的调用。 然后,它没有返回模拟的ForceClient对象,而是尝试运行当然会失败的方法。

在此先感谢您的帮助。

解决方案:这是我解决问题的方法。

首先,我创建了一个返回ForceClient的服务,如下所示:

public class ForceClientService : IForceClientService
{
    private readonly ILogger _logger;

    private readonly ISettingService _settingService;

    public ForceClientService(
        ILogger<ForceClientService> logger,
        ISettingService settingService)
    {
        this._logger = logger;
        this._settingService = settingService;
    }

    public async Task<IForceClient> GetForceClient()
    {
        ForceClient forceClient = null;
        try
        {
            var auth = new AuthenticationClient();

            var consumerKey = this._settingService.GetSetting("SalesForceConsumerKey");
            var consumerSecret = this._settingService.GetSetting("SalesForceConsumerSecret");
            var password = this._settingService.GetSetting("SalesForcePassword");
            var securityToken = this._settingService.GetSetting("SalesForceSecurityToken");
            var username = this._settingService.GetSetting("SalesForceUsername");
            var tokenUrl = $"{this._settingService.GetSetting("SalesForceUrl")}/services/oauth2/token";

            await auth.UsernamePasswordAsync(
                consumerKey,
                consumerSecret,
                username,
                password + securityToken,
                tokenUrl);

            forceClient = new ForceClient(auth.InstanceUrl, auth.AccessToken, auth.ApiVersion);
        }
        catch (Exception e)
        {
            this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"GetForceClient");
            throw;
        }

        return forceClient;
    }
}

然后,我更改了要测试的方法:

public async Task DeleteRecord<TSf>(TSf record)
        where TSf : ISalesForceObject
    {
        try
        {
            var client = await this._forceClientService.GetForceClient();
            var response = await client.DeleteAsync(typeof(TSf).Name, record.Id);
            if (!response)
            {
                throw new Exception($"Error deleting record with ID {record.Id}");
            }
        }
        catch (Exception e)
        {
            this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"ForceDotComService.DeleteRecord");
            throw;
        }
    }

然后,我重新构建了模拟来模拟依赖项与方法:

var mockForceClient = new Mock<IForceClient>();
        mockForceClient
            .Setup(
                i => i.GetDeleted<DeletedRecordRootObject>(
                    It.IsAny<string>(),
                    It.IsAny<DateTime>(),
                    It.IsAny<DateTime>())).ReturnsAsync(deletedRecordRootObject);

        var mockLogger = new Mock<ILogger<ForceDotComService>>();
        var mockForceClientService = new Mock<IForceClientService>();
        mockForceClientService.Setup(m => m.GetForceClient()).ReturnsAsync(mockForceClient.Object);

        this._service = new ForceDotComService(mockLogger.Object, mockForceClientService.Object);

现在它正在按预期方式工作。 非常感谢你的帮助!

您模拟依赖项 ,而不是被测类上的方法。

您需要注入依赖项IForceClient ,例如,通过使其成为构造函数参数。 因为现在您的GetForceClient()只是在被测类上调用,该类在该类中运行,而不是在您的模拟中运行,因此只需返回其中声明的new ForceClient()

this.GetForceClient()提取到其自己的由抽象支持的服务中

public IForceClientProvider {
    Task<IForceClient> GetForceClient();
}

然后,您可以通过构造函数注入来重构当前受测类以显式依赖该接口。

public class ForceDotComService : IForceDotComService {
    private readonly IForceClientProvider provider;

    public ForceDotComService(IForceClientProvider provider) {
        this.provider = provider;
    }

    public async Task<List<string>> GetDeletedRecordIds<T>(DateTime startDate)
        where T : ISalesForceObject {
        List<string> result;
        try {
            var client = await provider.GetForceClient();
            var init = await client.GetDeleted<DeletedRecordRootObject>(typeof(T).Name, startDate, DateTime.Now);
            result = init?.DeletedRecords.Select(d => d.Id).ToList();
        } catch (Exception e) {
            this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, "GetDeletedRecordIds");
            throw;
        }

        return result;
    }
}

然后,这将允许您在测试时模拟所需的行为。 在实现代码中,您将具有与上面的GetForceClient()方法相同的代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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