当前位置: 首页 > 知识库问答 >
问题:

当单元测试在Android项目中生成grpc代码时,遇到未实现的方法StatusRuntimeException

龙珂
2023-03-14

我有一个Android项目,它在构建时从proto文件生成gRPC类,在helloworld示例下:https://github.com/grpc/grpc-java/tree/master/examples/src

我的JUnit测试遵循为HelloWorldClient编写的测试(https://github.com/grpc/grpc-java/blob/master/examples/example-kotlin/src/test/kotlin/io/grpc/examples/helloworld/HelloWorldClientTest.kt),我认为理论上是可行的

但是,当我运行测试时,我遇到了异常错误,如下所示:

io.grpc.StatusRuntimeException: UNIMPLEMENTED: Method helloworld.Greeter/SayHello is unimplemented
    at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:235)
    at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:216)
    at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:141)
    at io.grpc.examples.helloworld.GreeterGrpc$GreeterBlockingStub.sayHello(GreeterGrpc.java:177)
    at com.android.grpcmvvm.data.GreeterRemoteDataSource.sayHello(GreeterRemoteDataSource.kt:49)
    at com.android.grpcmvvm.view.GreeterViewModelUnitTest.testTestFunction(GreeterViewModelUnitTest.kt:109)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at io.grpc.testing.GrpcCleanupRule$1.evaluate(GrpcCleanupRule.java:125)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


Wanted but not invoked:
serviceImpl.sayHello(
    <Capturing argument>,
    <any>
);
-> at io.grpc.examples.helloworld.GreeterGrpc$GreeterImplBase.sayHello(GreeterGrpc.java:101)

However, there was exactly 1 interaction with this mock:
serviceImpl.bindService();
-> at io.grpc.internal.AbstractServerImplBuilder.addService(AbstractServerImplBuilder.java:114)


Wanted but not invoked:
serviceImpl.sayHello(
    <Capturing argument>,
    <any>
);
-> at io.grpc.examples.helloworld.GreeterGrpc$GreeterImplBase.sayHello(GreeterGrpc.java:101)

However, there was exactly 1 interaction with this mock:
serviceImpl.bindService();
-> at io.grpc.internal.AbstractServerImplBuilder.addService(AbstractServerImplBuilder.java:114)


    at io.grpc.examples.helloworld.GreeterGrpc$GreeterImplBase.sayHello(GreeterGrpc.java:101)
    at com.android.grpcmvvm.view.GreeterViewModelUnitTest.testTestFunction(GreeterViewModelUnitTest.kt:112)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at io.grpc.testing.GrpcCleanupRule$1.evaluate(GrpcCleanupRule.java:125)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

以下是测试和相关代码(请记住ViewModel的名称。为了回答这个问题,我从测试中删除了关于ViewModel的任何内容)

GreeterViewModelUnitTest。千吨级

import com.android.grpcmvvm.data.GreeterRemoteDataSource
import com.android.grpcmvvm.grpc.GrpcService
import io.grpc.ManagedChannel
import io.grpc.examples.helloworld.GreeterGrpc
import io.grpc.examples.helloworld.HelloReply
import io.grpc.examples.helloworld.HelloRequest
import io.grpc.inprocess.InProcessChannelBuilder
import io.grpc.inprocess.InProcessServerBuilder
import io.grpc.stub.StreamObserver
import io.grpc.testing.GrpcCleanupRule
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.*
import org.mockito.AdditionalAnswers.delegatesTo
import org.mockito.internal.matchers.Any

@RunWith(JUnit4::class)
class GreeterViewModelUnitTest {
    // region JUnit test rules
    @get:Rule
    val grpcCleanupRule = GrpcCleanupRule()

    private val serviceImpl = Mockito.mock(GreeterGrpc.GreeterImplBase::class.java, delegatesTo<Any>(object: GreeterGrpc.GreeterImplBase(){}))
    // endregion

    // region Private properties
    @Mock
    private lateinit var grpcService: GrpcService

    private lateinit var managedChannel: ManagedChannel

    private lateinit var greeterRemoteDataSource: GreeterRemoteDataSource
    // endregion

