在测试中运行应用程序时如何解决 GOOGLE_APPLICATION_CREDENTIALS,Spring Boot?

Posted

技术标签:

【中文标题】在测试中运行应用程序时如何解决 GOOGLE_APPLICATION_CREDENTIALS,Spring Boot?【英文标题】:How to resolve GOOGLE_APPLICATION_CREDENTIALS when running app in test, Spring Boot? 【发布时间】:2021-05-28 21:12:33 【问题描述】:

我有一个依赖于 Google PubSub 的 Spring Boot 应用程序。我想用 Google Cloud PubSub 模拟器运行它。如何解决GOOGLE_APPLICATION_CREDENTIALS,让应用程序启动并使用来自本地模拟器的消息,而不是外部项目?

目前,如果我将 GOOGLE_APPLICATION_CREDENTIALS 设置为 dev.json,如果我不设置变量,则不会调用 PubSub,测试会崩溃。我怎样才能克服它?我不能拼图。

注意:我正在编写一个完整的 Spring 启动启动的集成测试。

我的 PubSub 实现:

import com.github.dockerjava.api.exception.DockerClientException
import com.google.api.gax.core.NoCredentialsProvider
import com.google.api.gax.grpc.GrpcTransportChannel
import com.google.api.gax.rpc.FixedTransportChannelProvider
import com.google.api.gax.rpc.TransportChannelProvider
import com.google.cloud.pubsub.v1.*
import com.google.cloud.pubsub.v1.stub.GrpcSubscriberStub
import com.google.cloud.pubsub.v1.stub.SubscriberStubSettings
import com.google.protobuf.ByteString
import com.google.pubsub.v1.*
import com.greenbird.server.contracts.TestServer
import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import org.testcontainers.containers.PubSubEmulatorContainer
import org.testcontainers.utility.DockerImageName
import java.util.concurrent.TimeUnit

class PubSubTestServer(private val projectName: ProjectName, private val ports: Array<Int> = arrayOf(8085)) :
    TestServer 

    constructor(projectId: String): this(ProjectName.of(projectId))

    private val projectId = projectName.project

    var emulator: PubSubEmulatorContainer = PubSubEmulatorContainer(
        DockerImageName.parse("gcr.io/google.com/cloudsdktool/cloud-sdk:latest")
    )

    private var channels: MutableList<ManagedChannel> = mutableListOf()

    private fun channel(): ManagedChannel 
        return if (channels.isEmpty()) 
            val endpoint = emulator.emulatorEndpoint
            val channel = ManagedChannelBuilder
                .forTarget(endpoint)
                .usePlaintext()
                .build()
            channels.add(channel)
            channel
         else 
            channels.first()
        
    

    private val channelProvider: TransportChannelProvider
        get() 
            return FixedTransportChannelProvider
                .create(
                    GrpcTransportChannel.create(channel())
                )
        

    private val credentialsProvider: NoCredentialsProvider = NoCredentialsProvider.create()

    private val topicAdminSettings: TopicAdminSettings
        get() 
            when 
                emulator.isRunning -> 
                    return buildTopicAdminSettings()
                
                else -> 
                    throw DockerClientException("Topic admin settings attempted to initialize before starting PubSub emulator")
                
            
        

    private fun buildTopicAdminSettings(): TopicAdminSettings 
        return TopicAdminSettings.newBuilder()
            .setTransportChannelProvider(channelProvider)
            .setCredentialsProvider(credentialsProvider)
            .build()
    

    private val subscriptionAdminSettings: SubscriptionAdminSettings
        get() 
            when 
                emulator.isRunning -> 
                    return buildSubscriptionAdminSettings()
                
                else -> 
                    throw DockerClientException("Subscription admin settings attempted to initialize before starting PubSub emulator")
                
            
        

    private fun buildSubscriptionAdminSettings(): SubscriptionAdminSettings 
        return SubscriptionAdminSettings.newBuilder()
            .setTransportChannelProvider(channelProvider)
            .setCredentialsProvider(credentialsProvider)
            .build()
    

    override fun start() 
        emulator.withExposedPorts(*ports).start()
    

    override fun stop() 
        terminate()
        emulator.stop()
    

    private fun terminate() 
        for (channel in channels) 
            channel.shutdownNow()
            channel.awaitTermination(5, TimeUnit.SECONDS)
        
    

    fun createTopic(topicId: String) 
        TopicAdminClient.create(topicAdminSettings).use  topicAdminClient ->
            val topicName = TopicName.of(projectId, topicId)
            topicAdminClient.createTopic(topicName)
        
    

    fun listTopics(): List<String> 
        return TopicAdminClient.create(topicAdminSettings)
            .listTopics(projectName)
            .iterateAll()
            .map  it.name 
            .toList()
    

    fun createSubscription(subscriptionId: String, topicId: String) 
        val subscriptionName = ProjectSubscriptionName.of(projectId, subscriptionId)
        SubscriptionAdminClient.create(subscriptionAdminSettings).createSubscription(
            subscriptionName,
            TopicName.of(projectId, topicId),
            PushConfig.getDefaultInstance(),
            10
        )
    

    fun listSubscriptions(): List<String> 
        return SubscriptionAdminClient.create(subscriptionAdminSettings)
            .listSubscriptions(projectName)
            .iterateAll()
            .map  it.name 
            .toList()
    

    fun push(topicId: String, message: String) 
        val publisher: Publisher = Publisher.newBuilder(TopicName.of(projectId, topicId))
            .setChannelProvider(channelProvider)
            .setCredentialsProvider(credentialsProvider)
            .build()


        val pubsubMessage: PubsubMessage = PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8(message)).build()
        publisher.publish(pubsubMessage).get()
    

    fun poll(size: Int, subscriptionId: String): List<String> 
        val subscriberStubSettings: SubscriberStubSettings = SubscriberStubSettings.newBuilder()
            .setTransportChannelProvider(channelProvider)
            .setCredentialsProvider(credentialsProvider)
            .build()
        GrpcSubscriberStub.create(subscriberStubSettings).use  subscriber ->
            val pullRequest: PullRequest = PullRequest.newBuilder()
                .setMaxMessages(size)
                .setSubscription(ProjectSubscriptionName.format(projectId, subscriptionId))
                .build()
            val pullResponse: PullResponse = subscriber.pullCallable().call(pullRequest)

            return pullResponse.receivedMessagesList
                .map  it.message.data.toStringUtf8() 
                .toList()
        
    


【问题讨论】:

您遇到的错误是什么?您如何运行测试环境? 【参考方案1】:

我找不到问题的答案。

我找到了一个使用 junit-pioneer 的 Junit5 解决方法,可以在实际测试运行之前将 env 变量设置为某个值。

因此,代码将与之前相同,但使用@SetEnvironmentVariable注释

@SetEnvironmentVariable(key="GOOGLE_APPLICATION_CREDENTIALS", value="dev.json")
class PubSubTestServer 
  ...

JUnit 先锋:Maven central.

【讨论】:

以上是关于在测试中运行应用程序时如何解决 GOOGLE_APPLICATION_CREDENTIALS,Spring Boot?的主要内容,如果未能解决你的问题,请参考以下文章

如何在春季测试中使用 liquibase 解决“已经有太多客户”的问题?

如何在 Python Django 中运行单元测试时禁用日志记录?

如何在运行测试时使用BeanShell服务器修改Jmeter的用户属性

如何在执行 POST 请求时解决颤振 CERTIFICATE_VERIFY_FAILED 错误?

[在运行单元测试(UWP)时启动应用程序

Jenkins如何集成运行testng.xml文件的解决方案