You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/10/21 03:07:24 UTC

[dubbo] branch 3.0 updated: Add some unit tests for remoting module (#9043)

This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new 3b32a26  Add some unit tests for remoting module (#9043)
3b32a26 is described below

commit 3b32a263c698c9e3b1155bfcaf457d55977f0f4d
Author: 灼华 <43...@users.noreply.github.com>
AuthorDate: Thu Oct 21 11:07:09 2021 +0800

    Add some unit tests for remoting module (#9043)
    
    * Add unit test for remoting module
    
    * FIX
    
    * Add unit test & fix check style
    
    * Update ChannelHandlersTest.java
    
    * Update RequestTest.java
    
    * Update PortUnificationExchangerTest.java
    
    * Update RequestTest.java
    
    * Add some unit tests for remoting module
    
    * Add license
---
 .../java/org/apache/dubbo/remoting/Decodeable.java |   2 +-
 .../org/apache/dubbo/remoting/IdleSensible.java    |   4 +-
 .../exchange/PortUnificationExchanger.java         |   5 +
 .../remoting/exchange/codec/ExchangeCodec.java     |   2 -
 .../support/ExchangeHandlerDispatcher.java         |  12 +--
 .../exchange/support/ReplierDispatcher.java        |   3 +-
 .../support/header/HeaderExchangeHandler.java      |   2 +-
 .../exchange/support/header/HeartbeatHandler.java  |  10 +-
 .../dubbo/remoting/telnet/codec/TelnetCodec.java   |   6 +-
 .../dubbo/remoting/transport/AbstractServer.java   |   2 +-
 .../dispatcher/WrappedChannelHandler.java          |   2 +-
 .../dispatcher/all/AllChannelHandler.java          |   6 +-
 .../ConnectionOrderedChannelHandler.java           |  10 +-
 .../dubbo/remoting/codec/ExchangeCodecTest.java    |  10 +-
 .../dubbo/remoting/codec/TelnetCodecTest.java      |  40 +++----
 .../exchange/PortUnificationExchangerTest.java     |  40 +++++++
 .../dubbo/remoting/exchange/RequestTest.java       |  50 +++++++++
 .../dubbo/remoting/exchange/ResponseTest.java      |  43 ++++++++
 .../support/ExchangeHandlerDispatcherTest.java     |  65 ++++++++++++
 .../exchange/support/MultiMessageTest.java         |  66 ++++++++++++
 .../handler/ConnectChannelHandlerTest.java         |  34 +++---
 .../handler/HeaderExchangeHandlerTest.java         | 104 +++++++++++-------
 .../handler/WrappedChannelHandlerTest.java         |  51 +++++++--
 .../remoting/transport/AbstractCodecTest.java      |  51 +++++++--
 .../transport/ChannelHandlerDispatcherTest.java    | 117 +++++++++++++++++++++
 .../remoting/transport/DecodeHandlerTest.java      |  81 ++++++++++++++
 .../transport/MultiMessageHandlerTest.java         |  66 ++++++++++++
 .../dispatcher/ChannelEventRunnableTest.java       |  61 +++++++++++
 .../transport/dispatcher/ChannelHandlersTest.java  |  47 +++++++++
 .../remoting/transport/netty4/NettyChannel.java    |   2 +-
 .../transport/netty4/NettyCodecAdapter.java        |   2 +-
 .../remoting/transport/netty4/NettyServer.java     |  10 +-
 .../transport/netty4/ClientToServerTest.java       |   2 -
 .../transport/netty4/NettyChannelTest.java         | 117 +++++++++++++++++++++
 .../transport/netty4/NettyClientHandlerTest.java   |  85 +++++++++++++++
 .../transport/netty4/NettyCodecAdapterTest.java    |  44 ++++++++
 36 files changed, 1116 insertions(+), 138 deletions(-)

diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Decodeable.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Decodeable.java
index 3852108..a913ef8 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Decodeable.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Decodeable.java
@@ -19,6 +19,6 @@ package org.apache.dubbo.remoting;
 
 public interface Decodeable {
 
-    public void decode() throws Exception;
+    void decode() throws Exception;
 
 }
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/IdleSensible.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/IdleSensible.java
index e1f973b..ad4fed2 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/IdleSensible.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/IdleSensible.java
@@ -23,10 +23,10 @@ package org.apache.dubbo.remoting;
  */
 public interface IdleSensible {
     /**
-     * Whether the implementation can sense and handle the idle connection. By default it's false, the implementation
+     * Whether the implementation can sense and handle the idle connection. By default, it's false, the implementation
      * relies on dedicated timer to take care of idle connection.
      *
-     * @return whether has the ability to handle idle connection
+     * @return whether it has the ability to handle idle connection
      */
     default boolean canHandleIdle() {
         return false;
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
index a90f463..3b0fd38 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java
@@ -50,4 +50,9 @@ public class PortUnificationExchanger {
             }
         }
     }
+
+    // for test
+    public static ConcurrentMap<String, PortUnificationServer> getServers() {
+        return servers;
+    }
 }
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java
index 0063576..87d84fb 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java
@@ -493,8 +493,6 @@ public class ExchangeCodec extends TelnetCodec {
                 if ((flag & FLAG_EVENT) != 0) {
                     res.setEvent(true);
                 }
-                // get status.
-                byte status = header[3];
                 res.setStatus(Response.CLIENT_ERROR);
                 String errorMsg = "Data length too large: " + size + ", max payload: " + payload + ", channel: " + channel;
                 logger.error(errorMsg);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java
index f3a8337..ea1736c 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcher.java
@@ -39,21 +39,15 @@ public class ExchangeHandlerDispatcher implements ExchangeHandler {
     private final TelnetHandler telnetHandler;
 
     public ExchangeHandlerDispatcher() {
-        replierDispatcher = new ReplierDispatcher();
-        handlerDispatcher = new ChannelHandlerDispatcher();
-        telnetHandler = new TelnetHandlerAdapter();
+        this(null, null);
     }
 
     public ExchangeHandlerDispatcher(Replier<?> replier) {
-        replierDispatcher = new ReplierDispatcher(replier);
-        handlerDispatcher = new ChannelHandlerDispatcher();
-        telnetHandler = new TelnetHandlerAdapter();
+        this(replier, null);
     }
 
     public ExchangeHandlerDispatcher(ChannelHandler... handlers) {
-        replierDispatcher = new ReplierDispatcher();
-        handlerDispatcher = new ChannelHandlerDispatcher(handlers);
-        telnetHandler = new TelnetHandlerAdapter();
+        this(null, handlers);
     }
 
     public ExchangeHandlerDispatcher(Replier<?> replier, ChannelHandler... handlers) {
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ReplierDispatcher.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ReplierDispatcher.java
index 4cd8eb4..afb6052 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ReplierDispatcher.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/ReplierDispatcher.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.remoting.exchange.support;
 
+import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.remoting.RemotingException;
 import org.apache.dubbo.remoting.exchange.ExchangeChannel;
 
@@ -41,7 +42,7 @@ public class ReplierDispatcher implements Replier<Object> {
 
     public ReplierDispatcher(Replier<?> defaultReplier, Map<Class<?>, Replier<?>> repliers) {
         this.defaultReplier = defaultReplier;
-        if (repliers != null && repliers.size() > 0) {
+        if (CollectionUtils.isNotEmptyMap(repliers)) {
             this.repliers.putAll(repliers);
         }
     }
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java
index 5f03b50..94dbd4c 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeHandler.java
@@ -185,7 +185,7 @@ public class HeaderExchangeHandler implements ChannelHandlerDelegate {
                 logger.error(e.getMessage(), e);
             } else {
                 String echo = handler.telnet(channel, (String) message);
-                if (echo != null && echo.length() > 0) {
+                if (StringUtils.isNotEmpty(echo)) {
                     channel.send(echo);
                 }
             }
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandler.java
index 9f49848..38a10e3 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandler.java
@@ -70,13 +70,11 @@ public class HeartbeatHandler extends AbstractChannelHandlerDelegate {
                 Response res = new Response(req.getId(), req.getVersion());
                 res.setEvent(HEARTBEAT_EVENT);
                 channel.send(res);
-                if (logger.isInfoEnabled()) {
+                if (logger.isDebugEnabled()) {
                     int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
-                    if (logger.isDebugEnabled()) {
-                        logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
-                                + ", cause: The channel has no data-transmission exceeds a heartbeat period"
-                                + (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
-                    }
+                    logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
+                        + ", cause: The channel has no data-transmission exceeds a heartbeat period"
+                        + (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
                 }
             }
             return;
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/codec/TelnetCodec.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/codec/TelnetCodec.java
index d97e4cc..6ecfbf2 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/codec/TelnetCodec.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/telnet/codec/TelnetCodec.java
@@ -177,8 +177,8 @@ public class TelnetCodec extends TransportCodec {
 
         if (message[message.length - 1] == '\b') { // Windows backspace echo
             try {
-                boolean doublechar = message.length >= 3 && message[message.length - 3] < 0; // double byte char
-                channel.send(new String(doublechar ? new byte[]{32, 32, 8, 8} : new byte[]{32, 8}, getCharset(channel).name()));
+                boolean isDoubleChar = message.length >= 3 && message[message.length - 3] < 0; // double byte char
+                channel.send(new String(isDoubleChar ? new byte[]{32, 32, 8, 8} : new byte[]{32, 8}, getCharset(channel).name()));
             } catch (RemotingException e) {
                 throw new IOException(StringUtils.toString(e));
             }
@@ -234,7 +234,7 @@ public class TelnetCodec extends TransportCodec {
                     for (int i = 0; i < ov.length(); i++) {
                         buf.append('\b');
                     }
-                    value = buf.toString() + value;
+                    value = buf + value;
                 }
                 try {
                     channel.send(value);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java
index 8a6ed0c..ebf880d 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractServer.java
@@ -44,7 +44,7 @@ public abstract class AbstractServer extends AbstractEndpoint implements Remotin
 
     protected static final String SERVER_THREAD_POOL_NAME = "DubboServerHandler";
     private static final Logger logger = LoggerFactory.getLogger(AbstractServer.class);
-    ExecutorService executor;
+    private ExecutorService executor;
     private InetSocketAddress localAddress;
     private InetSocketAddress bindAddress;
     private int accepts;
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java
index 7742d04..7c307b8 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/WrappedChannelHandler.java
@@ -111,7 +111,7 @@ public class WrappedChannelHandler implements ChannelHandlerDelegate {
         if (msg instanceof Response) {
             Response response = (Response) msg;
             DefaultFuture responseFuture = DefaultFuture.getFuture(response.getId());
-            // a typical scenario is the response returned after timeout, the timeout response may has completed the future
+            // a typical scenario is the response returned after timeout, the timeout response may have completed the future
             if (responseFuture == null) {
                 return getSharedExecutorService();
             } else {
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java
index 422e8ec..d5fa975 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/all/AllChannelHandler.java
@@ -37,7 +37,7 @@ public class AllChannelHandler extends WrappedChannelHandler {
 
     @Override
     public void connected(Channel channel) throws RemotingException {
-        ExecutorService executor = getExecutorService();
+        ExecutorService executor = getSharedExecutorService();
         try {
             executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED));
         } catch (Throwable t) {
@@ -47,7 +47,7 @@ public class AllChannelHandler extends WrappedChannelHandler {
 
     @Override
     public void disconnected(Channel channel) throws RemotingException {
-        ExecutorService executor = getExecutorService();
+        ExecutorService executor = getSharedExecutorService();
         try {
             executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.DISCONNECTED));
         } catch (Throwable t) {
@@ -71,7 +71,7 @@ public class AllChannelHandler extends WrappedChannelHandler {
 
     @Override
     public void caught(Channel channel, Throwable exception) throws RemotingException {
-        ExecutorService executor = getExecutorService();
+        ExecutorService executor = getSharedExecutorService();
         try {
             executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CAUGHT, exception));
         } catch (Throwable t) {
diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java
index 2132cac..cad5cf4 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/dispatcher/connection/ConnectionOrderedChannelHandler.java
@@ -43,7 +43,7 @@ import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_QUEUE_WARNING_
 public class ConnectionOrderedChannelHandler extends WrappedChannelHandler {
 
     protected final ThreadPoolExecutor connectionExecutor;
-    private final int queuewarninglimit;
+    private final int queueWarningLimit;
 
     public ConnectionOrderedChannelHandler(ChannelHandler handler, URL url) {
         super(handler, url);
@@ -54,7 +54,7 @@ public class ConnectionOrderedChannelHandler extends WrappedChannelHandler {
                 new NamedThreadFactory(threadName, true),
                 new AbortPolicyWithReport(threadName, url)
         );  // FIXME There's no place to release connectionExecutor!
-        queuewarninglimit = url.getParameter(CONNECT_QUEUE_WARNING_SIZE, DEFAULT_CONNECT_QUEUE_WARNING_SIZE);
+        queueWarningLimit = url.getParameter(CONNECT_QUEUE_WARNING_SIZE, DEFAULT_CONNECT_QUEUE_WARNING_SIZE);
     }
 
     @Override
@@ -93,7 +93,7 @@ public class ConnectionOrderedChannelHandler extends WrappedChannelHandler {
 
     @Override
     public void caught(Channel channel, Throwable exception) throws RemotingException {
-        ExecutorService executor = getExecutorService();
+        ExecutorService executor = getSharedExecutorService();
         try {
             executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CAUGHT, exception));
         } catch (Throwable t) {
@@ -102,8 +102,8 @@ public class ConnectionOrderedChannelHandler extends WrappedChannelHandler {
     }
 
     private void checkQueueLength() {
-        if (connectionExecutor.getQueue().size() > queuewarninglimit) {
-            logger.warn(new IllegalThreadStateException("connectionordered channel handler `queue size: " + connectionExecutor.getQueue().size() + " exceed the warning limit number :" + queuewarninglimit));
+        if (connectionExecutor.getQueue().size() > queueWarningLimit) {
+            logger.warn(new IllegalThreadStateException("connectionordered channel handler queue size: " + connectionExecutor.getQueue().size() + " exceed the warning limit number :" + queueWarningLimit));
         }
     }
 }
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java
index d801c64..495092d 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/ExchangeCodecTest.java
@@ -351,7 +351,7 @@ public class ExchangeCodecTest extends TelnetCodecTest {
     @Test
     public void test_Encode_Request() throws IOException {
         ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(2014);
-        Channel channel = getCliendSideChannel(url);
+        Channel channel = getClientSideChannel(url);
         Request request = new Request();
         Person person = new Person();
         request.setData(person);
@@ -372,7 +372,7 @@ public class ExchangeCodecTest extends TelnetCodecTest {
     @Test
     public void test_Encode_Response() throws IOException {
         ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(1024);
-        Channel channel = getCliendSideChannel(url);
+        Channel channel = getClientSideChannel(url);
         Response response = new Response();
         response.setHeartbeat(true);
         response.setId(1001L);
@@ -401,7 +401,7 @@ public class ExchangeCodecTest extends TelnetCodecTest {
     @Test
     public void test_Encode_Error_Response() throws IOException {
         ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(1024);
-        Channel channel = getCliendSideChannel(url);
+        Channel channel = getClientSideChannel(url);
         Response response = new Response();
         response.setHeartbeat(true);
         response.setId(1001L);
@@ -429,7 +429,7 @@ public class ExchangeCodecTest extends TelnetCodecTest {
 
     @Test
     public void testMessageLengthGreaterThanMessageActualLength() throws Exception {
-        Channel channel = getCliendSideChannel(url);
+        Channel channel = getClientSideChannel(url);
         Request request = new Request(1L);
         request.setVersion(Version.getProtocolVersion());
         Date date = new Date();
@@ -466,7 +466,7 @@ public class ExchangeCodecTest extends TelnetCodecTest {
         Request request = new Request(1L);
         request.setData("hello");
         ChannelBuffer encodeBuffer = ChannelBuffers.dynamicBuffer(512);
-        AbstractMockChannel channel = getCliendSideChannel(url.addParameter(Constants.PAYLOAD_KEY, 4));
+        AbstractMockChannel channel = getClientSideChannel(url.addParameter(Constants.PAYLOAD_KEY, 4));
         try {
             codec.encode(channel, encodeBuffer, request);
             Assertions.fail();
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/TelnetCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/TelnetCodecTest.java
index 8b16c1b..ff7f648 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/TelnetCodecTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/codec/TelnetCodecTest.java
@@ -59,7 +59,7 @@ public class TelnetCodecTest {
         return channel;
     }
 
-    protected AbstractMockChannel getCliendSideChannel(URL url) {
+    protected AbstractMockChannel getClientSideChannel(URL url) {
         url = url.addParameter(AbstractMockChannel.LOCAL_ADDRESS, "127.0.0.1:12345")
                 .addParameter(AbstractMockChannel.REMOTE_ADDRESS, url.getAddress());
         AbstractMockChannel channel = new AbstractMockChannel(url);
@@ -92,7 +92,7 @@ public class TelnetCodecTest {
                 throw new RuntimeException(e);
             }
         }
-        return (bytes);
+        return bytes;
     }
 
     protected Object byteToObject(byte[] objBytes) throws Exception {
@@ -110,7 +110,7 @@ public class TelnetCodecTest {
 
     protected void testDecode_assertEquals(byte[] request, Object ret, boolean isServerside) throws IOException {
         //init channel
-        Channel channel = isServerside ? getServerSideChannel(url) : getCliendSideChannel(url);
+        Channel channel = isServerside ? getServerSideChannel(url) : getClientSideChannel(url);
         //init request string
         ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(request);
 
@@ -122,7 +122,7 @@ public class TelnetCodecTest {
 
     protected void testEecode_assertEquals(Object request, byte[] ret, boolean isServerside) throws IOException {
         //init channel
-        Channel channel = isServerside ? getServerSideChannel(url) : getCliendSideChannel(url);
+        Channel channel = isServerside ? getServerSideChannel(url) : getClientSideChannel(url);
 
         ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(1024);
 
@@ -146,7 +146,7 @@ public class TelnetCodecTest {
         testDecode_assertEquals(null, request, ret, channelReceive);
     }
 
-    private void testDecode_assertEquals(AbstractMockChannel channel, Object request, Object expectret, Object channelReceive) throws IOException {
+    private void testDecode_assertEquals(AbstractMockChannel channel, Object request, Object expectRet, Object channelReceive) throws IOException {
         //init channel
         if (channel == null) {
             channel = getServerSideChannel(url);
@@ -157,21 +157,21 @@ public class TelnetCodecTest {
 
         //decode
         Object obj = codec.decode(channel, buffer);
-        Assertions.assertEquals(expectret, obj);
+        Assertions.assertEquals(expectRet, obj);
         Assertions.assertEquals(channelReceive, channel.getReceivedMessage());
     }
 
-    private void testDecode_PersonWithEnterByte(byte[] enterbytes, boolean isNeedmore) throws IOException {
+    private void testDecode_PersonWithEnterByte(byte[] enterBytes, boolean isNeedMore) throws IOException {
         //init channel
         Channel channel = getServerSideChannel(url);
         //init request string
         Person request = new Person();
-        byte[] newbuf = join(objectToByte(request), enterbytes);
-        ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(newbuf);
+        byte[] newBuf = join(objectToByte(request), enterBytes);
+        ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(newBuf);
 
         //decode
         Object obj = codec.decode(channel, buffer);
-        if (isNeedmore) {
+        if (isNeedMore) {
             Assertions.assertEquals(Codec2.DecodeResult.NEED_MORE_INPUT, obj);
         } else {
             Assertions.assertTrue(obj instanceof String, "return must string ");
@@ -229,14 +229,14 @@ public class TelnetCodecTest {
 
     @Test
     public void testDecode_WithExitByte() throws IOException {
-        HashMap<byte[], Boolean> exitbytes = new HashMap<byte[], Boolean>();
-        exitbytes.put(new byte[]{3}, true); /* Windows Ctrl+C */
-        exitbytes.put(new byte[]{1, 3}, false); //must equal the bytes
-        exitbytes.put(new byte[]{-1, -12, -1, -3, 6}, true); /* Linux Ctrl+C */
-        exitbytes.put(new byte[]{1, -1, -12, -1, -3, 6}, false); //must equal the bytes
-        exitbytes.put(new byte[]{-1, -19, -1, -3, 6}, true);  /* Linux Pause */
-
-        for (Map.Entry<byte[], Boolean> entry : exitbytes.entrySet()) {
+        HashMap<byte[], Boolean> exitBytes = new HashMap<byte[], Boolean>();
+        exitBytes.put(new byte[]{3}, true); /* Windows Ctrl+C */
+        exitBytes.put(new byte[]{1, 3}, false); //must equal the bytes
+        exitBytes.put(new byte[]{-1, -12, -1, -3, 6}, true); /* Linux Ctrl+C */
+        exitBytes.put(new byte[]{1, -1, -12, -1, -3, 6}, false); //must equal the bytes
+        exitBytes.put(new byte[]{-1, -19, -1, -3, 6}, true);  /* Linux Pause */
+
+        for (Map.Entry<byte[], Boolean> entry : exitBytes.entrySet()) {
             testDecode_WithExitByte(entry.getKey(), entry.getValue());
         }
     }
@@ -263,7 +263,7 @@ public class TelnetCodecTest {
         });
     }
 
-    @Test()
+    @Test
     public void testDecode_History_UP() throws IOException {
         //init channel
         AbstractMockChannel channel = getServerSideChannel(url);
@@ -305,7 +305,7 @@ public class TelnetCodecTest {
         testEecode_assertEquals("aaa", "aaa\r\n".getBytes(), false);
     }
     
-    /*@Test()
+    /*@Test
     public void testDecode_History_UP_DOWN_MULTI() throws IOException{
         AbstractMockChannel channel = getServerSideChannel(url);
         
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/PortUnificationExchangerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/PortUnificationExchangerTest.java
new file mode 100644
index 0000000..3be5dfb
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/PortUnificationExchangerTest.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.exchange;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.remoting.Constants;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class PortUnificationExchangerTest {
+
+    @Test
+    public void test() {
+        URL url = new ServiceConfigURL(CommonConstants.TRIPLE, "localhost", 9103,
+            new String[]{Constants.BIND_PORT_KEY, String.valueOf(9103)});
+        PortUnificationExchanger.bind(url);
+        PortUnificationExchanger.bind(url);
+        Assertions.assertEquals(PortUnificationExchanger.getServers().size(), 1);
+
+        PortUnificationExchanger.close();
+        Assertions.assertEquals(PortUnificationExchanger.getServers().size(), 0);
+    }
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/RequestTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/RequestTest.java
new file mode 100644
index 0000000..9e7f14f
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/RequestTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.exchange;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class RequestTest {
+
+    @Test
+    public void test() {
+        Request request = new Request();
+        request.setTwoWay(true);
+        request.setBroken(true);
+        request.setVersion("1.0.0");
+        request.setEvent(true);
+        request.setData("data");
+
+        Assertions.assertTrue(request.isTwoWay());
+        Assertions.assertTrue(request.isBroken());
+        Assertions.assertTrue(request.isEvent());
+        Assertions.assertEquals(request.getVersion(), "1.0.0");
+        Assertions.assertEquals(request.getData(), "data");
+        Assertions.assertTrue(request.getId() >= 0);
+
+        request.setHeartbeat(true);
+        Assertions.assertTrue(request.isHeartbeat());
+
+        Request copiedRequest = request.copy();
+        Assertions.assertEquals(copiedRequest.toString(), request.toString());
+
+        Request copyWithoutData = request.copyWithoutData();
+        Assertions.assertNull(copyWithoutData.getData());
+    }
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ResponseTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ResponseTest.java
new file mode 100644
index 0000000..e4e1a63
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/ResponseTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.exchange;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT;
+
+public class ResponseTest {
+    @Test
+    public void test() {
+        Response response = new Response();
+        response.setStatus(Response.OK);
+        response.setId(1);
+        response.setVersion("1.0.0");
+        response.setResult("test");
+        response.setEvent(HEARTBEAT_EVENT);
+        response.setErrorMessage("errorMsg");
+
+        Assertions.assertTrue(response.isEvent());
+        Assertions.assertTrue(response.isHeartbeat());
+        Assertions.assertEquals(response.getVersion(), "1.0.0");
+        Assertions.assertEquals(response.getId(), 1);
+        Assertions.assertEquals(response.getResult(), HEARTBEAT_EVENT);
+        Assertions.assertEquals(response.getErrorMessage(), "errorMsg");
+    }
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcherTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcherTest.java
new file mode 100644
index 0000000..8e7482c
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/ExchangeHandlerDispatcherTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.exchange.support;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.exchange.ExchangeChannel;
+import org.apache.dubbo.remoting.telnet.support.TelnetHandlerAdapter;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Field;
+
+public class ExchangeHandlerDispatcherTest {
+
+    @Test
+    public void test() throws Exception {
+        ExchangeHandlerDispatcher exchangeHandlerDispatcher = new ExchangeHandlerDispatcher();
+
+        ChannelHandler channelHandler = Mockito.mock(ChannelHandler.class);
+        Replier replier = Mockito.mock(Replier.class);
+        TelnetHandlerAdapter telnetHandlerAdapter = Mockito.mock(TelnetHandlerAdapter.class);
+        exchangeHandlerDispatcher.addChannelHandler(channelHandler);
+        exchangeHandlerDispatcher.addReplier(ExchangeHandlerDispatcherTest.class, replier);
+        Field telnetHandlerField = exchangeHandlerDispatcher.getClass().getDeclaredField("telnetHandler");
+        telnetHandlerField.setAccessible(true);
+        telnetHandlerField.set(exchangeHandlerDispatcher, telnetHandlerAdapter);
+
+        Channel channel = Mockito.mock(Channel.class);
+        ExchangeChannel exchangeChannel = Mockito.mock(ExchangeChannel.class);
+        exchangeHandlerDispatcher.connected(channel);
+        exchangeHandlerDispatcher.disconnected(channel);
+        exchangeHandlerDispatcher.sent(channel, null);
+        exchangeHandlerDispatcher.received(channel, null);
+        exchangeHandlerDispatcher.caught(channel, null);
+        ExchangeHandlerDispatcherTest obj = new ExchangeHandlerDispatcherTest();
+        exchangeHandlerDispatcher.reply(exchangeChannel, obj);
+        exchangeHandlerDispatcher.telnet(channel, null);
+
+        Mockito.verify(channelHandler, Mockito.times(1)).connected(channel);
+        Mockito.verify(channelHandler, Mockito.times(1)).disconnected(channel);
+        Mockito.verify(channelHandler, Mockito.times(1)).sent(channel, null);
+        Mockito.verify(channelHandler, Mockito.times(1)).received(channel, null);
+        Mockito.verify(channelHandler, Mockito.times(1)).caught(channel, null);
+        Mockito.verify(replier, Mockito.times(1)).reply(exchangeChannel, obj);
+        Mockito.verify(telnetHandlerAdapter, Mockito.times(1)).telnet(channel, null);
+
+        exchangeHandlerDispatcher.removeChannelHandler(channelHandler);
+        exchangeHandlerDispatcher.removeReplier(ExchangeHandlerDispatcherTest.class);
+    }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/MultiMessageTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/MultiMessageTest.java
new file mode 100644
index 0000000..1aa57f3
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/exchange/support/MultiMessageTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.exchange.support;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * {@link MultiMessage}
+ */
+public class MultiMessageTest {
+
+    @Test
+    public void test() {
+        MultiMessage multiMessage = MultiMessage.create();
+        Assertions.assertTrue(multiMessage instanceof Iterable);
+
+        multiMessage.addMessage("test1");
+        multiMessage.addMessages(Arrays.asList("test2", "test3"));
+        Assertions.assertEquals(multiMessage.size(), 3);
+        Assertions.assertFalse(multiMessage.isEmpty());
+        Assertions.assertEquals(multiMessage.get(0), "test1");
+        Assertions.assertEquals(multiMessage.get(1), "test2");
+        Assertions.assertEquals(multiMessage.get(2), "test3");
+
+        Collection messages = multiMessage.getMessages();
+        Assertions.assertTrue(messages.contains("test1"));
+        Assertions.assertTrue(messages.contains("test2"));
+        Assertions.assertTrue(messages.contains("test3"));
+
+        Iterator iterator = messages.iterator();
+        Assertions.assertTrue(iterator.hasNext());
+        Assertions.assertEquals(iterator.next(), "test1");
+        Assertions.assertEquals(iterator.next(), "test2");
+        Assertions.assertEquals(iterator.next(), "test3");
+
+        Collection removedCollection = multiMessage.removeMessages();
+        Assertions.assertArrayEquals(removedCollection.toArray(), messages.toArray());
+        messages = multiMessage.getMessages();
+        Assertions.assertTrue(messages.isEmpty());
+
+        MultiMessage multiMessage1 = MultiMessage.createFromCollection(Arrays.asList("test1", "test2"));
+        MultiMessage multiMessage2 = MultiMessage.createFromArray("test1", "test2");
+        Assertions.assertArrayEquals(multiMessage1.getMessages().toArray(), multiMessage2.getMessages().toArray());
+
+    }
+
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java
index ea91756..2525e92 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/ConnectChannelHandlerTest.java
@@ -35,12 +35,12 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest {
 
     @BeforeEach
     public void setUp() throws Exception {
-        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(true), url);
     }
 
     @Test
-    public void test_Connect_Blocked() throws RemotingException {
-        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+    public void testConnectBlocked() throws RemotingException {
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(false), url);
         ThreadPoolExecutor executor = (ThreadPoolExecutor) getField(handler, "connectionExecutor", 1);
         Assertions.assertEquals(1, executor.getMaximumPoolSize());
 
@@ -63,21 +63,21 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest {
     }
 
     @Test //biz error should not throw and affect biz thread.
-    public void test_Connect_Biz_Error() throws RemotingException {
-        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+    public void testConnectBizError() throws RemotingException {
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(true), url);
         handler.connected(new MockedChannel());
     }
 
     @Test //biz error should not throw and affect biz thread.
-    public void test_Disconnect_Biz_Error() throws RemotingException {
-        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(true), url);
+    public void testDisconnectBizError() throws RemotingException {
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(true), url);
         handler.disconnected(new MockedChannel());
     }
 
     @Test
-    public void test_Connect_Execute_Error() throws RemotingException {
+    public void testConnectExecuteError() throws RemotingException {
         Assertions.assertThrows(ExecutionException.class, () -> {
-            handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+            handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(false), url);
             ThreadPoolExecutor executor = (ThreadPoolExecutor) getField(handler, "connectionExecutor", 1);
             executor.shutdown();
             handler.connected(new MockedChannel());
@@ -85,9 +85,9 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest {
     }
 
     @Test
-    public void test_Disconnect_Execute_Error() throws RemotingException {
+    public void testDisconnectExecuteError() throws RemotingException {
         Assertions.assertThrows(ExecutionException.class, () -> {
-            handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+            handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(false), url);
             ThreadPoolExecutor executor = (ThreadPoolExecutor) getField(handler, "connectionExecutor", 1);
             executor.shutdown();
             handler.disconnected(new MockedChannel());
@@ -96,21 +96,21 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest {
 
     //throw  ChannelEventRunnable.runtimeExeception(int logger) not in execute exception
     @Test//(expected = RemotingException.class)
-    public void test_MessageReceived_Biz_Error() throws RemotingException {
+    public void testMessageReceivedBizError() throws RemotingException {
         handler.received(new MockedChannel(), "");
     }
 
     //throw  ChannelEventRunnable.runtimeExeception(int logger) not in execute exception
     @Test
-    public void test_Caught_Biz_Error() throws RemotingException {
+    public void testCaughtBizError() throws RemotingException {
         handler.caught(new MockedChannel(), new BizException());
     }
 
     @Test
     @Disabled("FIXME")
-    public void test_Received_InvokeInExecuter() throws RemotingException {
+    public void testReceivedInvokeInExecutor() throws RemotingException {
         Assertions.assertThrows(ExecutionException.class, () -> {
-            handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+            handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(false), url);
             ThreadPoolExecutor executor = (ThreadPoolExecutor) getField(handler, "SHARED_EXECUTOR", 1);
             executor.shutdown();
             executor = (ThreadPoolExecutor) getField(handler, "executor", 1);
@@ -125,8 +125,8 @@ public class ConnectChannelHandlerTest extends WrappedChannelHandlerTest {
     @SuppressWarnings("deprecation")
     @Disabled("Heartbeat is processed in HeartbeatHandler not WrappedChannelHandler.")
     @Test
-    public void test_Received_Event_invoke_direct() throws RemotingException {
-        handler = new ConnectionOrderedChannelHandler(new BizChannelHander(false), url);
+    public void testReceivedEventInvokeDirect() throws RemotingException {
+        handler = new ConnectionOrderedChannelHandler(new BizChannelHandler(false), url);
         ThreadPoolExecutor executor = (ThreadPoolExecutor) getField(handler, "SHARED_EXECUTOR", 1);
         executor.shutdown();
         executor = (ThreadPoolExecutor) getField(handler, "executor", 1);
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java
index 5ec5e40..721ac56 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/HeaderExchangeHandlerTest.java
@@ -24,6 +24,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeChannel;
 import org.apache.dubbo.remoting.exchange.ExchangeHandler;
 import org.apache.dubbo.remoting.exchange.Request;
 import org.apache.dubbo.remoting.exchange.Response;
+import org.apache.dubbo.remoting.exchange.support.DefaultFuture;
 import org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler;
 
 import org.junit.jupiter.api.Assertions;
@@ -32,51 +33,52 @@ import org.junit.jupiter.api.Test;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT;
 import static org.apache.dubbo.common.constants.CommonConstants.READONLY_EVENT;
 
 //TODO response test
 public class HeaderExchangeHandlerTest {
 
     @Test
-    public void test_received_request_oneway() throws RemotingException {
-        final Channel mchannel = new MockedChannel();
+    public void testReceivedRequestOneway() throws RemotingException {
+        final Channel mockChannel = new MockedChannel();
 
-        final Person requestdata = new Person("charles");
+        final Person requestData = new Person("charles");
         Request request = new Request();
         request.setTwoWay(false);
-        request.setData(requestdata);
+        request.setData(requestData);
 
-        ExchangeHandler exhandler = new MockedExchangeHandler() {
+        ExchangeHandler exHandler = new MockedExchangeHandler() {
             @Override
             public void received(Channel channel, Object message) throws RemotingException {
-                Assertions.assertEquals(requestdata, message);
+                Assertions.assertEquals(requestData, message);
             }
         };
-        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(exhandler);
-        hexhandler.received(mchannel, request);
+        HeaderExchangeHandler headExHandler = new HeaderExchangeHandler(exHandler);
+        headExHandler.received(mockChannel, request);
     }
 
     @Test
-    public void test_received_request_twoway() throws RemotingException {
-        final Person requestdata = new Person("charles");
+    public void testReceivedRequestTwoway() throws RemotingException {
+        final Person requestData = new Person("charles");
         final Request request = new Request();
         request.setTwoWay(true);
-        request.setData(requestdata);
+        request.setData(requestData);
 
         final AtomicInteger count = new AtomicInteger(0);
-        final Channel mchannel = new MockedChannel() {
+        final Channel mockChannel = new MockedChannel() {
             @Override
             public void send(Object message) throws RemotingException {
                 Response res = (Response) message;
                 Assertions.assertEquals(request.getId(), res.getId());
                 Assertions.assertEquals(request.getVersion(), res.getVersion());
                 Assertions.assertEquals(Response.OK, res.getStatus());
-                Assertions.assertEquals(requestdata, res.getResult());
+                Assertions.assertEquals(requestData, res.getResult());
                 Assertions.assertNull(res.getErrorMessage());
                 count.incrementAndGet();
             }
         };
-        ExchangeHandler exhandler = new MockedExchangeHandler() {
+        ExchangeHandler exHandler = new MockedExchangeHandler() {
             @Override
             public CompletableFuture<Object> reply(ExchangeChannel channel, Object request) throws RemotingException {
                 return CompletableFuture.completedFuture(request);
@@ -87,25 +89,25 @@ public class HeaderExchangeHandlerTest {
                 Assertions.fail();
             }
         };
-        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(exhandler);
-        hexhandler.received(mchannel, request);
+        HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(exHandler);
+        headerExchangeHandler.received(mockChannel, request);
         Assertions.assertEquals(1, count.get());
     }
 
     @Test
-    public void test_received_request_twoway_error_nullhandler() throws RemotingException {
+    public void testReceivedRequestTwowayErrorWithNullHandler() throws RemotingException {
         Assertions.assertThrows(IllegalArgumentException.class, () -> new HeaderExchangeHandler(null));
     }
 
     @Test
-    public void test_received_request_twoway_error_reply() throws RemotingException {
-        final Person requestdata = new Person("charles");
+    public void testReceivedRequestTwowayErrorReply() throws RemotingException {
+        final Person requestData = new Person("charles");
         final Request request = new Request();
         request.setTwoWay(true);
-        request.setData(requestdata);
+        request.setData(requestData);
 
         final AtomicInteger count = new AtomicInteger(0);
-        final Channel mchannel = new MockedChannel() {
+        final Channel mockChannel = new MockedChannel() {
             @Override
             public void send(Object message) throws RemotingException {
                 Response res = (Response) message;
@@ -117,26 +119,26 @@ public class HeaderExchangeHandlerTest {
                 count.incrementAndGet();
             }
         };
-        ExchangeHandler exhandler = new MockedExchangeHandler() {
+        ExchangeHandler exHandler = new MockedExchangeHandler() {
             @Override
             public CompletableFuture<Object> reply(ExchangeChannel channel, Object request) throws RemotingException {
                 throw new BizException();
             }
         };
-        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(exhandler);
-        hexhandler.received(mchannel, request);
+        HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(exHandler);
+        headerExchangeHandler.received(mockChannel, request);
         Assertions.assertEquals(1, count.get());
     }
 
     @Test
-    public void test_received_request_twoway_error_reqeustBroken() throws RemotingException {
+    public void testReceivedRequestTwowayErrorRequestBroken() throws RemotingException {
         final Request request = new Request();
         request.setTwoWay(true);
         request.setData(new BizException());
         request.setBroken(true);
 
         final AtomicInteger count = new AtomicInteger(0);
-        final Channel mchannel = new MockedChannel() {
+        final Channel mockChannel = new MockedChannel() {
             @Override
             public void send(Object message) throws RemotingException {
                 Response res = (Response) message;
@@ -148,36 +150,36 @@ public class HeaderExchangeHandlerTest {
                 count.incrementAndGet();
             }
         };
-        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler());
-        hexhandler.received(mchannel, request);
+        HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler());
+        headerExchangeHandler.received(mockChannel, request);
         Assertions.assertEquals(1, count.get());
     }
 
     @Test
-    public void test_received_request_event_readonly() throws RemotingException {
+    public void testReceivedRequestEventReadonly() throws RemotingException {
         final Request request = new Request();
         request.setTwoWay(true);
         request.setEvent(READONLY_EVENT);
 
-        final Channel mchannel = new MockedChannel();
-        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler());
-        hexhandler.received(mchannel, request);
-        Assertions.assertTrue(mchannel.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY));
+        final Channel mockChannel = new MockedChannel();
+        HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler());
+        headerExchangeHandler.received(mockChannel, request);
+        Assertions.assertTrue(mockChannel.hasAttribute(Constants.CHANNEL_ATTRIBUTE_READONLY_KEY));
     }
 
     @Test
-    public void test_received_request_event_other_discard() throws RemotingException {
+    public void testReceivedRequestEventOtherDiscard() throws RemotingException {
         final Request request = new Request();
         request.setTwoWay(true);
         request.setEvent("my event");
 
-        final Channel mchannel = new MockedChannel() {
+        final Channel mockChannel = new MockedChannel() {
             @Override
             public void send(Object message) throws RemotingException {
                 Assertions.fail();
             }
         };
-        HeaderExchangeHandler hexhandler = new HeaderExchangeHandler(new MockedExchangeHandler() {
+        HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler() {
 
             @Override
             public CompletableFuture<Object> reply(ExchangeChannel channel, Object request) throws RemotingException {
@@ -191,7 +193,35 @@ public class HeaderExchangeHandlerTest {
                 throw new RemotingException(channel, "");
             }
         });
-        hexhandler.received(mchannel, request);
+        headerExchangeHandler.received(mockChannel, request);
+    }
+
+    @Test
+    public void testReceivedResponseHeartbeatEvent() throws Exception {
+        Channel mockChannel = new MockedChannel();
+        HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler());
+        Response response = new Response(1);
+        response.setStatus(Response.OK);
+        response.setEvent(true);
+        response.setResult(HEARTBEAT_EVENT);
+        headerExchangeHandler.received(mockChannel, response);
+    }
+
+    @Test
+    public void testReceivedResponse() throws Exception {
+        Request request = new Request(1);
+        request.setTwoWay(true);
+        Channel mockChannel = new MockedChannel();
+        DefaultFuture future = DefaultFuture.newFuture(mockChannel, request, 5000, null);
+
+        HeaderExchangeHandler headerExchangeHandler = new HeaderExchangeHandler(new MockedExchangeHandler());
+        Response response = new Response(1);
+        response.setStatus(Response.OK);
+        response.setResult("MOCK_DATA");
+        headerExchangeHandler.received(mockChannel, response);
+
+        Object result = future.get();
+        Assertions.assertEquals(result.toString(),"MOCK_DATA");
     }
 
     private class BizException extends RuntimeException {
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java
index 53bebb1..46dd70b 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/handler/WrappedChannelHandlerTest.java
@@ -18,8 +18,14 @@ package org.apache.dubbo.remoting.handler;
 
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.threadpool.ThreadlessExecutor;
+import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
 import org.apache.dubbo.remoting.Channel;
 import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.exchange.Request;
+import org.apache.dubbo.remoting.exchange.Response;
+import org.apache.dubbo.remoting.exchange.support.DefaultFuture;
 import org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler;
 
 import org.junit.jupiter.api.Assertions;
@@ -27,6 +33,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import java.lang.reflect.Field;
+import java.util.concurrent.ExecutorService;
 
 import static org.junit.jupiter.api.Assertions.fail;
 
@@ -36,7 +43,7 @@ public class WrappedChannelHandlerTest {
 
     @BeforeEach
     public void setUp() throws Exception {
-        handler = new WrappedChannelHandler(new BizChannelHander(true), url);
+        handler = new WrappedChannelHandler(new BizChannelHandler(true), url);
     }
 
     @Test
@@ -79,22 +86,22 @@ public class WrappedChannelHandlerTest {
     }
 
     @Test
-    public void test_Connect_Biz_Error() throws RemotingException {
+    public void testConnectBizError() throws RemotingException {
         Assertions.assertThrows(RemotingException.class, () -> handler.connected(new MockedChannel()));
     }
 
     @Test
-    public void test_Disconnect_Biz_Error() throws RemotingException {
+    public void testDisconnectBizError() throws RemotingException {
         Assertions.assertThrows(RemotingException.class, () -> handler.disconnected(new MockedChannel()));
     }
 
     @Test
-    public void test_MessageReceived_Biz_Error() throws RemotingException {
+    public void testMessageReceivedBizError() throws RemotingException {
         Assertions.assertThrows(RemotingException.class, () -> handler.received(new MockedChannel(), ""));
     }
 
     @Test
-    public void test_Caught_Biz_Error() throws RemotingException {
+    public void testCaughtBizError() throws RemotingException {
         try {
             handler.caught(new MockedChannel(), new BizException());
             fail();
@@ -103,15 +110,43 @@ public class WrappedChannelHandlerTest {
         }
     }
 
-    class BizChannelHander extends MockedChannelHandler {
+    @Test
+    public void testGetExecutor() {
+        ExecutorService sharedExecutorService = handler.getSharedExecutorService();
+        Assertions.assertNotNull(sharedExecutorService);
+        ExecutorService preferredExecutorService = handler.getPreferredExecutorService(new Object());
+        Assertions.assertEquals(preferredExecutorService, sharedExecutorService);
+
+        Response response = new Response(10);
+        preferredExecutorService = handler.getPreferredExecutorService(response);
+        Assertions.assertEquals(preferredExecutorService, sharedExecutorService);
+
+        Channel channel = new MockedChannel();
+        Request request = new Request(10);
+        ExecutorService sharedExecutor = ExtensionLoader.getExtensionLoader(ExecutorRepository.class)
+            .getDefaultExtension().createExecutorIfAbsent(url);
+
+        DefaultFuture future = DefaultFuture.newFuture(channel, request, 1000, null);
+        preferredExecutorService = handler.getPreferredExecutorService(response);
+        Assertions.assertEquals(preferredExecutorService, sharedExecutor);
+        future.cancel();
+
+        ThreadlessExecutor executor = new ThreadlessExecutor(sharedExecutor);
+        future = DefaultFuture.newFuture(channel, request, 1000, executor);
+        preferredExecutorService = handler.getPreferredExecutorService(response);
+        Assertions.assertEquals(preferredExecutorService, executor);
+        future.cancel();
+    }
+
+    class BizChannelHandler extends MockedChannelHandler {
         private boolean invokeWithBizError;
 
-        public BizChannelHander(boolean invokeWithBizError) {
+        public BizChannelHandler(boolean invokeWithBizError) {
             super();
             this.invokeWithBizError = invokeWithBizError;
         }
 
-        public BizChannelHander() {
+        public BizChannelHandler() {
             super();
         }
 
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/AbstractCodecTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/AbstractCodecTest.java
index ed07ebb..7448f9c 100644
--- a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/AbstractCodecTest.java
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/AbstractCodecTest.java
@@ -18,21 +18,25 @@ package org.apache.dubbo.remoting.transport;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.remoting.Channel;
-
-import org.hamcrest.CoreMatchers;
+import org.apache.dubbo.remoting.buffer.ChannelBuffer;
+import org.junit.jupiter.api.Test;
 import org.mockito.internal.verification.VerificationModeFactory;
 
 import java.io.IOException;
+import java.net.InetSocketAddress;
 
 import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-public class AbstractCodecTest  {
+public class AbstractCodecTest {
 
-    public void test_checkPayload_default8M() throws Exception {
+    @Test
+    public void testCheckPayloadDefault8M() throws Exception {
         Channel channel = mock(Channel.class);
         given(channel.getUrl()).willReturn(URL.valueOf("dubbo://1.1.1.1"));
 
@@ -42,15 +46,16 @@ public class AbstractCodecTest  {
             AbstractCodec.checkPayload(channel, 15 * 1024 * 1024);
         } catch (IOException expected) {
             assertThat(expected.getMessage(), allOf(
-                    CoreMatchers.containsString("Data length too large: "),
-                    CoreMatchers.containsString("max payload: " + 8 * 1024 * 1024)
+                containsString("Data length too large: "),
+                containsString("max payload: " + 8 * 1024 * 1024)
             ));
         }
 
         verify(channel, VerificationModeFactory.atLeastOnce()).getUrl();
     }
 
-    public void test_checkPayload_minusPayloadNoLimit() throws Exception {
+    @Test
+    public void tesCheckPayloadMinusPayloadNoLimit() throws Exception {
         Channel channel = mock(Channel.class);
         given(channel.getUrl()).willReturn(URL.valueOf("dubbo://1.1.1.1?payload=-1"));
 
@@ -58,4 +63,36 @@ public class AbstractCodecTest  {
 
         verify(channel, VerificationModeFactory.atLeastOnce()).getUrl();
     }
+
+    @Test
+    public void testIsClientSide() {
+        AbstractCodec codec = getAbstractCodec();
+
+        Channel channel = mock(Channel.class);
+        given(channel.getRemoteAddress()).willReturn(new InetSocketAddress("172.24.157.13", 9103));
+        given(channel.getUrl()).willReturn(URL.valueOf("dubbo://172.24.157.13:9103"));
+        assertThat(codec.isClientSide(channel), is(true));
+        assertThat(codec.isServerSide(channel), is(false));
+
+        given(channel.getRemoteAddress()).willReturn(new InetSocketAddress("172.24.157.14", 9103));
+        given(channel.getUrl()).willReturn(URL.valueOf("dubbo://172.24.157.13:9103"));
+        assertThat(codec.isClientSide(channel), is(false));
+        assertThat(codec.isServerSide(channel), is(true));
+
+    }
+
+    private AbstractCodec getAbstractCodec() {
+        AbstractCodec codec = new AbstractCodec() {
+            @Override
+            public void encode(Channel channel, ChannelBuffer buffer, Object message) throws IOException {
+
+            }
+
+            @Override
+            public Object decode(Channel channel, ChannelBuffer buffer) throws IOException {
+                return null;
+            }
+        };
+        return codec;
+    }
 }
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/ChannelHandlerDispatcherTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/ChannelHandlerDispatcherTest.java
new file mode 100644
index 0000000..6fffc2e
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/ChannelHandlerDispatcherTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.RemotingException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Collection;
+
+public class ChannelHandlerDispatcherTest {
+
+    @Test
+    public void test() {
+        ChannelHandlerDispatcher channelHandlerDispatcher = new ChannelHandlerDispatcher();
+        MockChannelHandler channelHandler1 = new MockChannelHandler();
+        MockChannelHandler channelHandler2 = new MockChannelHandler();
+        channelHandlerDispatcher.addChannelHandler(channelHandler1);
+        channelHandlerDispatcher.addChannelHandler(channelHandler2);
+        Collection<ChannelHandler> channelHandlers = channelHandlerDispatcher.getChannelHandlers();
+        Assertions.assertTrue(channelHandlers.contains(channelHandler1));
+        Assertions.assertTrue(channelHandlers.contains(channelHandler2));
+
+        Channel channel = Mockito.mock(Channel.class);
+        channelHandlerDispatcher.sent(channel, "test");
+        channelHandlerDispatcher.connected(channel);
+        channelHandlerDispatcher.disconnected(channel);
+        channelHandlerDispatcher.caught(channel, null);
+        channelHandlerDispatcher.received(channel, "test");
+
+        Assertions.assertEquals(MockChannelHandler.getSentCount(), 2);
+        Assertions.assertEquals(MockChannelHandler.getConnectedCount(), 2);
+        Assertions.assertEquals(MockChannelHandler.getDisconnectedCount(), 2);
+        Assertions.assertEquals(MockChannelHandler.getCaughtCount(), 2);
+        Assertions.assertEquals(MockChannelHandler.getReceivedCount(), 2);
+
+        channelHandlerDispatcher = channelHandlerDispatcher.removeChannelHandler(channelHandler1);
+        Assertions.assertFalse(channelHandlerDispatcher.getChannelHandlers().contains(channelHandler1));
+
+    }
+
+}
+
+class MockChannelHandler extends ChannelHandlerAdapter {
+    private static int sentCount = 0;
+    private static int connectedCount = 0;
+    private static int disconnectedCount = 0;
+    private static int receivedCount = 0;
+    private static int caughtCount = 0;
+
+    @Override
+    public void connected(Channel channel) throws RemotingException {
+        connectedCount++;
+        super.connected(channel);
+    }
+
+    @Override
+    public void disconnected(Channel channel) throws RemotingException {
+        disconnectedCount++;
+        super.disconnected(channel);
+    }
+
+    @Override
+    public void sent(Channel channel, Object message) throws RemotingException {
+        sentCount++;
+        super.sent(channel, message);
+    }
+
+    @Override
+    public void received(Channel channel, Object message) throws RemotingException {
+        receivedCount++;
+        super.received(channel, message);
+    }
+
+    @Override
+    public void caught(Channel channel, Throwable exception) throws RemotingException {
+        caughtCount++;
+        super.caught(channel, exception);
+    }
+
+    public static int getSentCount() {
+        return sentCount;
+    }
+
+    public static int getConnectedCount() {
+        return connectedCount;
+    }
+
+    public static int getDisconnectedCount() {
+        return disconnectedCount;
+    }
+
+    public static int getReceivedCount() {
+        return receivedCount;
+    }
+
+    public static int getCaughtCount() {
+        return caughtCount;
+    }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/DecodeHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/DecodeHandlerTest.java
new file mode 100644
index 0000000..6b4a9a6
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/DecodeHandlerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.Decodeable;
+import org.apache.dubbo.remoting.exchange.Request;
+import org.apache.dubbo.remoting.exchange.Response;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+/**
+ * {@link DecodeHandler}
+ */
+public class DecodeHandlerTest {
+
+    @Test
+    public void test() throws Exception {
+        ChannelHandler handler = Mockito.mock(ChannelHandler.class);
+        Channel channel = Mockito.mock(Channel.class);
+        DecodeHandler decodeHandler = new DecodeHandler(handler);
+
+        MockData mockData = new MockData();
+        decodeHandler.received(channel, mockData);
+        Assertions.assertTrue(mockData.isDecoded());
+
+        MockData mockRequestData = new MockData();
+        Request request = new Request(1);
+        request.setData(mockRequestData);
+        decodeHandler.received(channel, request);
+        Assertions.assertTrue(mockRequestData.isDecoded());
+
+        MockData mockResponseData = new MockData();
+        Response response = new Response(1);
+        response.setResult(mockResponseData);
+        decodeHandler.received(channel, response);
+        Assertions.assertTrue(mockResponseData.isDecoded());
+
+        mockData.setThrowEx(true);
+        decodeHandler.received(channel, mockData);
+    }
+
+    class MockData implements Decodeable {
+
+        private boolean isDecoded = false;
+
+        private boolean throwEx = false;
+
+        @Override
+        public void decode() throws Exception {
+            if (throwEx) {
+                throw new RuntimeException();
+            }
+            isDecoded = true;
+        }
+
+        public boolean isDecoded() {
+            return isDecoded;
+        }
+
+        public void setThrowEx(boolean throwEx) {
+            this.throwEx = throwEx;
+        }
+    }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/MultiMessageHandlerTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/MultiMessageHandlerTest.java
new file mode 100644
index 0000000..e65fd47
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/MultiMessageHandlerTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.exchange.support.MultiMessage;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+/**
+ * {@link MultiMessageHandler}
+ */
+public class MultiMessageHandlerTest {
+
+    @Test
+    public void test() throws Exception {
+        ChannelHandler handler = Mockito.mock(ChannelHandler.class);
+        Channel channel = Mockito.mock(Channel.class);
+        MultiMessageHandler multiMessageHandler = new MultiMessageHandler(handler);
+
+        MultiMessage multiMessage = MultiMessage.createFromArray("test1", "test2");
+        multiMessageHandler.received(channel, multiMessage);
+        // verify
+        ArgumentCaptor<Channel> channelArgumentCaptor = ArgumentCaptor.forClass(Channel.class);
+        ArgumentCaptor<Object> objectArgumentCaptor = ArgumentCaptor.forClass(Object.class);
+        Mockito.verify(handler, Mockito.times(2)).received(channelArgumentCaptor.capture(), objectArgumentCaptor.capture());
+        Assertions.assertEquals(objectArgumentCaptor.getAllValues().get(0), "test1");
+        Assertions.assertEquals(objectArgumentCaptor.getAllValues().get(1), "test2");
+        Assertions.assertEquals(channelArgumentCaptor.getValue(), channel);
+
+        Object obj = new Object();
+        multiMessageHandler.received(channel, obj);
+        // verify
+        Mockito.verify(handler, Mockito.times(3)).received(channelArgumentCaptor.capture(), objectArgumentCaptor.capture());
+        Assertions.assertEquals(objectArgumentCaptor.getValue(), obj);
+        Assertions.assertEquals(channelArgumentCaptor.getValue(), channel);
+
+        RuntimeException runtimeException = new RuntimeException();
+        Mockito.doThrow(runtimeException).when(handler).received(Mockito.any(), Mockito.any());
+        multiMessageHandler.received(channel, multiMessage);
+        // verify
+        ArgumentCaptor<Throwable> throwableArgumentCaptor = ArgumentCaptor.forClass(Throwable.class);
+        Mockito.verify(handler, Mockito.times(2)).caught(channelArgumentCaptor.capture(), throwableArgumentCaptor.capture());
+        Assertions.assertEquals(throwableArgumentCaptor.getAllValues().get(0), runtimeException);
+        Assertions.assertEquals(throwableArgumentCaptor.getAllValues().get(1), runtimeException);
+        Assertions.assertEquals(channelArgumentCaptor.getValue(), channel);
+
+    }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelEventRunnableTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelEventRunnableTest.java
new file mode 100644
index 0000000..0ecae79
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelEventRunnableTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport.dispatcher;
+
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+
+/**
+ * {@link ChannelEventRunnable}
+ */
+public class ChannelEventRunnableTest {
+
+    @Test
+    public void test() throws Exception {
+        ChannelEventRunnable.ChannelState[] values = ChannelEventRunnable.ChannelState.values();
+        Assertions.assertEquals(Arrays.toString(values), "[CONNECTED, DISCONNECTED, SENT, RECEIVED, CAUGHT]");
+
+        Channel channel = Mockito.mock(Channel.class);
+        ChannelHandler handler = Mockito.mock(ChannelHandler.class);
+        ChannelEventRunnable connectRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.CONNECTED);
+        ChannelEventRunnable disconnectRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.DISCONNECTED);
+        ChannelEventRunnable sentRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.SENT);
+        ChannelEventRunnable receivedRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.RECEIVED, "");
+        ChannelEventRunnable caughtRunnable = new ChannelEventRunnable(channel, handler, ChannelEventRunnable.ChannelState.CAUGHT, new RuntimeException());
+
+        connectRunnable.run();
+        disconnectRunnable.run();
+        sentRunnable.run();
+        receivedRunnable.run();
+        caughtRunnable.run();
+
+        ArgumentCaptor<Channel> channelArgumentCaptor = ArgumentCaptor.forClass(Channel.class);
+        ArgumentCaptor<Throwable> throwableArgumentCaptor = ArgumentCaptor.forClass(Throwable.class);
+        ArgumentCaptor<Object> objectArgumentCaptor = ArgumentCaptor.forClass(Object.class);
+        Mockito.verify(handler, Mockito.times(1)).connected(channelArgumentCaptor.capture());
+        Mockito.verify(handler, Mockito.times(1)).disconnected(channelArgumentCaptor.capture());
+        Mockito.verify(handler, Mockito.times(1)).sent(channelArgumentCaptor.capture(), Mockito.any());
+        Mockito.verify(handler, Mockito.times(1)).received(channelArgumentCaptor.capture(), objectArgumentCaptor.capture());
+        Mockito.verify(handler, Mockito.times(1)).caught(channelArgumentCaptor.capture(), throwableArgumentCaptor.capture());
+    }
+}
diff --git a/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlersTest.java b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlersTest.java
new file mode 100644
index 0000000..632f1dd
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-api/src/test/java/org/apache/dubbo/remoting/transport/dispatcher/ChannelHandlersTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport.dispatcher;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.exchange.support.header.HeartbeatHandler;
+import org.apache.dubbo.remoting.transport.AbstractChannelHandlerDelegate;
+import org.apache.dubbo.remoting.transport.MultiMessageHandler;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Field;
+
+public class ChannelHandlersTest {
+    @Test
+    public void test() {
+        ChannelHandlers instance1 = ChannelHandlers.getInstance();
+        ChannelHandlers instance2 = ChannelHandlers.getInstance();
+        Assertions.assertEquals(instance1, instance2);
+
+        ChannelHandler channelHandler = Mockito.mock(ChannelHandler.class);
+        URL url = new ServiceConfigURL("dubbo", "127.0.0.1", 9999);
+        ChannelHandler wrappedHandler = ChannelHandlers.wrap(channelHandler, url);
+        Assertions.assertTrue(wrappedHandler instanceof MultiMessageHandler);
+
+        MultiMessageHandler multiMessageHandler = (MultiMessageHandler) wrappedHandler;
+        ChannelHandler handler = multiMessageHandler.getHandler();
+        Assertions.assertEquals(channelHandler, handler);
+    }
+}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java
index 27b70d4..780ae9d 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyChannel.java
@@ -218,7 +218,7 @@ final class NettyChannel extends AbstractChannel {
 
     @Override
     public void setAttribute(String key, Object value) {
-        // The null value is unallowed in the ConcurrentHashMap.
+        // The null value is not allowed in the ConcurrentHashMap.
         if (value == null) {
             attributes.remove(key);
         } else {
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapter.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapter.java
index d1cb65b..beed485 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapter.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapter.java
@@ -63,7 +63,7 @@ final public class NettyCodecAdapter {
 
         @Override
         protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
-            org.apache.dubbo.remoting.buffer.ChannelBuffer buffer = new NettyBackedChannelBuffer(out);
+            ChannelBuffer buffer = new NettyBackedChannelBuffer(out);
             Channel ch = ctx.channel();
             NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
             codec.encode(channel, buffer, msg);
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java
index 3d66046..47acff7 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java
@@ -29,13 +29,13 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.config.ConfigurationUtils;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.ExecutorUtil;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.remoting.Channel;
 import org.apache.dubbo.remoting.ChannelHandler;
 import org.apache.dubbo.remoting.Constants;
 import org.apache.dubbo.remoting.RemotingException;
-import org.apache.dubbo.remoting.RemotingServer;
 import org.apache.dubbo.remoting.api.NettyEventLoopFactory;
 import org.apache.dubbo.remoting.api.SslServerTlsHandler;
 import org.apache.dubbo.remoting.transport.AbstractServer;
@@ -58,7 +58,7 @@ import static org.apache.dubbo.remoting.Constants.EVENT_LOOP_WORKER_POOL_NAME;
 /**
  * NettyServer.
  */
-public class NettyServer extends AbstractServer implements RemotingServer {
+public class NettyServer extends AbstractServer {
 
     private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
     /**
@@ -147,9 +147,9 @@ public class NettyServer extends AbstractServer implements RemotingServer {
             logger.warn(e.getMessage(), e);
         }
         try {
-            Collection<org.apache.dubbo.remoting.Channel> channels = getChannels();
-            if (channels != null && channels.size() > 0) {
-                for (org.apache.dubbo.remoting.Channel channel : channels) {
+            Collection<Channel> channels = getChannels();
+            if (CollectionUtils.isNotEmpty(channels)) {
+                for (Channel channel : channels) {
                     try {
                         channel.close();
                     } catch (Throwable e) {
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java
index 9da53e8..2be1c03 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientToServerTest.java
@@ -34,8 +34,6 @@ import java.util.concurrent.CompletableFuture;
  */
 public abstract class ClientToServerTest {
 
-    protected static final String LOCALHOST = "127.0.0.1";
-
     protected ExchangeServer server;
 
     protected ExchangeChannel client;
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyChannelTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyChannelTest.java
new file mode 100644
index 0000000..3b8b1da
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyChannelTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport.netty4;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.RemotingException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.net.InetSocketAddress;
+
+public class NettyChannelTest {
+    private Channel channel = Mockito.mock(Channel.class);
+    private URL url = new ServiceConfigURL("dubbo", "127.0.0.1", 8080);
+    private ChannelHandler channelHandler = Mockito.mock(ChannelHandler.class);
+
+    @Test
+    public void test() throws Exception {
+        Channel channel = Mockito.mock(Channel.class);
+        Mockito.when(channel.isActive()).thenReturn(true);
+        URL url = URL.valueOf("test://127.0.0.1/test");
+        ChannelHandler channelHandler = Mockito.mock(ChannelHandler.class);
+        NettyChannel nettyChannel = NettyChannel.getOrAddChannel(channel, url, channelHandler);
+
+        Assertions.assertEquals(nettyChannel.getChannelHandler(), channelHandler);
+        Assertions.assertTrue(nettyChannel.isActive());
+
+        NettyChannel.removeChannel(channel);
+        Assertions.assertFalse(nettyChannel.isActive());
+
+        nettyChannel = NettyChannel.getOrAddChannel(channel, url, channelHandler);
+        Mockito.when(channel.isActive()).thenReturn(false);
+        NettyChannel.removeChannelIfDisconnected(channel);
+        Assertions.assertFalse(nettyChannel.isActive());
+
+        nettyChannel = NettyChannel.getOrAddChannel(channel, url, channelHandler);
+        Assertions.assertFalse(nettyChannel.isConnected());
+
+        nettyChannel = NettyChannel.getOrAddChannel(channel, url, channelHandler);
+        nettyChannel.markActive(true);
+        Assertions.assertTrue(nettyChannel.isActive());
+
+    }
+
+    @Test
+    public void testAddress() {
+        NettyChannel nettyChannel = NettyChannel.getOrAddChannel(channel, url, channelHandler);
+        InetSocketAddress localAddress = InetSocketAddress.createUnresolved("127.0.0.1", 8888);
+        InetSocketAddress remoteAddress = InetSocketAddress.createUnresolved("127.0.0.1", 9999);
+        Mockito.when(channel.localAddress()).thenReturn(localAddress);
+        Mockito.when(channel.remoteAddress()).thenReturn(remoteAddress);
+        Assertions.assertEquals(nettyChannel.getLocalAddress(), localAddress);
+        Assertions.assertEquals(nettyChannel.getRemoteAddress(), remoteAddress);
+    }
+
+    @Test
+    public void testSend() throws Exception {
+        NettyChannel nettyChannel = NettyChannel.getOrAddChannel(channel, url, channelHandler);
+        ChannelFuture future = Mockito.mock(ChannelFuture.class);
+        Mockito.when(future.await(1000)).thenReturn(true);
+        Mockito.when(future.cause()).thenReturn(null);
+        Mockito.when(channel.writeAndFlush(Mockito.any())).thenReturn(future);
+        nettyChannel.send("msg", true);
+
+        NettyChannel finalNettyChannel = nettyChannel;
+        Exception exception = Mockito.mock(Exception.class);
+        Mockito.when(exception.getMessage()).thenReturn("future cause");
+        Mockito.when(future.cause()).thenReturn(exception);
+        Assertions.assertThrows(RemotingException.class, () -> {
+            finalNettyChannel.send("msg", true);
+        }, "future cause");
+
+        Mockito.when(future.await(1000)).thenReturn(false);
+        Mockito.when(future.cause()).thenReturn(null);
+        Assertions.assertThrows(RemotingException.class, () -> {
+            finalNettyChannel.send("msg", true);
+        }, "in timeout(1000ms) limit");
+    }
+
+    @Test
+    public void testAttribute() {
+        NettyChannel nettyChannel = NettyChannel.getOrAddChannel(channel, url, channelHandler);
+        nettyChannel.setAttribute("k1", "v1");
+        Assertions.assertTrue(nettyChannel.hasAttribute("k1"));
+        Assertions.assertEquals(nettyChannel.getAttribute("k1"), "v1");
+        nettyChannel.removeAttribute("k1");
+        Assertions.assertFalse(nettyChannel.hasAttribute("k1"));
+    }
+
+    @Test
+    public void testEquals() {
+        Channel channel2 = Mockito.mock(Channel.class);
+        NettyChannel nettyChannel = NettyChannel.getOrAddChannel(channel, url, channelHandler);
+        NettyChannel nettyChannel2 = NettyChannel.getOrAddChannel(channel2, url, channelHandler);
+        Assertions.assertEquals(nettyChannel, nettyChannel);
+        Assertions.assertNotEquals(nettyChannel, nettyChannel2);
+    }
+}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandlerTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandlerTest.java
new file mode 100644
index 0000000..88d8e72
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandlerTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport.netty4;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.util.concurrent.GenericFutureListener;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.exchange.Request;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class NettyClientHandlerTest {
+
+    @Test
+    public void test() throws Exception {
+        URL url = new ServiceConfigURL("dubbbo", "127.0.0.1", 20901);
+        ChannelHandler handler = Mockito.mock(ChannelHandler.class);
+        ChannelHandlerContext ctx = Mockito.mock(ChannelHandlerContext.class);
+        Channel channel = Mockito.mock(Channel.class);
+        Mockito.when(ctx.channel()).thenReturn(channel);
+        Mockito.when(channel.isActive()).thenReturn(true);
+
+        ChannelFuture future = mock(ChannelFuture.class);
+        when(channel.writeAndFlush(any())).thenReturn(future);
+        when(future.cause()).thenReturn(null);
+
+        NettyClientHandler nettyClientHandler = new NettyClientHandler(url, handler);
+
+        nettyClientHandler.channelActive(ctx);
+        ArgumentCaptor<NettyChannel> captor = ArgumentCaptor.forClass(NettyChannel.class);
+        Mockito.verify(handler, Mockito.times(1)).connected(captor.capture());
+
+        nettyClientHandler.channelInactive(ctx);
+        captor = ArgumentCaptor.forClass(NettyChannel.class);
+        Mockito.verify(handler, Mockito.times(1)).disconnected(captor.capture());
+
+        Throwable throwable = Mockito.mock(Throwable.class);
+        nettyClientHandler.exceptionCaught(ctx, throwable);
+        captor = ArgumentCaptor.forClass(NettyChannel.class);
+        ArgumentCaptor<Throwable> throwableArgumentCaptor = ArgumentCaptor.forClass(Throwable.class);
+        Mockito.verify(handler, Mockito.times(1)).caught(captor.capture(), throwableArgumentCaptor.capture());
+
+        nettyClientHandler.channelRead(ctx, "test");
+        captor = ArgumentCaptor.forClass(NettyChannel.class);
+        ArgumentCaptor<Object> objectArgumentCaptor = ArgumentCaptor.forClass(Object.class);
+        Mockito.verify(handler, Mockito.times(1)).received(captor.capture(), objectArgumentCaptor.capture());
+
+        nettyClientHandler.userEventTriggered(ctx, IdleStateEvent.READER_IDLE_STATE_EVENT);
+        ArgumentCaptor<Request> requestArgumentCaptor = ArgumentCaptor.forClass(Request.class);
+        Mockito.verify(channel, Mockito.times(1)).writeAndFlush(requestArgumentCaptor.capture());
+
+
+        Request request = new Request();
+        ChannelPromise promise = Mockito.mock(ChannelPromise.class);
+        nettyClientHandler.write(ctx,request,promise);
+        ArgumentCaptor<GenericFutureListener> listenerArgumentCaptor = ArgumentCaptor.forClass(GenericFutureListener.class);
+        Mockito.verify(promise, Mockito.times(1)).addListener(listenerArgumentCaptor.capture());
+
+    }
+}
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapterTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapterTest.java
new file mode 100644
index 0000000..d65f776
--- /dev/null
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyCodecAdapterTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.transport.netty4;
+
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.codec.MessageToByteEncoder;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.Codec2;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+/**
+ * {@link NettyCodecAdapter}
+ */
+public class NettyCodecAdapterTest {
+
+    @Test
+    public void test() {
+        Codec2 codec2 = Mockito.mock(Codec2.class);
+        URL url = Mockito.mock(URL.class);
+        ChannelHandler handler = Mockito.mock(ChannelHandler.class);
+        NettyCodecAdapter nettyCodecAdapter = new NettyCodecAdapter(codec2, url, handler);
+        io.netty.channel.ChannelHandler decoder = nettyCodecAdapter.getDecoder();
+        io.netty.channel.ChannelHandler encoder = nettyCodecAdapter.getEncoder();
+        Assertions.assertTrue(decoder instanceof ByteToMessageDecoder);
+        Assertions.assertTrue(encoder instanceof MessageToByteEncoder);
+    }
+}