【问题标题】:How to mock method's local variables/variable methods using Mockito如何使用 Mockito 模拟方法的局部变量/变量方法
【发布时间】:2018-02-14 09:28:18
【问题描述】:

我有一个名为 A 的类和方法 method1()

Class A{

    boolean method1(String path){
        File procDir = new File(path);
        if(!procDir.exists()){
            return false`;
        }
        if(!procDir.canRead()){
            return false;
        }
    }
}

考虑到上面的代码,任何人都会建议使用方法 1 和内部方法/变量 (procDir.canRead()) 的方法。

【问题讨论】:

  • 我注意到你的问题仍然是“开放的”——因为你没有接受答案。请查看并决定是否要accept 回答。或者让我知道我是否可以做些什么来增强我的输入以使其被接受。接受有助于未来的读者确定问题是否已解决,并对花时间回答你的人表示感谢。谢谢!
  • 请把current source code to be tested should not be modified之类的详细信息放在那个区域。这里的答案将基于您将提到的情况,除了您所说的问题。这里有问题的答案,但并非所有解决方案都适用。

标签: mocking mockito junit4 powermockito junit5


【解决方案1】:

第一个答案是:你不能单独使用 Mockito。

您需要一个能够模拟对new 的调用的模拟框架。例如 PowerMock(ito) 或 JMockit。您可以找到有关如何使用 PowerMock here 执行此操作的文档。从这个意义上说,从技术上讲,这是一个已解决的问题——它只需要一些操作就可以让它工作(如果你的先决条件之一错误,它根本就不会工作)。

但除此之外,其他答案就在眼前:您创建了难以测试的代码,而现在您正在寻找一种创可贴来解决您不灵活的设计所带来的后果。

因此,与其将宝贵的时间花在玩弄 PowerMock(ito) 上,不如遵循您已经获得的建议并解决您的测试问题的根本原因。例如,通过使用某种依赖注入向此代码提供一个 File 对象(而不是让此代码调用 new 本身)。

【讨论】:

  • 我完全同意你在这里的评价。说得好。
  • 我同意,但是,鉴于我们不知道提出这个问题的情况,我们不能得出结论必须遵循上述评估(即,在首先,设计得当。)。在这方面,我可能会假设问题与 OP 无法控制的遗留代码有关(即使重构代码也会花费很多,或者在任何情况下都不应该修改遗留代码)。
  • @lemoncodes 当然,但是 OP 从来没有回来提供更多细节,所以无论如何;-(
【解决方案2】:

这是一个设计问题,因为该方法与 File 紧密耦合

Explicit Dependencies Principle 声明:

方法和类应该明确要求(通常通过方法参数或构造函数参数)它们需要的任何协作对象才能正常运行。

考虑到这一点,考虑重构方法以显式依赖File

boolean method1(File procDir){    
    if(!procDir.exists()){
      return false`;
    }

    if(!procDir.canRead()){
      return false;
    }

    return true;
}

这也将依赖项的创建反转为类/方法外部的委托。这也让该方法明确说明它实际需要什么

现在方法已解耦,可以通过传递适当的文件或模拟来测试方法。

也可以考虑抽象File

public interface FileInfo {
    boolean exists();
    boolean canRead()
}

因为类应该依赖于抽象而不是具体。

boolean method1(FileInfo procDir){    
    if(!procDir.exists()){
      return false`;
    }

    if(!procDir.canRead()){
      return false;
    }

    return true;
}

FileInfo 的实现然后可以封装一个实际的File 并公开所需的行为。

现在应该更容易通过继承或模拟框架直接模拟/存根/伪造抽象。

FileInfo file = mock(FileInfo.class);

when(file.exists()).thenReturn(true);
when(file.exists()).thenReturn(true);

subject.method1(file);

【讨论】:

    【解决方案3】:

    您可以使用 JUnit 的 TemporaryFolder 规则(或者,如果使用 JUnit5,则使用其 equivalent extension)为您的测试创建一个输入文件。

    你会...

    • 将此文件的路径传递到method1() - 以测试快乐路径
    • 将不存在的文件路径传递到method1() 以测试!procDir.exists() 悲伤路径

    测试完成后JUnit会丢弃临时文件夹,从而坚持自包含的测试原则。

    这种方法允许您在没有任何模拟的情况下测试您的代码,同时仍然是一个独立的单元测试。

    或者,您可以将File procDir = new File(path); 调用隐藏在接口后面:

    public interface FileCreator {
        File create(String path);
    }
    

    通过一个简单的实现:

    public class FileCreatorImpl implements FileCreator {
        public File create(String path) {
            return new File(path);    
        }
    }
    

    在测试时,您可以将Mock(FileCreator.class) 注入到包含method1() 的类的实例中,并按如下方式对该实例设置期望:

    String filePath = "...";
    File file = Mockito.mock(File.class);
    Mockito.when(fileCreator.create(filePath)).thenReturn(file);
    
    // happy path
    Mockito.when(file.exists()).thenReturn(true);
    Mockito.when(file.canRead()).thenReturn(true);
    assertThat(sut.method1(filePath), is(true));
    
    // sad path 1
    Mockito.when(file.exists()).thenReturn(false);
    assertThat(sut.method1(filePath), is(false));
    
    // sad path 2
    Mockito.when(file.exists()).thenReturn(true);
    Mockito.when(file.canRead()).thenReturn(false);
    assertThat(sut.method1(filePath), is(false));
    

    【讨论】:

    • 谢谢,我相信这个实现对我有用。
    猜你喜欢
    • 2014-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多