diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 9dc6cb4..c7caec3 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -4,7 +4,7 @@
 name: Go
 
 on:
-  [push]
+  [push, pull_request, workflow_dispatch]
 
 jobs:
 
diff --git a/tests/config.go b/tests/config.go
index c15fd9a..38d7ddd 100644
--- a/tests/config.go
+++ b/tests/config.go
@@ -14,18 +14,4 @@ const (
 	httpServiceUrl = "http://" + httpServerAddr + "/httpEcho"
 
 	tunaNodeStarted = "tuna node is started"
-
-	webServerIsReady = "web server is ready"
-	webServerExited  = "web server exited"
-	webClientExited  = "web client exited"
-
-	tcpServerIsReady = "tcp server is ready"
-	tcpServerExited  = "tcp server exited"
-	tcpClientExited  = "tcp client exited"
-
-	udpServerIsReady = "udp server is ready"
-	udpServerExited  = "udp server exited"
-	udpClientExited  = "udp client exited"
-
-	exited = "exited"
 )
diff --git a/tests/dns.go b/tests/dns.go
new file mode 100644
index 0000000..76155e7
--- /dev/null
+++ b/tests/dns.go
@@ -0,0 +1,19 @@
+package tests
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/txthinking/brook"
+)
+
+func dnsQuery() {
+	for i := 1; i <= rounds; i++ {
+		err := brook.Socks5Test(proxyAddr, "", "", "http3.ooo", "137.184.237.95", "8.8.8.8:53")
+		if err != nil {
+			fmt.Printf("TestDNSProxy try %v err: %v\n", i, err)
+			time.Sleep(time.Duration(i) * time.Second)
+			break
+		}
+	}
+}
diff --git a/tests/dns_proxy_test.go b/tests/dns_proxy_test.go
deleted file mode 100644
index fc9a73d..0000000
--- a/tests/dns_proxy_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package tests
-
-import (
-	"fmt"
-	"testing"
-	"time"
-
-	"github.com/txthinking/brook"
-)
-
-// go test -v -run=TestDNSByProxy
-func TestDNSByProxy(t *testing.T) {
-	go StartNconnectServerWithTunaNode(true, true, false)
-	time.Sleep(15 * time.Second)
-
-	go DnsByProxy()
-	waitFor(ch, exited)
-}
-
-func DnsByProxy() {
-	tuna, udp, tun := true, true, false
-	go func() {
-		err := startNconnect("client.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect client err: %v\n", err)
-			return
-		}
-	}()
-	time.Sleep(20 * time.Second)
-
-	for i := 1; i <= rounds; i++ {
-		err := brook.Socks5Test(proxyAddr, "", "", "http3.ooo", "137.184.237.95", "8.8.8.8:53")
-		if err != nil {
-			fmt.Printf("TestDNSProxy try %v err: %v\n", i, err)
-			time.Sleep(time.Duration(i) * time.Second)
-			break
-		}
-	}
-
-	ch <- udpClientExited
-}
diff --git a/tests/dns_tun_test.go b/tests/dns_tun_test.go
deleted file mode 100644
index ac1067c..0000000
--- a/tests/dns_tun_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package tests
-
-import (
-	"fmt"
-	"testing"
-	"time"
-
-	"github.com/txthinking/brook"
-)
-
-// go test -v -run=TestDNSByTun
-func TestDNSByTun(t *testing.T) {
-	go StartNconnectServerWithTunaNode(true, true, false)
-	time.Sleep(15 * time.Second)
-
-	go DnsByTun()
-	waitFor(ch, exited)
-}
-
-func DnsByTun() {
-	tuna, udp, tun := true, true, true
-	go func() {
-		err := startNconnect("client.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect client err: %v\n", err)
-			return
-		}
-	}()
-	time.Sleep(15 * time.Second)
-
-	for i := 1; i <= 10; i++ {
-		err := brook.Socks5Test(proxyAddr, "", "", "http3.ooo", "137.184.237.95", "8.8.8.8:53")
-		if err != nil {
-			fmt.Printf("TestDNSProxy try %v err: %v\n", i, err)
-			time.Sleep(time.Duration(i) * time.Second)
-			break
-		}
-	}
-
-	ch <- udpClientExited
-}
diff --git a/tests/http_tun_test.go b/tests/http_tun_test.go
deleted file mode 100644
index ab4ae5c..0000000
--- a/tests/http_tun_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package tests
-
-import (
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"io"
-	"net/http"
-	"testing"
-	"time"
-)
-
-// go test -v -run=TestHttpByTun
-func TestHttpByTun(t *testing.T) {
-	tunaNode, err := getTunaNode()
-	if err != nil {
-		fmt.Printf("startTunaNode err %v\n", err)
-		return
-	}
-
-	tuna, udp, tun := true, true, false
-
-	go func() {
-		err := startNconnect("server.json", tuna, udp, tun, tunaNode)
-		if err != nil {
-			fmt.Printf("start nconnect server err: %v\n", err)
-			return
-		}
-	}()
-
-	time.Sleep(15 * time.Second)
-	tun = true
-	go func() {
-		err := startNconnect("client.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect client err: %v\n", err)
-			return
-		}
-	}()
-	time.Sleep(15 * time.Second)
-
-	go StartTunWebClient()
-
-	waitFor(ch, exited)
-}
-
-func StartTunWebClient() error {
-	httpClient := &http.Client{
-		Timeout: 10 * time.Second,
-	}
-
-	user := &Person{Name: "http_tun_boy", Age: 0}
-	b := new(bytes.Buffer)
-
-	for i := 0; i < 10; i++ {
-		user.Age++
-		err := json.NewEncoder(b).Encode(user)
-		if err != nil {
-			fmt.Printf("StartWebClient.Encode err: %v\n", err)
-			break
-		}
-		req, err := http.NewRequest(http.MethodPost, httpServiceUrl, b)
-		req.Header.Set("Content-type", "application/json")
-
-		if err != nil {
-			fmt.Printf("StartWebClient.http.NewRequest err: %v\n", err)
-			break
-		}
-
-		resp, err := httpClient.Do(req)
-		if err != nil {
-			fmt.Printf("StartWebClient.http.Do err: %v\n", err)
-			break
-		}
-		defer resp.Body.Close()
-
-		body, err := io.ReadAll(resp.Body)
-		if err != nil {
-			fmt.Printf("StartWebClient.io.ReadAll err: %v\n", err)
-			break
-		}
-
-		respUser := &Person{}
-		err = json.Unmarshal(body, respUser)
-		if err != nil {
-			fmt.Printf("StartWebClient.json.Unmarshal err: %v\n", err)
-			break
-		}
-
-		fmt.Printf("respUser %+v\n", respUser)
-		if respUser.Age != user.Age {
-			fmt.Printf("StartWebClient got wrong response, sent %+v, recv %+v\n", user, respUser)
-			break
-		}
-	}
-
-	time.Sleep(time.Second)
-	ch <- webClientExited
-
-	return nil
-}
diff --git a/tests/main_test.go b/tests/main_test.go
index c5ac0b3..4f20c94 100644
--- a/tests/main_test.go
+++ b/tests/main_test.go
@@ -3,6 +3,7 @@ package tests
 import (
 	"os"
 	"testing"
+	"time"
 )
 
 func TestMain(m *testing.M) {
@@ -10,6 +11,9 @@ func TestMain(m *testing.M) {
 	go StartWebServer()
 	go StartUdpServer()
 
+	go StartNconnectServerWithTunaNode(true, true, false)
+	time.Sleep(15 * time.Second)
+
 	exitVal := m.Run()
 	os.Exit(exitVal)
 }
diff --git a/tests/proxy_test.go b/tests/proxy_test.go
new file mode 100644
index 0000000..a7772da
--- /dev/null
+++ b/tests/proxy_test.go
@@ -0,0 +1,25 @@
+package tests
+
+import (
+	"fmt"
+	"testing"
+	"time"
+)
+
+// go test -v -run=TestProxy
+func TestProxy(t *testing.T) {
+	tuna, udp, tun := true, true, false
+	go func() {
+		err := startNconnect("client.json", tuna, udp, tun, nil)
+		if err != nil {
+			fmt.Printf("start nconnect client err: %v\n", err)
+			return
+		}
+	}()
+	time.Sleep(15 * time.Second)
+
+	dnsQuery()
+	StartWebClient()
+	StartTCPClient()
+	StartUDPClient()
+}
diff --git a/tests/pub.go b/tests/pub.go
index 12eb44b..9e0bcab 100644
--- a/tests/pub.go
+++ b/tests/pub.go
@@ -52,9 +52,9 @@ func startNconnect(configFile string, tuna, udp, tun bool, n *types.Node) error
 	}
 
 	if opts.Client {
-		port++
 		proxyAddr = fmt.Sprintf("127.0.0.1:%v", port)
 		opts.LocalSocksAddr = proxyAddr
+		port++
 	}
 	nc, _ := nconnect.NewNconnect(opts)
 	if opts.Server {
diff --git a/tests/tcp_proxy_test.go b/tests/tcp.go
similarity index 67%
rename from tests/tcp_proxy_test.go
rename to tests/tcp.go
index a17238b..f6dde8b 100644
--- a/tests/tcp_proxy_test.go
+++ b/tests/tcp.go
@@ -4,47 +4,17 @@ import (
 	"encoding/json"
 	"fmt"
 	"net"
-	"strings"
-	"testing"
 	"time"
 
 	"golang.org/x/net/proxy"
 )
 
-// go test -v -run=TestTCPByProxy
-func TestTCPByProxy(t *testing.T) {
-
-	tuna, udp, tun := true, true, false
-	go func() {
-		err := startNconnect("server.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect server err: %v\n", err)
-			return
-		}
-	}()
-
-	time.Sleep(15 * time.Second)
-	go func() {
-		err := startNconnect("client.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect client err: %v\n", err)
-			return
-		}
-	}()
-	time.Sleep(15 * time.Second)
-
-	go StartTCPClient()
-
-	waitFor(ch, exited)
-}
-
 func StartTcpServer() error {
 	tcpServer, err := net.Listen("tcp", tcpServerAddr)
 	if err != nil {
 		return err
 	}
 	fmt.Println("TCP Server is listening at ", tcpServerAddr)
-	ch <- tcpServerIsReady
 
 	for {
 		conn, err := tcpServer.Accept()
@@ -56,18 +26,13 @@ func StartTcpServer() error {
 			n, err := conn.Read(b)
 			if err != nil {
 				fmt.Printf("StartTcpServer, Read err %v\n", err)
-				return err
+				break
 			}
-			fmt.Printf("StartTcpServer, got: %v\n", string(b[:n]))
+
 			_, err = conn.Write(b[:n])
 			if err != nil {
 				fmt.Printf("StartTcpServer, write err %v\n", err)
-				return err
-			}
-
-			if strings.Contains(string(b[:n]), tcpClientExited) {
-				ch <- tcpServerExited
-				return nil
+				break
 			}
 		}
 	}
@@ -113,15 +78,50 @@ func StartTCPClient() error {
 			break
 		}
 
-		fmt.Printf("respUser %+v\n", respUser)
 		if respUser.Age != user.Age {
 			fmt.Printf("StartTCPClient, got wrong response, sent %+v, recv %+v\n", user, respUser)
 			break
 		}
 	}