    @Before
    @Throws(Exception::class)
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        val serverName = InProcessServerBuilder.generateName()

        grpcCleanupRule.register(InProcessServerBuilder
            .forName(serverName)
            .directExecutor()
            .addService(serviceImpl)
            .build()
            .start())

        managedChannel = grpcCleanupRule.register(InProcessChannelBuilder
            .forName(serverName)
            .directExecutor()
            .build())

        Mockito.`when`(grpcService.createManagedChannel()).thenReturn(managedChannel)
        greeterRemoteDataSource = GreeterRemoteDataSource(grpcService)
    }

    @Test
    fun testTestFunction() {
        val requestCaptor = ArgumentCaptor.forClass(HelloRequest::class.java)

        greeterRemoteDataSource.sayHello("once again")

        Mockito.verify<GreeterGrpc.GreeterImplBase>(serviceImpl)
            .sayHello(requestCaptor.capture(), ArgumentMatchers.any<StreamObserver<HelloReply>>())

        assertEquals("once again", requestCaptor.value.name)
    }

GrpcService.kt

import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import java.util.concurrent.Executors

class GrpcService(private val host: String, private val port: Int) {

    fun createManagedChannel(): ManagedChannel {
        return ManagedChannelBuilder
            .forAddress(host, port)
            .executor(Executors.newSingleThreadExecutor())
            .usePlaintext()
            .build()
    }
}

GreeterRemoteDataSource。千吨级

import com.android.grpcmvvm.grpc.GrpcService
import io.grpc.ManagedChannel
import io.grpc.StatusRuntimeException
import io.grpc.examples.helloworld.GreeterGrpc
import io.grpc.examples.helloworld.HelloReply
import io.grpc.examples.helloworld.HelloRequest

class GreeterRemoteDataSource constructor(private val grpcService: GrpcService) {

    private lateinit var channel: ManagedChannel

    fun sayHello(message: String): String {
        channel = grpcService.createManagedChannel()

        val stub = GreeterGrpc.newBlockingStub(channel)
        val request = HelloRequest.newBuilder().setName(message).build()

        val reply: HelloReply = try {
            stub.sayHello(request)
        } catch (e: StatusRuntimeException) {
            e.printStackTrace()
            return String.format("{0}", e.status)
        } finally {
            channel.shutdown()
        }

        return reply.message
    }
}

build.gradle(app)

apply plugin: 'com.android.application'

apply plugin: 'com.google.protobuf'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.android.grpcmvvm"
        minSdkVersion 15
        targetSdkVersion 28
        multiDexEnabled true
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    def grpc_version = "1.22.1"
    def lifecycle_version = "2.2.0-alpha04"
    def mockito_kotlin_version = "2.1.0"
    def mockito_version = "3.0.0"
    def multidex_version = "2.0.1"
    def truth_version = "0.45"

    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation "androidx.multidex:multidex:$multidex_version"
    implementation 'com.google.android.material:material:1.0.0'

    // gRPC
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
    implementation "io.grpc:grpc-okhttp:$grpc_version"
    implementation "io.grpc:grpc-protobuf-lite:$grpc_version"
    implementation "io.grpc:grpc-stub:$grpc_version"

    // Lifecycle
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

    // Instrumented tests
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    androidTestImplementation 'androidx.test:runner:1.2.0'

    // Unit tests
    testImplementation "androidx.arch.core:core-testing:2.1.0"
    testImplementation "com.google.truth:truth:$truth_version"
    testImplementation "io.grpc:grpc-testing:$grpc_version"
    testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockito_kotlin_version"
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.3.1'
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.1'
    testImplementation "org.mockito:mockito-inline:$mockito_version"
}

protobuf {
    protoc {
        artifact = 'com.google.protobuf:protoc:3.9.0'
    }
    plugins {
        grpc {
            artifact = "io.grpc:protoc-gen-grpc-java:1.22.1"
        }
        javalite {
            artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
        }
    }
    generateProtoTasks {
        all()*.plugins {
            javalite {}
        }
        ofNonTest()*.plugins {
            grpc {
                // Options added to --grpc_out
                option 'lite'
            }
        }
    }
}

