在java中使用模拟静态方法编写单元测试

时间:2016-03-18 09:21:25

标签: java unit-testing testing mocking powermock

我是初次编写测试的新手。我需要测试的类有一个方法,需要进行测试:

public String run(final Map<String, Dataset> datasets)
            throws ApiException {

        final String sourcePath = ElementsUtil.getElementFromDatasets(inputElementNames.get(0), datasets).getValue();
        final String destinationPath = ElementsUtil.getElementFromDatasets(inputElementNames.get(1), datasets).getValue();

        final File source = new File(sourcePath);
        final File destination = new File(destinationPath);

        if (source.exists()) {
            if (source.isDirectory()) {
                final IOFileFilter filter = new WildcardFileFilter(pattern);
                final Iterator<File> it = FileUtils.iterateFiles(source, filter, null);

                while (it.hasNext()) {
                    final File file = it.next();
                    moveFileToDirectory(file, destination);
                }
            } else {
                moveFileToDirectory(source, destination);
            }
        } else {
            LOGGER.error("Source file/folder at path {} doesn't exist.", sourcePath);
        }

        return "0";
    }

起初,由于我对编写单元测试的知识有限,我的单元测试看起来像这样:

@Test(description = "Test in case the source is a file.")
    public void moveFileTest1() {

        // setup
        final String fileName = UUID.randomUUID().toString() + ".txt";
        final String folderName = UUID.randomUUID().toString();
        final Element source = new Element("source", "./" + fileName);
        final Element destination = new Element("destination", "./" + folderName);

        ...

        final Path sourcePath = Paths.get(source.getValue());
        final Path destinationPath = Paths.get(destination.getValue());
        final Path fileDestination = Paths.get(destination.getValue() + "/" + fileName);
        try {
            Files.createFile(sourcePath);
            Files.createDirectory(destinationPath);

            // exercise
            moveFile.run("", datasets, null);

            // verify
            Assert.assertEquals(Files.exists(fileDestination), true);
            Assert.assertEquals(Files.exists(sourcePath), false);
        } catch (ApiException | IOException e) {
            LOGGER.error("Exception : ", e);
        } finally {

            // teardown
            try {
                Files.deleteIfExists(sourcePath);
            } catch (final IOException e) {
                LOGGER.error("Exception in teardown: ", e);
            }
            try {
                Files.deleteIfExists(fileDestination);
            } catch (IOException e) {
                LOGGER.error("Exception in teardown: ", e);
            }
            try {
                Files.deleteIfExists(destinationPath);
            } catch (IOException e) {
                LOGGER.error("Exception in teardown: ", e);
            }
        }
    }

在阅读了一些关于单元测试的文章之后,我发现我的测试并不完全是测试一个单元,因为我的方法依赖于不同的util方法。我还发现了在测试中模拟对象以及应该如何模拟所有内容。我的问题是:我应该在每个这些util方法/新对象调用等中使用mocking还是有不同的方法?你会如何测试这段代码?

2 个答案:

答案 0 :(得分:0)

您正在做的事情称为集成测试。集成测试是测试一个对象/方法,而不会在测试本身产生的实际数据或数据上进行任何模拟。集成测试的作用是测试您的设备,设备使用的设备和使用流程。如果您只想测试您的单位,您应该模拟您的单位使用的所有其他单位,并创建那些单位成功完成工作的流程。这意味着你应该复制一个测试,其中一个用过的单位抛出一个异常/返回一个你不希望作为一个好的值的值(只有它确实可以做到)并返回你期望的值,现在如何跟...共事。 通常在编写测试时,您会进行两种测试,单元测试和集成测试

答案 1 :(得分:0)

有一个很好的测试原则,如“不要嘲笑你不拥有的东西”。这不是什么意思?这意味着你不应该模拟/存储任何你无法控制的界面,即使这个界面是由你的公司编写的,而不是由你的团队编写的。

但是编写单元测试,而不是集成测试,你可能想要模拟除正在测试的类之外的所有类?实际上,这是一个非常棘手的问题。关于系统设计而不是关于测试的答案更多。您可以阅读有关如何解决问题herehere的信息。

这对你意味着什么?

正如你提到的那样0.496899361 30.42497045 已经写好了,所以这个类应该被嘲笑。怎么样?这取决于您现在正在编写的遗留代码或新代码。如果您有遗留代码 - 那么您需要PowerMock,否则您可能会更改设计并使用socket的实例。

例如,将ElementsUtil类划分为三个类:ElementsUtil - 接口,ElementsUtil - 实现,Elements - 具有静态访问权限的类以保持兼容性。 ElementsImpl可能有方法

ElementsUtil

该方法可以由构造函数中包含ElementsUtil方法的类使用。但是您可以为构造函数提供参数或setter。顺便说一句,我记得Mockito可以把嘲笑注入私人田地。在重构之后,您不需要PowerMock,只能使用Mockito。

现在关于public static Elements getInstance()。这个班不属于你,所以如果遵循良好的做法,那么这个班应该是嘲笑的。但run适用于文件,它已经是集成测试。所以回答 - FileUtils应该由你的新课程包装。