更新:链接到repo被移动到回答,因为repo现在使用下面回答中的代码更新。
问题描述
当前代码正在运行,但它正在使用谷歌/云sdk的gcloud测试版模拟器pubsub进行集成测试。
我需要在maven surefire插件中设置以下环境变量。
<environmentVariables>
<PUBSUB_EMULATOR_HOST>localhost:8085</PUBSUB_EMULATOR_HOST>
</environmentVariables>
如何在Spring Boot中完成此操作
根据测试容器|Gcloud模块,在Spring Boot中使用PubSubEmulatorContainer实现集成测试的正确方法如下:https://github.com/saturnism/testcontainers-gcloud-examples/blob/main/springboot/pubsub-example/src/test/java/com/example/springboot/pubsub/PubSubIntegrationTests.java
这将在随机端口上调出容器,这是可能的,因为Spring中的DynamicProperty ty注册表
。似乎Micronaut缺少这种可能性。
文件编号:https://www.testcontainers.org/modules/gcloud/
我正在寻找在Micronaut 3中实现的JUnit5或Spock集成测试的工作示例。使用PubSubEmulatorContainer的x,如上述文档所述。
相关单据:https://micronaut-projects.github.io/micronaut-gcp/latest/guide/#emulator
GitHub上有一些关于配置TransportChannelProvider
的评论。我可以注入一个实例并检查它,但我仍然没有找到确切的操作。
以下是迄今为止最接近的线索:https://github.com/micronaut-projects/micronaut-gcp/issues/257 https://github.com/micronaut-projects/micronaut-gcp/pull/259
太长别读
我们需要首先启动testcontainer,获取模拟器主机地址,然后调用ApplicationContext。按如下方式运行:
applicationContext = ApplicationContext.run(
["pubsub.emulator.host": emulatorHost])
小型Github repo,示例代码:https://github.com/roar-skinderviken/pubsub-emulator-demo
带代码的长答案
我终于设法使用Micronaut 3.0.2和Spock制作了一个工作解决方案。相关的Micronaut PR让我走上了正轨,还有这篇文章:Micronaut测试最佳实践https://objectcomputing.com/files/9815/9259/7089/slide_deck_Micronaut_Testing_Best_Practices_webinar.pdf
第一个PubSubEmulator类(Groovy)
package no.myproject.testframework.testcontainers
import org.testcontainers.containers.PubSubEmulatorContainer
import org.testcontainers.utility.DockerImageName
class PubSubEmulator {
static PubSubEmulatorContainer pubSubEmulatorContainer
static init() {
if (pubSubEmulatorContainer == null) {
pubSubEmulatorContainer = new PubSubEmulatorContainer(
DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:emulators"))
pubSubEmulatorContainer.start()
}
}
}
然后是PubSubEmulator(Groovy)的夹具
package no.myproject.testframework.testcontainers
trait PubSubEmulatorFixture {
Map<String, Object> getPubSubConfiguration() {
if (PubSubEmulator.pubSubEmulatorContainer == null || !PubSubEmulator.pubSubEmulatorContainer.isRunning()) {
PubSubEmulator.init()
}
[
"pubsub.emulator-host": PubSubEmulator.pubSubEmulatorContainer.getEmulatorEndpoint()
]
}
}
然后,一个规范类(Groovy)启动容器,创建主题和订阅。
这里的线索是传入pubsub。模拟器。调用ApplicationContext时,将主机作为配置的一部分。运行。
代码的其余部分与我在问题中链接的Spring Boot示例非常相似。
package no.myproject.testframework
import com.google.api.gax.core.NoCredentialsProvider
import com.google.api.gax.grpc.GrpcTransportChannel
import com.google.api.gax.rpc.FixedTransportChannelProvider
import com.google.cloud.pubsub.v1.SubscriptionAdminClient
import com.google.cloud.pubsub.v1.SubscriptionAdminSettings
import com.google.cloud.pubsub.v1.TopicAdminClient
import com.google.cloud.pubsub.v1.TopicAdminSettings
import com.google.pubsub.v1.ProjectSubscriptionName
import com.google.pubsub.v1.PushConfig
import com.google.pubsub.v1.TopicName
import io.grpc.ManagedChannelBuilder
import io.micronaut.context.ApplicationContext
import no.myproject.configuration.GcpConfigProperties
import no.myproject.configuration.PubSubConfigProperties
import no.myproject.testframework.testcontainers.PubSubEmulatorFixture
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification
abstract class PubSubSpecification extends Specification
implements PubSubEmulatorFixture, EnvironmentFixture {
@AutoCleanup
@Shared
EmbeddedServer embeddedServer
@AutoCleanup
@Shared
ApplicationContext applicationContext
def setupSpec() {
// start the pubsub emulator
def emulatorHost = getPubSubConfiguration().get("pubsub.emulator-host")
// start a temporary applicationContext in order to read config
// keep any pubsub subscriptions out of context at this stage
applicationContext = ApplicationContext.run()
def gcpConfigProperties = applicationContext.getBean(GcpConfigProperties)
def pubSubConfigProperties = applicationContext.getBean(PubSubConfigProperties)
def channel = ManagedChannelBuilder.forTarget("dns:///" + emulatorHost)
.usePlaintext()
.build()
def channelProvider =
FixedTransportChannelProvider.create(GrpcTransportChannel.create(channel))
// START creating topic
def topicAdminClient =
TopicAdminClient.create(
TopicAdminSettings.newBuilder()
.setCredentialsProvider(NoCredentialsProvider.create())
.setTransportChannelProvider(channelProvider)
.build())
def topic = TopicName.of(
gcpConfigProperties.getProjectId(),
pubSubConfigProperties.getTopicName())
try {
topicAdminClient.createTopic(topic)
} catch (AlreadyExistsException) {
// this is fine, already created
topicAdminClient.getTopic(topic)
}
// START creating subscription
pubSubConfigProperties.getSubscriptionNames().forEach(it -> {
def subscription =
ProjectSubscriptionName.of(gcpConfigProperties.getProjectId(), it)
def subscriptionAdminClient =
SubscriptionAdminClient.create(
SubscriptionAdminSettings.newBuilder()
.setTransportChannelProvider(channelProvider)
.setCredentialsProvider(NoCredentialsProvider.create())
.build())
try {
subscriptionAdminClient
.createSubscription(
subscription,
topic,
PushConfig.getDefaultInstance(),
100)
System.out.println("Subscription created " + subscriptionAdminClient.getSubscription(subscription))
} catch (AlreadyExistsException) {
// this is fine, already created
subscriptionAdminClient.getSubscription(subscription)
}
})
channel.shutdown()
// stop the temporary applicationContext
applicationContext.stop()
// start the actual applicationContext
embeddedServer = ApplicationContext.run(
EmbeddedServer,
[
'spec.name' : "PubSubEmulatorSpec",
"pubsub.emulator.host": emulatorHost
],
environments)
applicationContext = embeddedServer.applicationContext
}
}
然后是用于模拟凭据的工厂类(Groovy)
package no.myproject.pubsub
import com.google.auth.oauth2.AccessToken
import com.google.auth.oauth2.GoogleCredentials
import io.micronaut.context.annotation.Factory
import io.micronaut.context.annotation.Replaces
import io.micronaut.context.annotation.Requires
import javax.inject.Singleton
@Factory
@Requires(property = 'spec.name', value = 'PubSubEmulatorSpec')
class EmptyCredentialsFactory {
@Singleton
@Replaces(GoogleCredentials)
GoogleCredentials mockCredentials() {
return GoogleCredentials.create(new AccessToken("", new Date()))
}
}
最后,斯波克测试规范。
package no.myproject.pubsub
import no.myproject.testframework.PubSubSpecification
import java.util.stream.IntStream
class PubSubIntegrationSpec extends PubSubSpecification {
def NUMBER_OF_MESSAGES_IN_TEST = 5
def DELAY_IN_MILLISECONDS_PER_MSG = 100
def "when a number of messages is sent, same amount of messages is received"() {
given:
def documentPublisher = applicationContext.getBean(DocumentPublisher)
def listener = applicationContext.getBean(IncomingDocListenerWithAck)
def initialReceiveCount = listener.getReceiveCount()
when:
IntStream.rangeClosed(1, NUMBER_OF_MESSAGES_IN_TEST)
.forEach(it -> documentPublisher.send("Hello World!"))
// wait a bit in order to let all messages propagate through the queue
Thread.sleep(NUMBER_OF_MESSAGES_IN_TEST * DELAY_IN_MILLISECONDS_PER_MSG)
then:
NUMBER_OF_MESSAGES_IN_TEST == listener.getReceiveCount() - initialReceiveCount
}
}
如何使用
将一段文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象, 可以传入一段字符串或一个文件句柄. from bs4 import BeautifulSoup soup = BeautifulSoup(open("index.html")) soup = BeautifulSoup("<html>data</html>") 首先,文档被转换成Unicode,并且HTML的实例
基础运用 Redis::set('user:profile:' . $id, "Swoft"); $userDesc = Redis::get('user:profile:' . $id); 你可以通过 Redis:: 调用任何 Redis 命令。Swoft 使用魔术方法将命令传递给 Redis 服务端,因此只需传递 Redis 命令所需的参数即可。示例: Redis::set('name',
引入 WeUI.css文件 利用 vue init mpvue/mpvue-quickstart my-project 初始化一个 mpvue 项目,然后在 /src/main.js 中引入 weui.css 由于是在小程序中使用,于是就直接使用了 weiui-wxss 中的样式文件,官方提供的是 weui.wxss,因此手动转成了 weui.css,然后引入即可。 这里提供 weui.css 一
将一段文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象, 可以传入一段字符串或一个文件句柄. from bs4 import BeautifulSoup soup = BeautifulSoup(open("index.html")) soup = BeautifulSoup("<html>data</html>") 首先,文档被转换成Unicode,并且HTML的实例
目录 简介 定义资源 主流框架的默认适配 抛出异常的方式定义资源 返回布尔值方式定义资源 注解方式定义资源 异步调用支持 规则的种类 流量控制规则 熔断降级规则 系统保护规则 访问控制规则 热点规则 查询修改规则 定制规则推送方式 其它 API 业务异常统计 Tracer 上下文工具类 ContextUtil 指标统计配置 规则生效的效果 判断限流降级异常 Dashboard 实时监控 简介 Se
英文原文:http://www.phpconcept.net/pclzip/user-guide/18 PKZIP 压缩包的内部表示方式 每个 PKZIP 压缩包都由一个 PclZip 对象表示。 当使用 PclZip 对象创建一个 PclZip 压缩包时,需绑定压缩包的名字。 此时,PclZip 不会检查压缩包,也不可读,甚至压缩包还不存在。 require_once('pclzip.lib.p
使用步骤 使用JustAuth总共分三步(这三步也适合于JustAuth支持的任何一个平台): 申请注册第三方平台的开发者账号 创建第三方平台的应用,获取配置信息(accessKey, secretKey, redirectUri) 使用该工具实现授权登陆 使用方式 引入依赖 <dependency> <groupId>me.zhyd.oauth</groupId> <artifa