建筑格拉德尔

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.50'
    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.10'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

我目前无法找到导致这种错误的确切原因。我也四处寻找答案/见解,但都没有结果。

感谢任何输入。

共有2个答案

沈子实
2023-03-14

没有sayHello的实现,因此异常是正确的:

    private val serviceImpl = Mockito.mock(GreeterGrpc.GreeterImplBase::class.java, delegatesTo<Any>(object: GreeterGrpc.GreeterImplBase(){}))

该示例并没有失败,因为HelloWorldClient捕获并记录了异常。如果您查看构建/报告/测试/测试/索引。然后导航到HelloWorldClientTest,标准错误包括:

Sep 18, 2019 9:18:11 AM io.grpc.examples.helloworld.HelloWorldClient greet
WARNING: RPC failed: Status{code=UNIMPLEMENTED, description=Method helloworld.Greeter/SayHello is unimplemented, cause=null}

要解决问题,只需提供sayHello方法的虚假实现:

    private val serviceImpl = Mockito.mock(GreeterGrpc.GreeterImplBase::class.java, delegatesTo<Any>(object: GreeterGrpc.GreeterImplBase(){
        override fun sayHello(req: HelloRequest, responseObserver: StreamObserver<HelloReply>) {
            responseObserver.onNext(HelloReply.getDefaultInstance())
            responseObserver.onCompleted()
        }
    }))

我提交了问题6161以使示例更加清晰。

孔飞舟
2023-03-14

您正在使用mockito-inline,它正在模拟最终方法。如果您切换到mockito-core,那么测试应该会通过。我用example le-kotlin测试了这一点;将mockito依赖项交换到mockito-inline破坏了测试。我将提到grpc-java强烈不支持覆盖最终方法。

作为快速修复,您可以使用:

Mockito.`when`(grpcService.bindService()).thenCallRealMethod()
 类似资料:
  • 当我构建我的解决方案(包括一个单元测试项目)时,我收到了这个错误。 此项目引用了此计算机上缺少的NuGet包。使用NuGet Package Restore下载它们。有关详细信息,请参见http://go.microsoft.com/fwlink/?LinkID=322105.缺少的文件是..\packages\MSTest.TestAdapter.1.1.11\build\net45\MSTes

  • 我实现了一个新类Holder。目标如下: 三个领域。字段1和2是整数,字段3是布尔值 这给我留下的问题是有50个可能的构造函数选项。5 X 5 X 2。这是不明智的或可扩展的(添加第6种类型,我必须编写一个巨大的更改),所以我使用了构建器模式: 这真的很有效。我有5个用于字段1初始化的生成器方法,5个用于字段2初始化的生成器方法,还有一个用于布尔值。设置字段后,调用build()方法,该方法使用格

  • 环境 问题 我正在测试Go实现的grpc服务器和客户端 首先,我进行了proto定义,并通过proto命令生成pb代码。我提到了这个解决方案。下面的错误以最小代码打印,以重现我的错误。 错误 密码 你好原型 main_test.go

  • 我在使用Yii2进行Codeception时遇到了一些问题。我刚刚升级到Yii 2.0.10,一直在使用本指南 我收到错误:

  • null 我知道这远不是理想的情况...它更像是一个中间步骤,而我们试图改进一个几乎没有测试的遗留项目... 我的问题是:在Sonar的Module1仪表板中执行集成测试,最好的方法是什么? 最初,我倾向于将module1-itest中的代码移到module1,并使用Failsafe插件和与Jacoco的集成来运行它们。这样,Module1将混合使用JUnit单元测试和TestNG集成测试,每组测

  • 我目前正在与CDI Unit合作一个项目,我遇到了一个奇怪的问题。我试图在一个简单的项目中重现它: 我有一个使用CdiRunner运行的测试类(如下所述:http://jglue.org/cdi-unit-user-guide/我的测试类注入了被测试的单元:UUD。这个类扩展了一个超级类“ParentTestClass”,它目前是无用的。 测试课。爪哇: 正如我提到的,父类是空的。 ParentT