{"id":1119,"date":"2019-01-31T14:17:24","date_gmt":"2019-01-31T06:17:24","guid":{"rendered":"http:\/\/van-yzt.com\/?p=1119"},"modified":"2019-01-31T14:17:24","modified_gmt":"2019-01-31T06:17:24","slug":"grpc-java-unit-test","status":"publish","type":"post","link":"https:\/\/huzi-baozi.com\/?p=1119","title":{"rendered":"gRPC-Java Unit Test"},"content":{"rendered":"<p>\u91c7\u7528In-Process\u4f5c\u4e3aTransport\u534f\u8bae\uff0c\u76f4\u63a5\u751f\u6210\u4e00\u4e2a\u672c\u5730\u7684Server\uff0c\u5c06Server\u7aefmock\uff0c\u4ece\u800c\u5b9e\u73b0Clinet\u7aef\u7684\u4ee3\u7801\u6d4b\u8bd5\u3002<a href=\"https:\/\/github.com\/grpc\/grpc-java\/issues\/1469#issuecomment-226873511\">gRPC\u7684\u7ef4\u62a4\u8005\u4eec\u4e0d\u5141\u8bb8Client\u53bb\u505amock<\/a>\u3002<\/p>\n<ol>\n<li>\n\u5b9e\u73b0\u672c\u5730In-Process\u7684Server\u7aef\uff1b<\/p>\n<ol>\n<li>\n\u5b9e\u73b0\u7531Protocol Buffer\u751f\u6210\u7684ServiceBase\u5bf9\u8c61\uff0c\u8986\u76d6\u5b83\u7684\u5177\u4f53\u65b9\u6cd5\uff1b\uff08\u8be5\u7c7b\u7684\u4f5c\u7528\u5c31\u662fserver\u6267\u884c\u7684\u5177\u4f53\u5b9e\u73b0\uff09<\/li>\n<li>\n\u5c06\u4e0a\u8ff0\u5b9e\u73b0\u6ce8\u518c\u5230\u672c\u5730In-Process Server\uff1b\uff08\u4e4b\u540eClient\u8c03\u7528Server\u5c31\u80fd\u7531\u6211\u4eec\u81ea\u5df1\u5b9e\u73b0\u7684ServiceBase\u6765\u5b8c\u6210\u6570\u636e\u7684mock\uff0c\u5b98\u65b9\u751f\u6210\u7684\u7c7b\u91cc\uff0c\u5b9e\u73b0\u662f\u629b\u9519\u2026\u2026\uff09<\/li>\n<\/ol>\n<\/li>\n<li>\n\u83b7\u53d6In-Process Server\u4e2d\u7684Channel\uff0c\u4ee5\u63d0\u4f9bClient\u751f\u6210Stub\uff1b<\/li>\n<li>\n\u8dd1\u6d4b\u8bd5\uff1b<\/li>\n<\/ol>\n<p>\u4f9d\u7167\u4e0a\u8ff0\u6b65\u9aa4\uff0c\u7ed3\u5408<code>\u539f\u529b\u9879\u76ee\uff08\u4ee3\u7801\u670d\u52a1\uff09<\/code>\u573a\u666f\uff1a<br \/>\n\u6211\u4eec\u9700\u8981\u5bf9\u67d0\u4e00\u4e2a\u8def\u5f84\u4e0b\u7684\u4ed3\u5e93\u68c0\u6d4b\u5176\u662f\u5426\u5b58\uff0c\u7528gRPC\u534f\u8bae\u8c03\u7528\u8fdc\u7aef\u7684Server\u5b8c\u6210\u3002<\/p>\n<h2>\nProjects.java(\u4e1a\u52a1Service)<\/h2>\n<pre><code class=\"language-Java\">   \/** \n     *{@inheritDoc}\n     *\n     * @param projectPathWithNamespace\n     * @return\n     *\/\n    @Override\n    public Optional&lt;Projects&gt; isExist(@NonNull String projectPathWithNamespace) {\n        Optional&lt;Projects&gt; projectsOptional = projectsDao.findByNamespace(projectPathWithNamespace);\n        if (projectsOptional.isPresent()) {\n            RepositoryExistsRequest repositoryExistsRequest = RepositoryExistsRequest.newBuilder()\n                .setRepository(GrpcUtil.generateRepository(projectPathWithNamespace)).build();\n            RepositoryServiceBlockingStub stub = grpcStub.get(RepositoryServiceBlockingStub.class,\n                GrpcUtil.getNamespace(projectPathWithNamespace));\n            RepositoryExistsResponse response = stub.repositoryExists(repositoryExistsRequest);\n            \/\/ todo \u901a\u77e5\u6e05\u7406\u810f\u6570\u636e\n            if (!response.getExists()) {\n                return Optional.empty();\n            }\n        }\n        return projectsOptional;\n    }\n<\/code><\/pre>\n<p>\u7cfb\u7edf\u5728\u8fd9\u91cc\u7edf\u4e00\u5904\u7406\u4e86stub\u7684\u751f\u6210\uff1a<code>grpcStub.get(...)<\/code><\/p>\n<h2>\n\u6269\u5c55RepositoryServiceGrpc\u4e0b\u7684RepositoryServiceImplBase\uff08Protocol Buffer\u81ea\u52a8\u751f\u6210Service\uff09<\/h2>\n<pre><code class=\"language-Java\">\/**\n * Extend {@link RepositoryServiceGrpc.RepositoryServiceImplBase} in order to implement the mock method.\n *\n * @author van.yzt\n * @date 2017\/09\/27\n *\/\npublic class MockRepositoryServiceImplBase extends RepositoryServiceGrpc.RepositoryServiceImplBase {\n\n    \/**\n     * Implement the exist check method.\n     *\n     * @param request\n     * @param responseObserver\n     *\/\n    @Override\n    public void repositoryExists(RepositoryExistsRequest request,\n        StreamObserver&lt;RepositoryExistsResponse&gt; responseObserver) {\n        responseObserver.onNext(RepositoryExistsResponse.newBuilder().setExists(true).build());\n        responseObserver.onCompleted();\n    }\n}\n<\/code><\/pre>\n<h2>\nProjectsTest.java<\/h2>\n<pre><code class=\"language-Java\">@Rule\nprivate GrpcStub grpcStub = new MockGrpcStub();\n...\n\n@Before\n\u2028public void setUp() throws Exception {\u2028\n    RepositoryServiceImplBase repositoryServiceImplBase = Mockito.spy(new MockRepositoryServiceImplBase() {});\n    \/\/ \u6ce8\u518c\u5230In-Process\u4e2d\u2028\n    grpcStub.getServiceRegistry().addService(repositoryServiceImplBase);\u2028\n}\n\n\n @Test\n    public void isExistTest() throws Exception {\n        Mockito.when(mockProjectsDao.findByNamespace(TestDataBuilder.ABSOLUTE_PATH)).thenReturn(\n            Optional.of(TestDataBuilder.buildProjects()));\n        Assert.assertNotNull(projects.isExist(TestDataBuilder.ABSOLUTE_PATH));\n    }\n\n<\/code><\/pre>\n<h2>\nMockGrpcStub.java\uff08\u4e1a\u52a1\u76f8\u5173\u7684\u5c01\u88c5\uff0c\u7528\u4e8e\u751f\u6210Stub\u7684Service\uff09<\/h2>\n<pre><code class=\"language-Java\">\/**\n * This stub generate service is used for unit test.\n *\n * @author van.yzt\n * @date 2017\/09\/27\n *\/\npublic class MockGrpcStub extends ExternalResource implements AbstractMockGrpcStub {\n    private ManagedChannel channel;\n    private Server server;\n    private String serverName;\n    private MutableHandlerRegistry serviceRegistry;\n    private boolean useDirectExecutor;\n\n    public AbstractMockGrpcStub directExecutor() {\n        useDirectExecutor = true;\n        return this;\n    }\n\n\n    \/**\n     * \u83b7\u53d6\u56fa\u5b9a\u7684headers\n     *\n     * @param namespace\n     * @param timestamp\n     * @return\n     *\/\n    private Map&lt;String, String&gt; headers(@NonNull String namespace, @NonNull Long timestamp) {\n        final Map&lt;String, String&gt; headers = new HashMap&lt;&gt;(2);\n        \/\/ Some operations\n        return headers;\n    }\n\n    @SuppressWarnings(&quot;unchecked&quot;)\n    @Override\n    public &lt;T extends AbstractStub&lt;T&gt;&gt; T get(@NonNull Class&lt;T&gt; stubClass, @NonNull final String namespace) {\n        try {\n            Constructor constructor = stubClass.getDeclaredConstructor(Channel.class);\n            constructor.setAccessible(true);\n            AbstractStub stub = (AbstractStub)constructor.newInstance(channel);\n            \/**\n             * \u4e0d\u9700\u8981\u5173\u5fc3\u5e76\u53d1\n             *\/\n            stub = attachHeaders(stub, headers(namespace, System.currentTimeMillis()));\n            return (T)stub;\n        } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {\n            throw new GrpcStubCreateException(&quot;gRPC create stub failed.&quot;, e);\n        } catch (NoSuchMethodException e) {\n            \/**\n             * This place means Google Protocol Buffers generate result format changed !!!!!\n             *\n             * When writing this code, the XXXBlockingStub class has private constructor like\n             * 'XXXBlockingStub(io.grpc.Channel)'\n             *\/\n            throw new GrpcStubCreateException(&quot;gRPC create stub failed because of the format changed.&quot;, e);\n        }\n    }\n\n    \/**\n     * Returns a {@link ManagedChannel} connected to this service.\n     *\/\n    public final ManagedChannel getChannel() {\n        return channel;\n    }\n\n    \/**\n     * Returns the underlying gRPC {@link Server} for this service.\n     *\/\n    public final Server getServer() {\n        return server;\n    }\n\n    \/**\n     * Returns the randomly generated server name for this service.\n     *\/\n    public final String getServerName() {\n        return serverName;\n    }\n\n    \/**\n     * Returns the service registry for this service. The registry is used to add service instances\n     * (e.g. {@link io.grpc.BindableService} or {@link io.grpc.ServerServiceDefinition} to the server.\n     *\/\n    public final MutableHandlerRegistry getServiceRegistry() {\n        return serviceRegistry;\n    }\n\n    \/**\n     * After the test has completed, clean up the channel and server.\n     *\/\n    @Override\n    protected void after() {\n        serverName = null;\n        serviceRegistry = null;\n\n        channel.shutdown();\n        server.shutdown();\n\n        try {\n            channel.awaitTermination(1, TimeUnit.MINUTES);\n            server.awaitTermination(1, TimeUnit.MINUTES);\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new RuntimeException(e);\n        } finally {\n            channel.shutdownNow();\n            channel = null;\n\n            server.shutdownNow();\n            server = null;\n        }\n    }\n\n    \/**\n     * Before the test has started, create the server and channel.\n     *\/\n    @Override\n    protected void before() throws Throwable {\n        serverName = UUID.randomUUID().toString();\n\n        serviceRegistry = new MutableHandlerRegistry();\n\n        InProcessServerBuilder serverBuilder = InProcessServerBuilder.forName(serverName)\n            .fallbackHandlerRegistry(serviceRegistry);\n\n        if (useDirectExecutor) {\n            serverBuilder.directExecutor();\n        }\n\n        server = serverBuilder.build().start();\n\n        InProcessChannelBuilder channelBuilder = InProcessChannelBuilder.forName(serverName);\n\n        if (useDirectExecutor) {\n            channelBuilder.directExecutor();\n        }\n\n        channel = channelBuilder.build();\n    }\n\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u91c7\u7528In-Process\u4f5c\u4e3aTransport\u534f\u8bae\uff0c\u76f4\u63a5\u751f\u6210\u4e00\u4e2a\u672c\u5730\u7684Server\uff0c\u5c06Server\u7aefmock\uff0c\u4ece\u800c\u5b9e\u73b0Clinet\u7aef\u7684\u4ee3\u7801\u6d4b\u8bd5\u3002gRPC\u7684\u7ef4\u62a4\u8005\u4eec\u4e0d\u5141\u8bb8Client\u53bb\u505amock\u3002 \u5b9e\u73b0\u672c\u5730In-Process\u7684Server\u7aef\uff1b \u5b9e\u73b0\u7531Protocol Buffer\u751f\u6210\u7684ServiceBase\u5bf9\u8c61\uff0c\u8986\u76d6\u5b83\u7684\u5177\u4f53\u65b9\u6cd5\uff1b\uff08\u8be5\u7c7b\u7684\u4f5c\u7528\u5c31\u662fserver\u6267\u884c\u7684\u5177\u4f53\u5b9e\u73b0\uff09 \u5c06\u4e0a\u8ff0\u5b9e\u73b0\u6ce8\u518c\u5230\u672c\u5730In-Process Server\uff1b\uff08\u4e4b\u540eClient\u8c03\u7528Server\u5c31\u80fd\u7531\u6211\u4eec\u81ea\u5df1\u5b9e\u73b0\u7684ServiceBase\u6765\u5b8c\u6210\u6570\u636e\u7684mock\uff0c\u5b98\u65b9\u751f\u6210\u7684\u7c7b\u91cc\uff0c\u5b9e\u73b0\u662f\u629b\u9519\u2026\u2026\uff09 \u83b7\u53d6In-Process Server\u4e2d\u7684Channel\uff0c\u4ee5\u63d0\u4f9bClient\u751f\u6210Stub\uff1b \u8dd1\u6d4b\u8bd5\uff1b \u4f9d\u7167\u4e0a\u8ff0\u6b65\u9aa4\uff0c\u7ed3\u5408\u539f\u529b\u9879\u76ee\uff08\u4ee3\u7801\u670d\u52a1\uff09\u573a\u666f\uff1a \u6211\u4eec\u9700\u8981\u5bf9\u67d0\u4e00\u4e2a\u8def\u5f84\u4e0b\u7684\u4ed3\u5e93\u68c0\u6d4b\u5176\u662f\u5426\u5b58\uff0c\u7528gRPC\u534f\u8bae\u8c03\u7528\u8fdc\u7aef\u7684Server\u5b8c\u6210\u3002 Projects.java(\u4e1a\u52a1Service) \/** *{@inheritDoc} * * @param projectPathWithNamespace * @return *\/ @Override public Optional&lt;Projects&gt; isExist(@NonNull String projectPathWithNamespace) { Optional&lt;Projects&gt; projectsOptional = projectsDao.findByNamespace(projectPathWithNamespace); if (projectsOptional.isPresent()) { RepositoryExistsRequest repositoryExistsRequest = RepositoryExistsRequest.newBuilder() .setRepository(GrpcUtil.generateRepository(projectPathWithNamespace)).build(); RepositoryServiceBlockingStub stub = grpcStub.get(RepositoryServiceBlockingStub.class, GrpcUtil.getNamespace(projectPathWithNamespace)); RepositoryExistsResponse response = stub.repositoryExists(repositoryExistsRequest); \/\/ todo \u901a\u77e5\u6e05\u7406\u810f\u6570\u636e if (!response.getExists()) { &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/huzi-baozi.com\/?p=1119\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;gRPC-Java Unit Test&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-1119","post","type-post","status-publish","format-standard","hentry","category-snippet"],"_links":{"self":[{"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=\/wp\/v2\/posts\/1119","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1119"}],"version-history":[{"count":1,"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=\/wp\/v2\/posts\/1119\/revisions"}],"predecessor-version":[{"id":1120,"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=\/wp\/v2\/posts\/1119\/revisions\/1120"}],"wp:attachment":[{"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/huzi-baozi.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}