单元测试初学者方法

Unit testing beginner method

我目前正在做一个需要我们编写单元测试的项目。这是我知识有限的领域。经过过去几天的研究,我看到了多个示例,展示了如何编写 basic 测试来断言一个字符串是否等于另一个字符串。

但是,我仍然不清楚的是如何将单元测试作为一个整体进行。我有一个示例方法需要在下面测试:

public static SearchResults GetSearchResults(SearchFormModel searchForm, int currentItemCount, int skip)
    {
        var results = new SearchResults();
        var client = new RestClient(Settings.Default.SearchWebServiceUrl);
        try
        {
            var request = new RestRequest("{0}", Method.GET);
            if (!searchForm.FirstName.IsNullOrEmpty()){request.AddParameter("forenames",searchForm.FirstName);}
            if (!searchForm.LastName.IsNullOrEmpty()) { request.AddParameter("surname", searchForm.LastName); }

            request.AddUrlSegment("0", "Basic");

            request.AddHeader("Accept", "application/json");

            var response = client.Execute<SearchResult>(request);
            if (response.ResponseStatus != ResponseStatus.Completed)
            {
                if (response.ErrorException != null)
                {
                    SendErrorEmail(response.ErrorException.Message);                        
                }
                throw new Exception(Settings.Default.SearchGenericError);
            }

            if (response.Data != null)
            {
                results.Valid = response.Data.Valid;
                var error = response.Data.Error;
                if (!string.IsNullOrEmpty(error)){error = Settings.Default.SearchGenericError;}
                results.Error = error;
                if (response.Data.SearchResults != null)
                {                        
                    results.SearchResults = new List<SearchResult>(response.Data.SearchResults );
                }
            }
            else
            {
                throw new Exception(Settings.Default.SearchGenericError);
            }
        }
        catch (Exception ex)
        {
            registrantList.Error = ex.Message;
        }
        return results;
    }

这里的一个好方法是将其分解为两个单独的方法吗?一个像这样设置请求并得到响应的:

public static SearchResults GetSearchResults(SearchFormModel searchForm, int currentItemCount, int skip)
{
    var results = new SearchResult();
    var client = new RestClient(Settings.Default.SearchWebServiceUrl);
    try
    {
        var request = new RestRequest("{0}", Method.GET);
        if (!searchForm.FirstName.IsNullOrEmpty()){request.AddParameter("forenames",searchForm.FirstName);}
        if (!searchForm.LastName.IsNullOrEmpty()) { request.AddParameter("surname", searchForm.LastName); }

        request.AddUrlSegment("0", "Basic");

        request.AddHeader("Accept", "application/json");

        var response = client.Execute<SearchResult>(request);
        return GetSearchResults(response);
}

这又 returns 到另一个应该可测试的方法,即:

public static SearchResults GetSearchResults(IRestResponse<SearchResults> response)
{
    var results = new SearchResult();
    if (response.ResponseStatus != ResponseStatus.Completed)
        {
            if (response.ErrorException != null)
            {
                SendErrorEmail(response.ErrorException.Message);                        
            }
            throw new Exception(Settings.Default.SearchGenericError);
        }

        if (response.Data != null)
        {
            results.Valid = response.Data.Valid;
            var error = response.Data.Error;
            if (!string.IsNullOrEmpty(error)){error = Settings.Default.SearchGenericError;}
            results.Error = error;
            if (response.Data.SearchResults != null)
            {                        
                results.SearchResults = new List<SearchResult>(response.Data.SearchResults );
            }
        }
        else
        {
            throw new Exception(Settings.Default.SearchGenericError);
        }
    }
    catch (Exception ex)
    {
        registrantList.Error = ex.Message;
    }
    return results;
}

对于初学者的问题深表歉意,但我想确保这种方法是可行的。这是正确的方法吗?

提前致谢

单元测试的最佳方法是在编写使它们通过的代码之前编写测试。以这种方式工作可能会导致您编写更小、更容易测试的 类 和方法——即松散耦合的代码。回想一下 类 应该有一个责任而方法应该做一件事的经验法则。

如果您需要围绕此处显示的代码进行测试,您肯定有分解这些方法的正确想法——我认为您可以提取许多更小的方法。如果方法的名称揭示了它正在做的事情的意图,就很难使方法太小。

您熟悉依赖注入和模拟对象库吗?就个人而言,如果没有它们,我不会编写单元测试(肯定不会,但绝不会)。我不熟悉 .Net space 中可用的内容,但我的想法是您安排 类 明确依赖于各种协作者,并且您使用模拟对象库轻松地放置这些协作者进入完全正确的状态以充分锻炼您的测试对象。依赖注入框架将帮助您依赖接口而不是具体的 类——另一个好的经验法则。

编辑:认为这个答案会受益于一些更实用的建议,所以我记得 this post by Andrew Binstock on Jeff Bay's original essay "Object Calisthenics". I will understand if your initial reaction to the advice proposed there is disbelief, but I firmly believe there's no quicker way to get up to speed on the practices that you want to develop. Francesco Cirillo's anti-if campaign 提供了关于如何管理代码复杂性的另一种观点。

基本同意@unigeek的回答。您将不得不按责任分解您的 类,然后使用依赖注入 (DI) 来模拟协作者。没有这个单元测试就变得非常困难或不可能。

例如,您不希望测试实际触发 REST 请求并发送电子邮件。所以你必须将这两个职责提取到它们自己的 类 中,你可以 mock。

Mark Seemann's book on DI 极大地帮助我理解了它。

对于模拟,我喜欢使用 NSubstitute,因为我发现它易于使用且轻巧。

尝试慢慢阅读这些主题。单元测试很难,但如果做得好,它会大大提高你的代码质量,让你成为一个更好的开发人员 IMO。