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

时间:2017-09-27 12:52:32

标签: c# unit-testing asp.net-core moq

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

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);

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

2 个答案:

答案 0 :(得分:2)

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

您需要注入依赖项IForceClient,例如将其作为构造函数参数。因为现在您的GetForceClient()只是在被测试的类上被调用,该类在该类中运行而不是在您的模拟上运行,因此只返回那里所述的new ForceClient()

答案 1 :(得分:2)

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()方法中提供的相同代码。