-	conn.Write([]byte(tcpClientExited))
-	time.Sleep(time.Second)
-	ch <- tcpClientExited
+
+	return nil
+}
+
+func StartTCPTunClient() error {
+	conn, err := net.Dial("tcp", tcpServerAddr)
+	if err != nil {
+		fmt.Printf("StartTCPClient, dailer.Dial err: %v\n", err)
+		return err
+	}
+
+	user := &Person{Name: "tcp_boy", Age: 0}
+	for i := 0; i < rounds; i++ {
+		user.Age++
+		b1, _ := json.Marshal(user)
+		_, err = conn.Write(b1)
+		if err != nil {
+			fmt.Printf("StartTCPClient, conn.Write err: %v\n", err)
+			break
+		}
+
+		b2 := make([]byte, 1024)
+		n, err := conn.Read(b2)
+		if err != nil {
+			fmt.Printf("StartTCPClient, conn.Read err: %v\n", err)
+			break
+		}
+		respUser := &Person{}
+		err = json.Unmarshal(b2[:n], respUser)
+		if err != nil {
+			fmt.Printf("StartTCPClient, json.Unmarshal err: %v\n", err)
+			break
+		}
+
+		if respUser.Age != user.Age {
+			fmt.Printf("StartTCPClient, got wrong response, sent %+v, recv %+v\n", user, respUser)
+			break
+		}
+	}
 
 	return nil
 }
