如何在spock中的方法级别模拟new File()



我有一个方法,其中我是

JSONArray execute(SonarqubeMaintenanceSetting settings) {
String projectsFilePath = "${settings.currentDirectory}/build/projects.json"
File targetDir = new File(projectsFilePath)
if (!targetDir.exists()) {
String url = SONAR_API_URL + 'projects/search?ps=500'
Object response = getProjectList(settings.sonarToken, url)
Object[] responseArr = []
if (response != null && response.components != null && response.paging.total != null)   {
responseArr = response.components

}
JSONArray projectList = new JSONArray(responseArr)
byte[] bytes = projectList.toString().bytes
OutputStream out = new FileOutputStream(projectsFilePath)
out.write(bytes)
}
InputStreamReader inputStreamReader = new InputStreamReader(new 
File(projectsFilePath), 'UTF-8')
BufferedReader reader = new BufferedReader(inputStreamReader)
Object obj = jsonSlurper.parse(reader)
JSONArray projectListInFile = new JSONArray(obj)
projectListInFile

}

我正在读取路径为/build/projects.json的文件是否存在。如果存在,则获取该文件并转换为json数组,如果不存在,则从sonar中检索数据,并在路径/build/projects.json中创建和文件,然后读取该文件。

要为此编写测试用例,我必须始终模拟文件在路径/build/projects.json中不存在,并且必须使用json数组返回响应,该数组从路径src/test/resources/projects.json读取数据,这是一个伪数据。我尝试过以下方法

class SonarqubeMaintenanceMainTestSpec extends Specification {
@Subject
SonarqubeMaintenanceMain subject
SonarqubeMaintenanceMain instance
def setup() {
subject = new SonarqubeMaintenanceMain()
subject.with {
instance = Mock(SonarqubeMaintenanceMain)
}
instance = subject.instance
GroovyMock(File, global:true)
}
void "Test execute with settings"() {
given:
File dir = Mock()
File file = new File("src/test/resources/projects.json") // this will be read as null if GroovyMock(File, global:true) is set 

when:
JSONArray listOfProject = subject.execute(settings)
then:
1 * new File("${settings.currentDirectory}/build/projects.json") >> dir
1 * mockFile.exists() >> false
1 * new File("${settings.currentDirectory}/build/projects.json") >> file

它可以模拟文件,但我不能从路径src/test/resources/projects.json中读取第行的文件File file = new File("src/test/resources/projects.json")

如果我删除GroovyMock(File, global:true)那么我不能为File.exists((模拟File,但可以使用File-File=new File("src/test/resources/projects.json"(读取文件

如何在groovy中使用spock在方法级别模拟文件?

我同意Leonard的观点,即如果存在其他选项,则应该避免全局mock,这里就是这样。全局mock只适用于测试中的Groovy代码,这也限制了它们的使用。

OTOH,探索我们能用它们做什么很好,所以让我们这样做吧。下面是测试中Groovy(!(类的简化版本:

class UnderTest {
boolean isFound() {
// Current directory should always exist -> true
new File(".").exists()
}
}

假设我们只想模拟exists(),不能注入File实例,但所有其他File操作,如构造函数调用和其他方法调用都应该继续运行,我们可以使用全局Groovy间谍,如下所示:

import spock.lang.Specification
import spock.lang.Subject
class SonarqubeMaintenanceMainTest extends Specification {
@Subject
UnderTest subject
def setup() {
subject = new UnderTest()
GroovySpy(File, constructorArgs: ["x"], global: true) {
exists() >> false
}
}
void "Test execute with settings"() {
given:
File file = new File("src/test/resources/test.txt")
expect:
!subject.isFound()
file.text.startsWith("Lorem ipsum")
}
}

看到了吗?可以读取资源文件,测试通过。

现在,如果我们想更夸张地说,如果文件实例真的是被测试类中配置文件路径名的文件实例,即在我们的示例"."中,我们真的只想为exists()返回一个假结果,该怎么办?在这种情况下,我们需要以某种方式获得File实例,因为我们不能只在存根闭包中说this。但是,我们可以从闭包委托中获取mock对象,从那里我们可以获取文件实例并调用其他方法,如getName(),以确定它是否是我们想要返回假结果的实例。

孩子们,不要在家里这样做:

def setup() {
subject = new UnderTest()
GroovySpy(File, constructorArgs: ["x"], global: true) {
exists() >> {
delegate.mockObject.instance.name == "." ? false : callRealMethod()
}
}
}
void "Test execute with settings"() {
given:
File file = new File("src/test/resources/test.txt")
expect:
!subject.isFound()
file.exists()
file.text.startsWith("Lorem ipsum")
}

请注意expect:块中的附加file.exists()。在无条件的原始存根版本>> false中,此条件将失败。现在它过去了。

由于您可以控制settings.currentDirectory,我建议使用TempDir扩展名并使用真实的文件系统。

class SonarqubeMaintenanceMainTestSpec extends Specification {
@TempDir
File settingsDir
void "Test execute with settings"() {
given:
def projectJson = new File(settingsDir, 'build/projects.json').tap { parent.mkdirs() }
projectJson.text = SonarqubeMaintenanceMainTestSpec.getResource('/projects.json').text

and:
def settings = new Settings(currentDirectory: settingsDir.absolutePath)
when:
JSONArray listOfProject = subject.execute(settings)

最新更新