当前位置: 首页 > 知识库问答 >
问题:

单元测试如何在返回 void 的方法上“测试合约”?

益明朗
2023-03-14

Java 8,但这是一个通用的单元测试问题,它(很可能)是与语言无关的。

编写JUnit测试的语法很容易,但是决定要编写什么测试以及如何测试主/生产代码是我发现的最大挑战。在阅读单元测试最佳实践时,我一遍又一遍地听到同样的事情:

测试合同

我相信这个想法是,单元测试不应该是脆弱的,如果方法的实现发生变化,它不应该被破坏。该方法应定义输入的协定 -

假设我有以下方法:

public void doFizzOnBuzz(Buzz buzz, boolean isFoobaz) {
    // wsClient is a REST client for a microservice
    Widget widget = wsClient.getWidgetByBuzzId(buzz.getId());

    if(widget.needsFile()) {
        File file = readFileFromFileSystem(buzz.getFile());

        if(isFoobaz) {
            // Do something with the file (doesn't matter what)
        }
    }

    return;
}

private File readFileFromFileSystem(String filename) {
    // Private helper method; implementation doesn't matter here EXCEPT...
    // Any checked exceptions that Java might throw (as a result of working)
    // with the file system are wrapped in a RuntimeException (hence are now
    // unchecked.

    // Reads a file from the file system based on the filename/URI you specify
}

所以在这里,我们有一个方法,我们希望为它编写单元测试。这种方法:

    < li >有两个参数,< code>buzz和< code>isFoobaz < li >使用类属性< code>wsClient进行网络/REST调用 < li >调用一个私有helper方法,该方法不仅与外部文件系统一起工作,而且“吞掉”检查的异常;因此,< code > readFileFromFileSystem 可能会引发< code > runtime exceptions

我们可以为此编写哪些类型的单元测试来“测试合同”?

验证输入(buzzisFoobaz)是显而易见的;合同应该定义每种情况的有效值/状态,以及如果它们无效,应该发生什么异常/结果。

但是除此之外,我甚至不确定这里的“契约”是什么,这使得为它编写测试非常困难。所以我想这个问题应该类似于“我如何确定单元测试的契约是什么,然后如何编写针对契约而不是实现的测试?”

但是这个标题对于一个SO问题来说太长了。

共有2个答案

左丘季
2023-03-14

问题是,你的契约方法并没有告诉你从外面能观察到什么效果。它基本上是一个双消费者,所以从确保是否有异常来看,不可能有太多的单元测试。

您可以做的测试是确保调用(Mocked)REST服务,或者File(Buzz参数的一部分,可能指向临时文件)在某些情况下会受到该方法的影响。

如果您想对方法的输出进行单元测试,您可能需要进行重构,以将确定应该做什么(文件需要更新)与实际做什么分开。

施自珍
2023-03-14

使用方法doFizzOnBuzz(Buzz buzz,boolean isFoobaz)和私有File readFileFromFileSystem(String filename)的代码不容易测试,因为第一种方法将尝试读取文件,这不是你想在测试中做的事情。

在这里,doFizzOnBuzz需要一些东西来提供一个文件供它使用。这个File提供者(我将称之为它)可以是一个接口,类似于:

public interface FileProvider {
  File getFile(String filename);
}

在生产环境中运行时,使用实际从磁盘读取文件的实现,但在单元测试doFizzOnBuzz时,可以使用File的模拟实现。这返回一个模拟File

要记住的关键点是,在测试doFizzOnBuzz时,我们不是在测试提供文件的任何东西或其他任何东西。我们假设工作正常。这些其他代码位有自己的单元测试。

可以使用Mockito之类的模拟框架来创建< code>FileProvider和< code>File的模拟实现,并可能使用setter将模拟的< code>FileProvider注入到被测类中:

public void setFileProvider(FileProvider f) {
  this.fileProvider = f;
}

此外,我不知道什么是wsClient,但我知道它有一个getWidgetByBuzzId()方法。这个类也可以是一个接口,出于测试目的,该接口将被模拟,并返回一个模拟<code>Widget</code>,类似于上面的FileProvider。

使用mockito,不仅可以设置接口的模拟实现,还可以定义在该接口上调用方法时返回的值:例如。

//setup mock FileProvider
FileProvider fp = Mockito.mock(FileProvider.class);

//Setup mock File for FileProvider to return
File mockFile = Mockito.mock(File.class);
Mockito.when(mockFile.getName()).thenReturn("mockfilename");
//other methods...

//Make mock FileProvider return mock File
Mockito.when(fp.getFile("filename")).thenReturn(mockFile);

ClassUnderTest test = new ClassUnderTest();
test.setFileProvider(fp); //inject mock file provider

//Also set up mocks for Buzz,, Widget, and anything else

//run test
test.doFizzOnBuzz(...)

//verify that FileProvider.getFile() was actually called:
Mockito.verify(fp).getFile("filenane"); 

如果未使用参数“filename”调用getFile(),则上述测试失败

结论如果您不能直接观察方法的结果,例如它是void,您可以使用Mocking来验证它与其他类和方法的交互。

 类似资料:
  • 测试方法: 测试用例:

  • 我陷入了单元测试场景的一个基本问题,并将感谢帮助。 我有一个类,它调用在DB中将标志设置为true。 我的测试: 我被要求测试并检查是否正在使用相关数据调用。 由于返回类型无效,我如何测试它?

  • 我正在为我的程序创建一个单元测试。我有一个void方法,它接受五个参数,然后将这些参数转换成一个闭包,并将其传递给另一个服务,然后将其转换成电子邮件并发送。我唯一关心的是这封邮件的正文,它存储在传递给这个函数的一个参数中。有没有可能在不改变我的程序设计的情况下验证这个论点? 编辑:作为对下面答案的回应,我在测试中插入了一个spock样式的模拟,但我认为我的语义不对,因为value存储的是NULL。

  • 问题内容: 如果我有这样的控制器: 基本上,我从存储库中获取东西,然后将其投影到匿名类型中。 如何进行单元测试? 有一个名为的属性,但它的类型与我们预期的一样。 这是否意味着如果我想测试JSON对象是否具有我期望的属性(“ id”,“ name”,“ type”),我是否必须使用反射? 编辑: 这是我的测试: 但是我在循环中收到一个运行时错误,指出“对象不包含id的定义”。 当我将断点定义为匿名类

  • 我正在我的项目中使用spring webflux。我的controller类调用返回Mono或Flux的服务类方法。 我正在尝试为我的服务类编写单元测试。我不确定如何为返回mono/flux的方法编写单元测试。我查看的大多数文章都建议我使用WebClientTest。但重点是,我在这里测试我的服务类。当我通过模拟服务类方法测试我的web层(控制器类)时,我使用了WebclientTest。 对如何