单元测试和封装

时间:2019-02-19 23:45:48

标签: c# unit-testing design-patterns

例如,我有一个与HttpClient

一起使用的课程
public class DomainActions : IDomainActions
{
    private readonly HttpClient _client;
    private readonly IConfiguration _configuration;

    public DomainActions(IConfiguration configuration)
    {
        _configuration = configuration;
        _client = new HttpClient()
        {
            BaseAddress = new Uri(_configuration.GetSection("DomainRegistration:BaseAddress").Value)
        };
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _configuration.GetSection("DomainRegistration:Token").Value);
    }

    public async Task<List<DomainDto>> GetDomainListAsync()
    {
        var responseMessage = await _client.GetAsync("domains");
        return await ProcessingDomainListResponseAsync(responseMessage);
    }

然后我们通过以下方式解决该问题:

    services.AddTransient<IConfiguration>(....);
    services.AddTransient<IDomainActions, DomainActions>();

和客户类别:

public class AddMxRecordToRegistrator
{
    protected readonly IDomainActions domainActions;
    public AddMxRecordToRegistrator(IDomainActions domainActions )
    {
        this.domainActions = domainActions ;
    }

    public async Task CreateDomainRecordAsync()
    {
            await domainActions.CreateDomainRecordAsync(queueItem.DomainForRegistration.DomainName, new DomainRegistrationCore.Models.DomainRecordDto
            {
                Content = queueItem.MxRecord,
                Name = String.Empty,
                Priority = 0,
                Ttl = 3600,
                Type = DomainRecordType.MX.ToString(),
                Regions = null
            });

好的,它工作正常。

现在,我想为AddMxRecordToRegistrator类创建单元测试,但是我不想使用真正的httpClient。怎么做?当然,我可以再添加一个依赖项:

public class DomainActions : IDomainActions
{
    private readonly HttpClient _client;
    private readonly IConfiguration _configuration;

    public DomainActions(IConfiguration configuration, HttpMessageHandler httpMessageHandler)
    {
        _configuration = configuration;
        _client = new HttpClient(httpMessageHandler)
        {
            BaseAddress = new Uri(_configuration.GetSection("DomainRegistration:BaseAddress").Value)
        };
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _configuration.GetSection("DomainRegistration:Token").Value);
    }


    public DomainActions(IConfiguration configuration) : this(configuration, new HttpClientHandler())
    {
    }

    public async Task<List<DomainDto>> GetDomainListAsync()
    {
        var responseMessage = await _client.GetAsync("domains");
        return await ProcessingDomainListResponseAsync(responseMessage);
    }

然后修改DI组成根:

    services.AddTransient<IConfiguration>(....);
    services.AddTransient<HttpMessageHandler>(....);
    services.AddTransient<IDomainActions, DomainActions>();

但是,为什么客户部分(在我们的情况下是组合根目录)仅因为需要创建单元测试而对DomainActions的内部细节一无所知?就像我们违反了单元测试的封装一样。如何正确实施?

2 个答案:

答案 0 :(得分:3)

要扩展@CamiloTerevinto的评论,c应该通过依赖注入依赖于c - '0',即该接口应该是传递给其构造函数的参数。

从封装的角度来看,AddMxRecordToRegistrator不应该知道IDomainActions依赖于AddMxRecordToRegistratorDomainActions。甚至不应该知道IConfiguration存在,因为那是一个具体的类,而HttpMessageHandler应该依赖于接口,而不是具体的类。

答案 1 :(得分:0)

  

但是为什么客户部分(在我们的情况下是组合根)应该知道   关于DomainActions内部细节的任何事情,仅仅是因为我们需要   创建单元测试?

组合根仅位于会“知道”所有较低级别依赖项的应用程序中。
“ Composition”根目录的作用是通过运行时实现组合所需的类。

AddMxRecordToRegistrator显然取决于抽象IDomainActions,因此对于单元测试AddMxRecordToRegistrator,您只需传递伪造的IDomainActions