单元测试:如何使用jUnit和Mockito编写测试用例

4
我对Mockito、jUnit和TDD等方面非常陌生,所以我想学习如何正确进行TDD并需要一些示例来开动我的头脑。请帮助我。
有一个方法getNameInc(String dirPath, String filenName)。给定一个文件名,比如bankAccount.pdf,如果在这个文件夹中没有文件名为bankAccount.pdf的文件,则返回bankAccountAA.pdf。如果已经存在一个名字为bankAccount.pdf的文件,则返回bankAccountBB.pdf。递增的顺序是从AA-ZZ。当它达到ZZ时,它就回滚到AA。我已经实现了这个方法的逻辑。如何使用Mockiti和jUnit对这个方法进行单元测试?
编辑:
以下是涉及到的类和方法。
public class PProcessor{

    private final Map<Integer, String> incMap = new HashMap<Integer, String>();

    private String getNameInc(String dirPath, String filenName){
         String[] nameList = new File(dirPath).list(new FilenameFilter(){
            public boolean accept(File file, String name) {
                //only load pdf files
                return (name.toLowerCase().endsWith(".pdf"));
            }
        });
        //Return the number of occurance that a given file name appear
        //inside the output folder.
        int freq = 0;
        for(int i=0; i<nameList.length; i++){

            if(fileName.equals(nameList[i].substring(0, 8))){
                freq++;
            }
        }
        return incMap.get(freq);
    }

    private void generateIncHashMap(){
        incMap.put(new Integer(0), "AA");
        incMap.put(new Integer(1), "BB");
        incMap.put(new Integer(2), "CC");
        ...
    }
}

generateIncHashMap()将在构造函数中调用以预先生成哈希映射


我认为你没有理解模拟的重点。一个模拟不应该实现任何逻辑。通常它只会根据它所使用的测试用例返回硬编码值。 - Mike Deck
@Mike:我已经实现了逻辑。方法 getNameInc(String dirPath, String fileName) 已经由我实现。我只是想知道如何对其进行单元测试。可以使用mockito或普通的jUnit。我正在尝试学习这个想法。 - Thang Pham
1
很高兴您想使用TDD!但根据您最后的评论,似乎您先编写了逻辑...这与TDD的工作方式相反。首先编写一个失败的测试,然后使用真实代码使测试通过,然后重构该代码,使其更清晰,但不要比测试多做任何事情。然后编写另一个失败的测试。Mockito可以帮助您存根依赖于您类的复杂服务。您能否更新您的问题,列出在此功能上进行协作的类? - alpian
@alpian:我更新了我的帖子并添加了一些代码。你能看一下吗? - Thang Pham
2个回答

7
你正在尝试测试你的getNameInc(..)方法,我想是这样吧。当你调用它时,它会查找你指定的目录中的文件,并根据所找到的内容来修饰你给它的名称。
为了使类可单元测试,你应该抽象化对文件系统的依赖,这样在模拟中,你可以模拟任何你想要的目录内容。你的类将接受此接口的实例作为依赖项,并调用它来查找目录中的内容。当你在程序中真正使用该类时,你将提供这个接口的一个实现,这个实现委托给JDK文件系统调用。当你单元测试该类时,你将提供这个接口的Mockito模拟。
避免将太多逻辑放入FilesystemImpl类中,因为你无法对它进行严格的单元测试。让它成为一个非常简单的文件系统包装器,以便所有智能的东西都在Yourclass中,你将为其编写大量的单元测试。
public interface Filesystem {
    boolean contains(String dirpath, String filename);
}

public class FilesystemImpl {
    boolean contains(String dirpath, String filename) {
        // Make JDK calls to determine if the specified directory has the file.
        return ...
    }
}

public class Yourmainclass {
    public static void main(String[] args) {

         Filesystem f = new FilesystemImpl();
         Yourclass worker = new Yourclass(f);
         // do something with your worker
         // etc...
    }
}

public class Yourclass {
    private Filesystem filesystem;

    public Yourclass(Filesystem filesystem) {
        this.filesystem = filesystem;
    }

    String getNameInc(String dirpath, String filename) {
       ...
       if (filesystem.contains(dirpath, filename) {
          ...
       }
    }

}

public class YourclassTest {

   @Test
   public void testShouldAppendAAWhenFileExists() {
       Filesystem filesystem = Mockito.mock(Filesystem.class);
       when(filesystem.contains("/some/mock/path", "bankAccount.pdf").thenReturn(true);
       Yourclass worker = new Yourclass(filesystem);
       String actual = worker.getNameInc("/some/mock/path", "bankAccount.pdf");
       assertEquals("bankAccountAA.pdf", actual);
   }

   @Test
   public void testShouldNotAppendWhenFileDoesNotExist {
       Filesystem filesystem = Mockito.mock(Filesystem.class);
       when(filesystem.contains("/some/mock/path", "bankAccount.pdf").thenReturn(false);
       Yourclass worker = new Yourclass(filesystem);
       String actual = worker.getNameInc("/some/mock/path", "bankAccount.pdf");
       assertequals("bankAccount.pdf", actual);
   }
}

由于测试之间存在很多重复,因此您可能会创建一个设置方法并在其中完成一些工作,并为测试创建一些实例变量以供使用:

    private static final String TEST_PATH = "/some/mock/path";
    private static final String TEST_FILENAME = "bankAccount.pdf";
    private Filesystem filesystem;
    private Yourclass worker;

    @Before
    public void setUp() {
        filesystem = Mockito.mock(Filesystem.class);
        worker = new Yourclass(filesystem);
    }

    @Test
   public void testShouldAppendAAWhenFileExists() {
       when(filesystem.contains(TEST_PATH, TEST_FILENAME).thenReturn(true);
       String actual = worker.getNameInc(TEST_PATH, TEST_FILENAME);
       assertEquals("bankAccountAA.pdf", actual);
   }

   etc...

“FilesystemImpl” 应该是实现 “Filesystem” 接口的类,而不是一个接口本身,对吗? - Thang Pham

5

根据您所描述的情况,我不建议使用Mockito,因为似乎没有什么需要模拟的东西(因为很容易操纵文件系统)。

我会测试以下几点: - 如果调用getNameInc时没有匹配的文件该怎么办 - 如果调用getNameInc且文件夹中已经有AA-YY文件该怎么办 - 如果调用getNameInc并且文件ZZ已经存在该怎么办

然而,TDD的重点是,您应该已经编写了这些测试代码,然后实现代码使测试通过。由于您已经有了代码,所以您不会真正进行TDD。


是的,我知道在编写代码后实施测试并没有太多意义,但由于这对我来说是新的,我正在努力掌握TDD。 - Thang Pham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接