diff --git a/tests/tcp_tun_test.go b/tests/tcp_tun_test.go
deleted file mode 100644
index b181149..0000000
--- a/tests/tcp_tun_test.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package tests
-
-import (
-	"encoding/json"
-	"fmt"
-	"net"
-	"testing"
-	"time"
-)
-
-// go test -v -run=TestTCPByTun
-func TestTCPByTun(t *testing.T) {
-
-	tuna, udp, tun := true, true, false
-	go func() {
-		err := startNconnect("server.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect server err: %v\n", err)
-			return
-		}
-	}()
-
-	time.Sleep(15 * time.Second)
-
-	tun = true
-	go func() {
-		err := startNconnect("client.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect client err: %v\n", err)
-			return
-		}
-	}()
-	time.Sleep(10 * time.Second)
-
-	go StartTCPClient()
-
-	waitFor(ch, exited)
-}
-
-func StartTCPTunClient() error {
-	conn, err := net.Dial("tcp", tcpServerAddr)
-	if err != nil {
-		fmt.Printf("StartTCPClient, dailer.Dial err: %v\n", err)
-		return err
-	}
-
-	user := &Person{Name: "tcp_boy", Age: 0}
-	for i := 0; i < rounds; i++ {
-		user.Age++
-		b1, _ := json.Marshal(user)
-		_, err = conn.Write(b1)
-		if err != nil {
-			fmt.Printf("StartTCPClient, conn.Write err: %v\n", err)
-			break
-		}
-
-		b2 := make([]byte, 1024)
-		n, err := conn.Read(b2)
-		if err != nil {
-			fmt.Printf("StartTCPClient, conn.Read err: %v\n", err)
-			break
-		}
-		respUser := &Person{}
-		err = json.Unmarshal(b2[:n], respUser)
-		if err != nil {
-			fmt.Printf("StartTCPClient, json.Unmarshal err: %v\n", err)
-			break
-		}
-
-		fmt.Printf("respUser %+v\n", respUser)
-		if respUser.Age != user.Age {
-			fmt.Printf("StartTCPClient, got wrong response, sent %+v, recv %+v\n", user, respUser)
-			break
-		}
-	}
-	conn.Write([]byte(tcpClientExited))
-	time.Sleep(time.Second)
-	ch <- tcpClientExited
-
-	return nil
-}
diff --git a/tests/tun_test.go b/tests/tun_test.go
new file mode 100644
index 0000000..f15f94c
--- /dev/null
+++ b/tests/tun_test.go
@@ -0,0 +1,25 @@
+package tests
+
+import (
+	"fmt"
+	"testing"
+	"time"
+)
+
+// go test -v -run=TestTun
+func TestTun(t *testing.T) {
+	tuna, udp, tun := true, true, false
+	go func() {
+		err := startNconnect("client.json", tuna, udp, tun, nil)
+		if err != nil {
+			fmt.Printf("start nconnect client err: %v\n", err)
+			return
+		}
+	}()
+	time.Sleep(20 * time.Second)
+
+	dnsQuery()
+	StartTunWebClient()
+	StartTCPClient()
+	StartUDPClient()
+}
diff --git a/tests/udp_proxy_test.go b/tests/udp.go
similarity index 61%
rename from tests/udp_proxy_test.go
rename to tests/udp.go
index 2673a1a..c84b8d2 100644
--- a/tests/udp_proxy_test.go
+++ b/tests/udp.go
@@ -5,47 +5,11 @@ import (
 	"encoding/json"
 	"fmt"
 	"net"
-	"strings"
-	"testing"
 	"time"
 
 	"github.com/txthinking/socks5"
 )
 
