我如何在 spock 的方法级别模拟 new File()?
How can i mock new File() at method level in spock?
我有一个方法
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 数组,如果不存在,则从声纳中检索数据,并在路径 /build/projects.json 处生成和文件,然后读取该文件。
要为此编写测试用例,我必须始终模拟路径 /build/projects.json 中不存在的文件,并且必须 return 使用 [=34] 进行响应=] 数组从路径 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 = new File("src/test/resources/projects.json")
读取文件
如何在 groovy 中使用 spock 在方法级别模拟文件?
由于您可以控制 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)
我同意 Leonard 的观点,如果存在其他选项,您应该避免全局模拟,这里就是这种情况。全局模拟仅适用于 Groovy 被测代码,这也限制了它们的使用。
OTOH,很高兴探索我们可以用它们做什么,所以让我们去做吧。这是您正在测试的 Groovy(!)class 的简化版本:
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")
}
}
看到了吗?可以读取资源文件,测试通过
现在,如果我们想变得更有趣,说我们真的只想 return exists()
的假结果,如果文件实例确实是配置文件路径名的那个在被测 class 中,即在我们的示例中 "."
?在那种情况下,我们需要以某种方式获取 File
实例,因为我们不能只在存根闭包中说 this
。但是我们可以从闭包委托中获取 mock object,从那里我们可以获取文件实例并调用其他方法,例如 getName()
以确定它是否是我们想要的实例 return 的假结果。
孩子们,不要在家里这样做:
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
中,此条件会失败。现在它通过了。
我有一个方法
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 数组,如果不存在,则从声纳中检索数据,并在路径 /build/projects.json 处生成和文件,然后读取该文件。
要为此编写测试用例,我必须始终模拟路径 /build/projects.json 中不存在的文件,并且必须 return 使用 [=34] 进行响应=] 数组从路径 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 = new File("src/test/resources/projects.json")
如何在 groovy 中使用 spock 在方法级别模拟文件?
由于您可以控制 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)
我同意 Leonard 的观点,如果存在其他选项,您应该避免全局模拟,这里就是这种情况。全局模拟仅适用于 Groovy 被测代码,这也限制了它们的使用。
OTOH,很高兴探索我们可以用它们做什么,所以让我们去做吧。这是您正在测试的 Groovy(!)class 的简化版本:
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")
}
}
看到了吗?可以读取资源文件,测试通过
现在,如果我们想变得更有趣,说我们真的只想 return exists()
的假结果,如果文件实例确实是配置文件路径名的那个在被测 class 中,即在我们的示例中 "."
?在那种情况下,我们需要以某种方式获取 File
实例,因为我们不能只在存根闭包中说 this
。但是我们可以从闭包委托中获取 mock object,从那里我们可以获取文件实例并调用其他方法,例如 getName()
以确定它是否是我们想要的实例 return 的假结果。
孩子们,不要在家里这样做:
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
中,此条件会失败。现在它通过了。