【问题标题】:What methods should I have JUnit tests for - How to mock methods with many dependencies我应该对哪些方法进行 JUnit 测试 - 如何模拟具有许多依赖项的方法
【发布时间】:2016-11-18 16:05:36
【问题描述】:

我是 JUnit 新手,不知道哪些方法应该有测试,哪些不应该。举个例子:

public List<Site> getSites(String user)
{
    SiteDao dao = new SiteDaoImpl();
    List<Site> siteList = new ArrayList<Site>();
    ServiceRequest rq = new ServiceRequest();
    rq.setUser(user);

    try
    {
        ServiceResponse response = siteDAO.getReponse(rq);
        List<String> siteNums = response.getSiteNums();

        if (siteNums != null && !siteNums.isEmpty())
        {
            List<DbModelSite> siteInfo = dao.getSiteInfo(siteNums);

            if (siteInfo != null && !siteInfo.isEmpty())
            {
                siteList = SiteMapper.mapSites(siteInfo);
            }
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

    return siteList;
}


public static List<Site> mapSites(List<DbModelSite> siteInfo)
{
    List<Site> siteList = null;

    if (siteInfo != null && !siteInfo.isEmpty())
    {
        siteList = new ArrayList<Site>();

        for (DbModelSite temp : siteInfo)
        {
            Site currSite = mapSite(temp);
            siteList.add(currSite);
        }
    }

    return siteList;
}


public static Site mapSite(DbModelSite site)
{
    Site mappedSite = null;

    if (site != null)
    {
        mappedSite = new Site();

        mappedSite.setSiteNum(site.getSiteNum());
        mappedSite.setSpace(site.getSpace());
        mappedSite.setIndicator("Y");
    }

    return mappedSite;
}

mapSites()mapSite() 方法进行单元测试是非常简单的,但我遇到问题的地方是getSites() 方法。对这种方法进行单元测试有意义吗?如果是这样,我将如何去做?似乎这需要大量的模拟,而且由于我对 JUnit 还很陌生,所以我无法弄清楚如何模拟所有这些对象。

所以我的问题实际上是两个方面:

  1. 如何确定一个方法是否需要进行单元测试?
  2. 如何对需要大量模拟的复杂方法进行单元测试?

【问题讨论】:

  • 是的,它也应该进行单元测试,理想情况下,至少所有public 方法都应该进行单元测试,因为它们以某种方式定义了您的应用程序的合同

标签: java unit-testing junit mocking mockito


【解决方案1】:

是的,测试该方法是有意义的。

能够对其进行测试的第一件事是使用依赖注入。如果该方法使用 new 创建自己的 SiteDao 实例,则您无法告诉该方法使用 SiteDao 的另一个模拟实例。

所以,阅读依赖注入并使用它。基本上,它归结为

public class MyService {

    private SiteDao siteDao;

    public MyService(SiteDao siteDao) {
        this.siteDao = siteDao;
    }

    // use the siteDao passed when constructing the object, instead of constructing it
}

这样,在测试你的服务时,你可以这样做

SiteDao mockSiteDao = mock(SiteDao.class);
SiteService service = new SiteService(mockSiteDao);

这里有一条建议,它与您的问题没有直接关系,但会使您的代码更简单,因此也更容易测试:

  1. 永远不要从返回集合的方法中返回 null。返回一个空集合以表示“没有元素”。
  2. 一般来说,不接受 null 作为有效的方法参数值,尤其是当参数是一个集合时。
  3. 1 和 2 的推论:遵循这些原则,您永远不需要检查集合是否为空或为空。直接用就行了。

这将减少if (siteNums != null &amp;&amp; !siteNums.isEmpty()) 使您的代码杂乱无章的数量,并且您要测试的分支也将减少。

请注意,所有健全的库(JDK 方法、JPA 等)都遵循这些原则。例如,JPA 查询永远不会返回空列表。

另外,不要通过打印堆栈跟踪并返回一个空列表来吞下异常,就好像没有发生任何不好的事情一样。让异常传播,以便您注意到并修复错误。

想象一下,这种方法是一种返回医学分析系统发现的癌症肿瘤数量的方法。您真的希望系统告诉您您身体健康,而系统实际上由于异常而无法完成工作吗?我真的更希望系统说“我出了故障,请使用另一台机器确定”。

【讨论】:

  • 谢谢!非常有帮助。就空值检查而言,如果它们是我的方法,那是有道理的,但以siteNums 为例。这是从外部服务返回的,所以我不知道他们是否有足够的空值检查,所以我想检查我这边的响应,以防我从该服务返回空值。实际上,我删除了大部分异常逻辑以使我的帖子更短,但无论哪种方式都有帮助。
  • 它是从 ServiceResponse 返回的,它本身是由 SiteDao 返回的,我希望它是你的类。这些类应该遵守相同的规则。
【解决方案2】:

单元测试的想法是确保每个“单元”(通常是一种方法)都可以单独测试,这样您就可以测试给定输入,您会收到预期的输出,因此回答您的问题:

  1. 我应该说所有公共方法应该进行单元测试
  2. 如果您在方法中做的太多以至于需要模拟批次,那么您可能希望将功能分解到另一个类中

回到你的例子,如果你想进行单元测试,有几件事需要注意:

  • new - 每当您在方法中使用此关键字时,您都会发现很难模拟该对象。在某些情况下(如ServiceRequest)没问题,但在其他情况下(如SiteDao)你会遇到问题。
  • 静态方法 - 相同,SiteMapper.mapSites(siteInfo) 你会发现很难模拟

您可以使用诸如PowerMock 之类的库来模拟newprivatestatic 方法,但我个人尽量避免这种情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-26
    • 1970-01-01
    • 2020-12-06
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    相关资源
    最近更新 更多