-// go test -v -run=TestUDPByProxy
-func TestUDPByProxy(t *testing.T) {
-	tunaNode, err := getTunaNode()
-	if err != nil {
-		fmt.Printf("getTunaNode err %v\n", err)
-		return
-	}
-
-	tuna, udp, tun := true, true, false
-	go func() {
-		err := startNconnect("server.json", tuna, udp, tun, tunaNode)
-		if err != nil {
-			fmt.Printf("start nconnect server err: %v\n", err)
-			return
-		}
-	}()
-
-	time.Sleep(15 * time.Second)
-
-	go func() {
-		err := startNconnect("client.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect client err: %v\n", err)
-			return
-		}
-	}()
-
-	time.Sleep(15 * time.Second)
-
-	go StartUDPClient()
-
-	waitFor(ch, exited)
-}
-
 func StartUdpServer() error {
 	a, err := net.ResolveUDPAddr("udp", udpServerAddr)
 	if err != nil {
@@ -65,7 +29,6 @@ func StartUdpServer() error {
 			fmt.Printf("StartUdpServer.ReadFromUDP err: %v\n", err)
 			break
 		}
-		fmt.Printf("UDP Server got: %v\n", string(b[:n]))
 
 		time.Sleep(100 * time.Millisecond)
 		_, _, err = udpServer.WriteMsgUDP(b[:n], nil, addr)
@@ -73,26 +36,19 @@ func StartUdpServer() error {
 			fmt.Printf("StartUdpServer.WriteMsgUDP err: %v\n", err)
 			break
 		}
-
-		if strings.Contains(string(b[:n]), udpClientExited) {
-			break
-		}
 	}
 
-	ch <- udpServerExited
 	return nil
 }
 
 func StartUDPClient() error {
 	s5c, err := socks5.NewClient(proxyAddr, "", "", 0, 60)
 	if err != nil {
-		ch <- udpClientExited
 		return err
 	}
 	uc, err := s5c.Dial("udp", udpServerAddr)
 	if err != nil {
 		fmt.Println("StartUDPClient.s5c.Dial err: ", err)
-		ch <- udpClientExited
 		return err
 	}
 	defer uc.Close()
@@ -118,10 +74,5 @@ func StartUDPClient() error {
 		}
 	}
 
