如何在Java中设计接口以方便测试?

如何在Java中设计接口以方便测试?,java,design-patterns,Java,Design Patterns,我有一个需求,我将有两个类来处理两个存储库,比如Git和Svn。这两个存储库上的操作将是相同的: 获得分支 获取提交ID 获取最后一个提交文件 等 我想我可以设计一个涵盖以下操作的界面: interface Repo{ public JSONObject getBranches() public JSONObject getCommits() . . . . } 现在类Git和Svn可以实现Repo。一切都很好 但问题来了,我们将如何测试这些接口?因为到repo的连接在各自

我有一个需求,我将有两个类来处理两个存储库,比如
Git
Svn
。这两个存储库上的操作将是相同的:

  • 获得分支
  • 获取提交ID
  • 获取最后一个提交文件
  • 我想我可以设计一个涵盖以下操作的界面:

    interface Repo{
       public JSONObject getBranches()
       public JSONObject getCommits()
       . . . . 
    }
    
    现在类
    Git
    Svn
    可以实现
    Repo
    。一切都很好

    但问题来了,我们将如何测试这些接口?因为到repo的连接在各自的类
    Git
    Svn
    (例如在其构造函数中)中处理。所以看起来像

    class Git implements Repo {
       public Git(String url, String username, String password){
           //connect to git url
       }
    
       . . . 
    }
    
    现在,就接口而言,这是一个公平的设计(就测试而言),还是我们需要将创建与repo连接的逻辑移动到其他类,比如
    RepoFactory
    ,并将它们注入方法参数中?因此,
    Repo
    界面如下所示:

    interface Repo{
       public JSONObject getBranches(RepoFactory repo)
       public JSONObject getCommits(RepoFactory repo)
       . . . . 
    }
    
    我们还可以注入
    RepoFactory
    的模拟,以便于测试


    哪个设计会更好?或者还有比这两个更好的东西?

    为什么需要测试接口?我认为您必须测试repo的两种实现:Git和SVN。如果您需要测试更高的逻辑,您可以使用返回模拟值的定制MockRepo实现,或者使用特殊框架(如Moq框架)设置此模拟


    祝你好运

    这取决于你想测试什么

    假设您有一个类a,它依赖于类B,它依赖于框架类

    现在,如果A是您的测试单元,那么您将创建一个模拟的B伪,并根据给定的场景监视结果,并测试A的可靠性

    如果你想测试B,那么它会变得很棘手,你应该问自己一个问题。B是否有符合逻辑且值得测试的部件?如果是这样的话,它们应该被提取到不同的类中并单独测试,或者您可以用接口包装框架对象,然后它就变成了一个相同的类

    示例1:B从框架连接对象读取字节流,并将其传递给第三方JSON解析器,然后将结果返回给a。实际上不需要测试B,因为它只将操作委托给(希望是)已经测试过的组件

    示例2:B正在从一个框架连接对象读取一个字节流,出于效率考虑,它会自己将字节流解析为一个JSON对象,然后将结果返回给a。很明显,如果您引入一个JSON解析器,创建并测试实现,那么情况将与示例1中的情况完全相同

    但是我们可以说,出于某种(愚蠢和错误的)原因,您希望B成为解析字节的组件。在这种情况下,您别无选择,只能在框架连接对象上创建一个包装接口,并模拟它以返回所需的输出。这种方法的问题是对一个组件进行了不必要的抽象,该组件将永远不会有多个实现,并且缺少对B()的明确责任

    然而,您的介绍中似乎存在一个设计缺陷:我不知道对您的系统的要求,但是这个接口永远不会允许不支持JSON的存储库,我认为这是不对的,我建议用定义良好的POJO来代替它。如果您肯定JSON中的用法永远是正确的,那么无论如何您都应该这样做!但是如果您坚持,那么至少要确保返回类型是您对JSON的定义,而不是某个第三方对象

    在这两种情况下,我都会使用定义良好的非耦合POJO来实现如下的单一抽象级别

    interface Repo {
       public List<Branch> getBranches();   
       public List<Commit> getCommits();
       . . . . 
    }
    
    接口回购{
    公共列表getBranchs();
    公共列表getcommittes();
    . . . . 
    }
    

    接口的分支和提交可以通过JSON元素轻松实现。

    为什么不创建一个接受
    Repo
    的测试方法,并简单地创建一个你想要测试的
    Repo
    的实例?@MadProgrammer:我还没完全明白为什么不编写一个期望
    Repo
    的测试方法(
    公共静态无效测试(Repo-Repo)
    ,那么不管你传递给它什么样的实例,
    测试(new Git());
    ??@MadProgrammer:所以你说只坚持第一种模式。对吗?对我来说,是的,这并不意味着它是对的;),但是,虽然你当然应该为测试而设计,但也不要为它牺牲API的可用性;)