虽然来自 Spock 的 运行 空手道 (1.0.1) 测试,但在 mock 中设置的系统 属性 最终在 karate.properties['message'] 中未定义
While running Karate(1.0.1) tests from Spock, System property that was set in mock ends up undefined in karate.properties['message']
在空手道版本 0.9.5 中,我能够在模拟调用期间使用 System.setProperty('message', message)。然后 属性 在使用 karate.properties['message'] 的功能中可用。我已经升级到版本 1.0.1,现在 karate.properties['message'] 结果为 undefined
Spock 测试代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
@LocalServerPort
private int port
@SpringBean
MessageLogger messageLogger = Mock()
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
given:
System.setProperty('foo', 'bar')
when:
Results results = Runner.path("classpath:").tags("~@ignore").parallel(5)
then:
results != null
1 * messageLogger.logMessage(_ as String) >> { String message ->
assert message != null
System.setProperty("message", message)
}
}
}
控制器
@RestController
public class MessageController {
@Autowired private MessageLogger messageLogger;
@GetMapping("/message")
public String message() {
String message = "Important Message";
messageLogger.logMessage(message);
return message;
}
}
消息记录器
@Component
public class MessageLogger {
public void logMessage(String message) {
System.out.println(message);
}
}
空手道-config.js
function fn() {
karate.configure('connectTimeout', 10000);
karate.configure('readTimeout', 10000);
karate.configure('ssl', true);
var config = {
localUrl: 'http://localhost:' + java.lang.System.getProperty('server.port'),
};
print('localUrl::::::::::', config.localUrl);
return config;
}
特征
@mockMessage
@parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* print 'message value ' + karate.properties['message']
0.9.5
2021-04-28 15:07:51.819 (...) [print] **foo value bar**
2021-04-28 15:07:51.826 (...) [print] **message value Important Message**
1.0.1
2021-04-28 14:36:58.566 (...) [print] **foo value bar**
2021-04-28 14:36:58.580 (...) [print] **message value undefined**
我克隆了您的项目并注意到一些过时的东西(Groovy、Spock 和 GMaven+ 版本)。升级它们并没有改变结果,我仍然可以重现你的问题。
A 还注意到,在您的两个分支中,POM 的不同之处不仅仅是空手道版本号,而且依赖关系也不同。如果我使用 1.0.1 分支中的那些,测试将不再在 0.9.5 下工作。因此,我分叉了您的项目并向您发送了每个分支的两个拉取请求,其中的依赖设置对两个空手道版本的工作方式相同。现在分支真的只是空手道版本号不同:
https://github.com/kriegaex/spock-karate-example/compare/karate-0.9.5...kriegaex:karate-1.0.1
顺便说一句,出于某种原因我不得不编译你的代码 运行 JDK 11,JDK 16 没有工作。 GMaven+ 抱怨 Java 16 groovy class 文件(字节码版本 60.0),即使 GMaven+ 应该使用目标级别 11。不知道这是怎么回事。无论如何,在 Java 11 我可以重现你的问题。由于两个分支的 Spock 版本相同,我猜问题出在空手道本身。我建议在那里打开一个问题,链接到你的 GitHub 项目(在你接受我的 PR 之后)。 Spock 确实设置了系统 属性,我在存根关闭命令中添加了更多日志输出以验证这一点。也许这是关于 Karate 如何以及何时与 Spock 通信的问题。
更新: Peter Thomas 在他的回答中建议将要传输到特征的值存储在 Java 对象中,并在Spock 测试已经设置好了。我想,他的意思是这样的:
https://github.com/kriegaex/spock-karate-example/commit/ca88e3da
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
@LocalServerPort
private int port
@SpringBean
MessageLogger messageLogger = Mock() {
1 * logMessage(_ as String) >> { String message ->
assert message != null
MessageHolder.INSTANCE.message = message
}
}
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
given:
Results results = Runner
.path("classpath:")
.systemProperty("foo", "bar")
.tags("~@ignore")
.parallel(5)
expect:
results
}
static class MessageHolder {
public static final MessageHolder INSTANCE = new MessageHolder()
private String message
private MessageHolder() {}
String getMessage() {
return message
}
void setMessage(String message) {
this.message = message
}
}
}
@mockMessage
@parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* def getMessage =
"""
function() {
var MessageHolder = Java.type('com.example.spock.karate.ApiTestRunnerSpec.MessageHolder');
return MessageHolder.INSTANCE.getMessage();
}
"""
* def message = call getMessage {}
* print 'message value ' + message
更新2:这是Peter第二个想法的实现,通过JS简单地访问Java系统属性。所以我用消息持有者单例简化了工作但不必要的复杂版本,再次消除它:
https://github.com/kriegaex/spock-karate-example/commit/e235dd71
现在它看起来像这样(类似于原始的 Spock 规范,只是重构得不那么冗长):
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
@LocalServerPort
private int port
@SpringBean
MessageLogger messageLogger = Mock() {
1 * logMessage(_ as String) >> { String message ->
assert message != null
System.setProperty('message', message)
}
}
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
expect:
Runner.path("classpath:").systemProperty("foo", "bar").tags("~@ignore").parallel(5)
}
}
唯一重要的变化是空手道功能:
@mockMessage
@parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* def getMessage = function() { return Java.type('java.lang.System').getProperty('message'); }
* print 'message value ' + getMessage()
Runner
“构建器”有一个推荐的.systemProperty()
方法。
请参考:https://github.com/intuit/karate/wiki/1.0-upgrade-guide#improved-test-suite-builder
所以这应该有效。否则如我在评论中所说,请提交复制方法。
Results results = Runner.path("classpath:")
.systemProperty("foo", "bar")
.tags("~@ignore").parallel(5)
编辑:所以我可以确认 karate.properties
在测试套件启动时是不可变的。
所以这里有 3 个选项:
- 更改您的测试策略,以便在开始之前解析所有动态参数
- 而不是
karate.properties[]
在空手道 / JS 中进行老派 java.lang.System.getProperty('foo')
调用,我很确定它会起作用
- 使用 Java 单例来为您的测试运行程序和空手道功能保持共享状态
在空手道版本 0.9.5 中,我能够在模拟调用期间使用 System.setProperty('message', message)。然后 属性 在使用 karate.properties['message'] 的功能中可用。我已经升级到版本 1.0.1,现在 karate.properties['message'] 结果为 undefined
Spock 测试代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
@LocalServerPort
private int port
@SpringBean
MessageLogger messageLogger = Mock()
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
given:
System.setProperty('foo', 'bar')
when:
Results results = Runner.path("classpath:").tags("~@ignore").parallel(5)
then:
results != null
1 * messageLogger.logMessage(_ as String) >> { String message ->
assert message != null
System.setProperty("message", message)
}
}
}
控制器
@RestController
public class MessageController {
@Autowired private MessageLogger messageLogger;
@GetMapping("/message")
public String message() {
String message = "Important Message";
messageLogger.logMessage(message);
return message;
}
}
消息记录器
@Component
public class MessageLogger {
public void logMessage(String message) {
System.out.println(message);
}
}
空手道-config.js
function fn() {
karate.configure('connectTimeout', 10000);
karate.configure('readTimeout', 10000);
karate.configure('ssl', true);
var config = {
localUrl: 'http://localhost:' + java.lang.System.getProperty('server.port'),
};
print('localUrl::::::::::', config.localUrl);
return config;
}
特征
@mockMessage
@parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* print 'message value ' + karate.properties['message']
0.9.5
2021-04-28 15:07:51.819 (...) [print] **foo value bar**
2021-04-28 15:07:51.826 (...) [print] **message value Important Message**
1.0.1
2021-04-28 14:36:58.566 (...) [print] **foo value bar**
2021-04-28 14:36:58.580 (...) [print] **message value undefined**
我克隆了您的项目并注意到一些过时的东西(Groovy、Spock 和 GMaven+ 版本)。升级它们并没有改变结果,我仍然可以重现你的问题。
A 还注意到,在您的两个分支中,POM 的不同之处不仅仅是空手道版本号,而且依赖关系也不同。如果我使用 1.0.1 分支中的那些,测试将不再在 0.9.5 下工作。因此,我分叉了您的项目并向您发送了每个分支的两个拉取请求,其中的依赖设置对两个空手道版本的工作方式相同。现在分支真的只是空手道版本号不同:
https://github.com/kriegaex/spock-karate-example/compare/karate-0.9.5...kriegaex:karate-1.0.1
顺便说一句,出于某种原因我不得不编译你的代码 运行 JDK 11,JDK 16 没有工作。 GMaven+ 抱怨 Java 16 groovy class 文件(字节码版本 60.0),即使 GMaven+ 应该使用目标级别 11。不知道这是怎么回事。无论如何,在 Java 11 我可以重现你的问题。由于两个分支的 Spock 版本相同,我猜问题出在空手道本身。我建议在那里打开一个问题,链接到你的 GitHub 项目(在你接受我的 PR 之后)。 Spock 确实设置了系统 属性,我在存根关闭命令中添加了更多日志输出以验证这一点。也许这是关于 Karate 如何以及何时与 Spock 通信的问题。
更新: Peter Thomas 在他的回答中建议将要传输到特征的值存储在 Java 对象中,并在Spock 测试已经设置好了。我想,他的意思是这样的:
https://github.com/kriegaex/spock-karate-example/commit/ca88e3da
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
@LocalServerPort
private int port
@SpringBean
MessageLogger messageLogger = Mock() {
1 * logMessage(_ as String) >> { String message ->
assert message != null
MessageHolder.INSTANCE.message = message
}
}
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
given:
Results results = Runner
.path("classpath:")
.systemProperty("foo", "bar")
.tags("~@ignore")
.parallel(5)
expect:
results
}
static class MessageHolder {
public static final MessageHolder INSTANCE = new MessageHolder()
private String message
private MessageHolder() {}
String getMessage() {
return message
}
void setMessage(String message) {
this.message = message
}
}
}
@mockMessage
@parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* def getMessage =
"""
function() {
var MessageHolder = Java.type('com.example.spock.karate.ApiTestRunnerSpec.MessageHolder');
return MessageHolder.INSTANCE.getMessage();
}
"""
* def message = call getMessage {}
* print 'message value ' + message
更新2:这是Peter第二个想法的实现,通过JS简单地访问Java系统属性。所以我用消息持有者单例简化了工作但不必要的复杂版本,再次消除它:
https://github.com/kriegaex/spock-karate-example/commit/e235dd71
现在它看起来像这样(类似于原始的 Spock 规范,只是重构得不那么冗长):
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
@LocalServerPort
private int port
@SpringBean
MessageLogger messageLogger = Mock() {
1 * logMessage(_ as String) >> { String message ->
assert message != null
System.setProperty('message', message)
}
}
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
expect:
Runner.path("classpath:").systemProperty("foo", "bar").tags("~@ignore").parallel(5)
}
}
唯一重要的变化是空手道功能:
@mockMessage
@parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* def getMessage = function() { return Java.type('java.lang.System').getProperty('message'); }
* print 'message value ' + getMessage()
Runner
“构建器”有一个推荐的.systemProperty()
方法。
请参考:https://github.com/intuit/karate/wiki/1.0-upgrade-guide#improved-test-suite-builder
所以这应该有效。否则如我在评论中所说,请提交复制方法。
Results results = Runner.path("classpath:")
.systemProperty("foo", "bar")
.tags("~@ignore").parallel(5)
编辑:所以我可以确认 karate.properties
在测试套件启动时是不可变的。
所以这里有 3 个选项:
- 更改您的测试策略,以便在开始之前解析所有动态参数
- 而不是
karate.properties[]
在空手道 / JS 中进行老派java.lang.System.getProperty('foo')
调用,我很确定它会起作用 - 使用 Java 单例来为您的测试运行程序和空手道功能保持共享状态