-	uc.Write([]byte(udpClientExited))
-	time.Sleep(time.Second)
-
-	ch <- udpClientExited
-
 	return nil
 }
diff --git a/tests/udp_tun_test.go b/tests/udp_tun_test.go
deleted file mode 100644
index 4ef8d49..0000000
--- a/tests/udp_tun_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package tests
-
-import (
-	"fmt"
-	"testing"
-	"time"
-)
-
-// go test -v -run=TestUDPByTun
-func TestUDPByTun(t *testing.T) {
-	tunaNode, err := getTunaNode()
-	if err != nil {
-		fmt.Printf("getTunaNode err %v\n", err)
-		return
-	}
-
-	tuna, udp, tun := true, true, false
-	go func() {
-		err := startNconnect("server.json", tuna, udp, tun, tunaNode)
-		if err != nil {
-			fmt.Printf("start nconnect server err: %v\n", err)
-			return
-		}
-	}()
-
-	time.Sleep(15 * time.Second)
-
-	tun = true
-	go func() {
-		err := startNconnect("client.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect client err: %v\n", err)
-			return
-		}
-	}()
-
-	time.Sleep(15 * time.Second)
-
-	go StartUDPClient()
-
-	waitFor(ch, udpClientExited)
-}
diff --git a/tests/http_proxy_test.go b/tests/web.go
similarity index 66%
rename from tests/http_proxy_test.go
rename to tests/web.go
index b9fdf78..e47fe33 100644
--- a/tests/http_proxy_test.go
+++ b/tests/web.go
@@ -8,43 +8,13 @@ import (
 	"log"
 	"net/http"
 	"net/url"
-	"testing"
 	"time"
 )
 
-// go test -v -run=TestHttpByProxy
-func TestHttpByProxy(t *testing.T) {
-	tuna, udp, tun := true, true, false
-
-	go func() {
-		err := startNconnect("server.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect server err: %v\n", err)
-			return
-		}
-	}()
-
-	time.Sleep(15 * time.Second)
-
-	go func() {
-		err := startNconnect("client.json", tuna, udp, tun, nil)
-		if err != nil {
-			fmt.Printf("start nconnect client err: %v\n", err)
-			return
-		}
-	}()
-	time.Sleep(15 * time.Second)
-
-	go StartWebClient()
-
-	waitFor(ch, exited)
-}
-
 func StartWebServer() error {
 	http.HandleFunc("/httpEcho", httpEcho)
 	fmt.Println("WEB server is serving at ", httpServerAddr)
 	if err := http.ListenAndServe(httpServerAddr, nil); err != nil {
-		ch <- webServerExited
 		log.Fatal(err)
 	}
 
@@ -64,7 +34,6 @@ func httpEcho(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	fmt.Println("web server got user:", user)
 	b, _ := json.Marshal(user)
 	w.Write(b)
 }
@@ -122,7 +91,6 @@ func StartWebClient() error {
 			break
 		}
 
-		fmt.Printf("respUser %+v\n", respUser)
 		if respUser.Age != user.Age {
 			fmt.Printf("StartWebClient got wrong response, sent %+v, recv %+v\n", user, respUser)
 			break
@@ -130,7 +98,58 @@ func StartWebClient() error {
 	}
 
 	time.Sleep(time.Second)
-	ch <- webClientExited
+
+	return nil
+}
+
+func StartTunWebClient() error {
+	httpClient := &http.Client{
+		Timeout: 10 * time.Second,
+	}
+
+	user := &Person{Name: "http_tun_boy", Age: 0}
+	b := new(bytes.Buffer)
+
+	for i := 0; i < 10; i++ {
+		user.Age++
+		err := json.NewEncoder(b).Encode(user)
+		if err != nil {
+			fmt.Printf("StartWebClient.Encode err: %v\n", err)
+			break
+		}
+		req, err := http.NewRequest(http.MethodPost, httpServiceUrl, b)
+		req.Header.Set("Content-type", "application/json")
+
+		if err != nil {
+			fmt.Printf("StartWebClient.http.NewRequest err: %v\n", err)
+			break
+		}
+
+		resp, err := httpClient.Do(req)
+		if err != nil {
+			fmt.Printf("StartWebClient.http.Do err: %v\n", err)
+			break
+		}
+		defer resp.Body.Close()
+
+		body, err := io.ReadAll(resp.Body)
+		if err != nil {
+			fmt.Printf("StartWebClient.io.ReadAll err: %v\n", err)
+			break
+		}
+
+		respUser := &Person{}
+		err = json.Unmarshal(body, respUser)
+		if err != nil {
+			fmt.Printf("StartWebClient.json.Unmarshal err: %v\n", err)
+			break
+		}
+
+		if respUser.Age != user.Age {
+			fmt.Printf("StartWebClient got wrong response, sent %+v, recv %+v\n", user, respUser)
+			break
+		}
+	}
 
 	return nil
 }