Repository: pingcap/dumpling
Pull request #341 (2021-08-24 03:37:46)

*: update dumpling log and pd usage (#335)
This is an automated cherry-pick of #335

<!--
Thank you for contributing to Dumpling! Please read the [CONTRIBUTING](https://github.com/pingcap/dumpling/blob/master/CONTRIBUTING.md) document **BEFORE** filing this PR.

If you want to open the **Challenge Program** pull request, please use the following template:
https://raw.githubusercontent.com/pingcap/.github/master/.github/PULL_REQUEST_TEMPLATE/challenge-program.md

You can use it with query parameters: https://github.com/pingcap/dumpling/compare/master...${you branch}?template=challenge-program.md
-->

### What problem does this PR solve? <!--add issue link with summary if exists-->
1. Dumpling's warning log with error stack may terrify users. Users may think the dump task is failed but actually, it's not.
2. Dumpling may fail when dumpling account lacks privileges for `MYSQL.TiDB`.

### What is changed and how it works?
1. Change error stack to short error.
2. Ignore all errors when trying auto adjust gc to PD.

### Check List <!--REMOVE the items that are not applicable-->

Tests <!-- At least one of them must be included. -->

 - Unit test
 - Manual test (add detailed scripts or steps below)

Related changes

 - Need to cherry-pick to the release branch
 
### Release note

<!-- bugfixes or new feature need a release note, must in the form of a list, such as

- support -T/--tables-list argument

or if no need to be included in the release note, just add the following line

- No release note
-->
- No release note


ti-chi-bot: [REVIEW NOTIFICATION]

This pull request has not been approved.


To complete the [pull request process](https://book.prow.tidb.io/#/en/workflows/pr), please ask the reviewers in the [list](https://prow.tidb.io/tichi/repos/pingcap/dumpling/pulls/341/owners) to review by filling `/cc @reviewer` in the comment.
After your PR has acquired the required number of LGTMs, you can assign this pull request to the committer in the [list](https://prow.tidb.io/tichi/repos/pingcap/dumpling/pulls/341/owners) by filling  `/assign @committer` in the comment to help you merge this pull request.

The full list of commands accepted by this bot can be found [here](https://prow.tidb.io/command-help?repo=pingcap%2Fdumpling).

<details>

Reviewer can indicate their review by submitting an approval review.
Reviewer can cancel approval by submitting a request changes review.
</details>

<!--Review Notification Identifier-->
ti-chi-bot: @ti-chi-bot: This cherry pick PR is for a release branch and has not yet been approved by release team.
Adding the `do-not-merge/cherry-pick-not-approved` label.

To merge this cherry pick, it must first be approved by the collaborators.

**AFTER** it has been approved by collaborators, please ping the release team in a comment to request a cherry pick review.


<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index c57b33b..eaf83d3 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -29,22 +29,113 @@ jobs:
         run: make test WITH_RACE=1
       - uses: codecov/codecov-action@v1
 
-  integration-test:
+  integration-test-mysql-5735:
     runs-on: ubuntu-latest
     timeout-minutes: 15
     strategy:
       fail-fast: true
+    services:
+      mysql:
+        image: mysql:5.7.35
+        env:
+          MYSQL_ALLOW_EMPTY_PASSWORD: yes
+        ports:
+          - 3306:3306
+        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
     steps:
       - uses: actions/checkout@v2
+      - name: Shutdown Ubuntu MySQL (SUDO)
+        run: sudo service mysql stop # Shutdown the Default MySQL, "sudo" is necessary, please not remove it
       - name: Set up Go 1.16
         uses: actions/setup-go@v2
         with:
           go-version: 1.16
+      - uses: actions/cache@v2
+        with:
+          path: ~/go/pkg/mod
+          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+          restore-keys: |
+            ${{ runner.os }}-go-
+      - name: Get dependencies
+        run: go mod download
+      - name: Download dependencies
+        run: sh install.sh
+      - name: Integration test
+        run: make integration_test
+      - name: Set up tmate session
+        if: ${{ failure() }}
+        uses: mxschmitt/action-tmate@v3
+
+  integration-test-mysql-8026:
+    runs-on: ubuntu-latest
+    timeout-minutes: 15
+    strategy:
+      fail-fast: true
+    services:
+      mysql:
+        image: mysql:8.0.26
+        env:
+          MYSQL_ALLOW_EMPTY_PASSWORD: yes
+        ports:
+          - 3306:3306
+        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+    steps:
+      - uses: actions/checkout@v2
+      - name: Shutdown Ubuntu MySQL (SUDO)
+        run: sudo service mysql stop # Shutdown the Default MySQL, "sudo" is necessary, please not remove it
+      - name: Set up Go 1.16
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.16
+      - uses: actions/cache@v2
+        with:
+          path: ~/go/pkg/mod
+          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+          restore-keys: |
+            ${{ runner.os }}-go-
+      - name: Get dependencies
+        run: go mod download
+      - name: Download dependencies
+        run: sh install.sh
+      - name: Integration test
+        run: make integration_test
+      - name: Set up tmate session
+        if: ${{ failure() }}
+        uses: mxschmitt/action-tmate@v3
+
+  integration-test-mysql-8022:
+    runs-on: ubuntu-latest
+    timeout-minutes: 15
+    strategy:
+      fail-fast: true
+    services:
+      mysql:
+        image: mysql:8.0.22
+        env:
+          MYSQL_ALLOW_EMPTY_PASSWORD: yes
+        ports:
+          - 3306:3306
+        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+    steps:
+      - uses: actions/checkout@v2
+      - name: Shutdown Ubuntu MySQL (SUDO)
+        run: sudo service mysql stop # Shutdown the Default MySQL, "sudo" is necessary, please not remove it
+      - name: Set up Go 1.16
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.16
+      - uses: actions/cache@v2
+        with:
+          path: ~/go/pkg/mod
+          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+          restore-keys: |
+            ${{ runner.os }}-go-
+      - name: Get dependencies
+        run: go mod download
       - name: Download dependencies
         run: sh install.sh
-      - name: Start MySQL
-        run: |
-          sudo systemctl start mysql.service
-          mysqladmin -uroot -proot password ''
       - name: Integration test
         run: make integration_test
+      - name: Set up tmate session
+        if: ${{ failure() }}
+        uses: mxschmitt/action-tmate@v3
diff --git a/Makefile b/Makefile
index 108c118..eb21c01 100644
--- a/Makefile
+++ b/Makefile
@@ -29,7 +29,7 @@ test: failpoint-enable
 
 integration_test: bins failpoint-enable bin/dumpling
 	@make failpoint-disable
-	./tests/run.sh
+	./tests/run.sh $(CASE)
 
 tools:
 	@echo "install tools..."
@@ -50,12 +50,10 @@ check:
 static: export GO111MODULE=on
 static: tools
 	@ # Not running vet and fmt through metalinter becauase it ends up looking at vendor
-	tools/bin/gofumports -w -d -format-only -local $(DUMPLING_PKG) $$($(PACKAGE_DIRECTORIES)) 2>&1 | $(CHECKER)
 	tools/bin/govet --shadow $$($(PACKAGE_DIRECTORIES)) 2>&1 | $(CHECKER)
 
 	@# why some lints are disabled?
 	@#   gochecknoglobals - disabled because we do use quite a lot of globals
-	@#          goimports - executed above already, gofumports
 	@#              gofmt - ditto
 	@#                gci - ditto
 	@#                wsl - too pedantic about the formatting
@@ -76,7 +74,6 @@ static: tools
 	@#      sqlclosecheck - the rows in dumpling is created in one function but closed in other functions
 	CGO_ENABLED=0 tools/bin/golangci-lint run --enable-all --deadline 120s \
 		--disable gochecknoglobals \
-		--disable goimports \
 		--disable gofmt \
 		--disable gci \
 		--disable wsl \
@@ -97,6 +94,8 @@ static: tools
 		--disable errorlint \
 		--disable sqlclosecheck \
 		--disable scopelint \
+		--disable gofumpt \
+		--disable interfacer \
 		$$($(PACKAGE_DIRECTORIES))
 	# pingcap/errors APIs are mixed with multiple patterns 'pkg/errors',
 	# 'juju/errors' and 'pingcap/parser'. To avoid confusion and mistake,
diff --git a/go.mod b/go.mod
index 0b62cfc..400ab9e 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,6 @@ require (
 	github.com/grpc-ecosystem/grpc-gateway v1.14.3 // indirect
 	github.com/joho/sqltocsv v0.0.0-20210428211105-a6d6801d59df // indirect
 	github.com/pingcap/br v5.1.0-alpha.0.20210601094737-6cb0c4abc210+incompatible
-	github.com/pingcap/check v0.0.0-20200212061837-5e12011dc712
 	github.com/pingcap/errors v0.11.5-0.20201126102027-b0a155152ca3
 	github.com/pingcap/failpoint v0.0.0-20210316064728-7acb0f0a3dfd
 	github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4
@@ -26,11 +25,13 @@ require (
 	github.com/soheilhy/cmux v0.1.4
 	github.com/spaolacci/murmur3 v1.1.0 // indirect
 	github.com/spf13/pflag v1.0.5
+	github.com/stretchr/testify v1.7.0
 	github.com/syndtr/goleveldb v1.0.1-0.20190625010220-02440ea7a285 // indirect
 	github.com/tikv/pd v1.1.0-beta.0.20210323121136-78679e5e209d
 	github.com/xitongsys/parquet-go v1.6.0 // indirect
 	go.etcd.io/bbolt v1.3.5 // indirect
 	go.etcd.io/etcd v0.5.0-alpha.5.0.20200824191128-ae9734ed278b
+	go.uber.org/goleak v0.10.0
 	go.uber.org/zap v1.16.0
 	golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
 	golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
diff --git a/go.sum b/go.sum
index c79de3a..e38f731 100644
--- a/go.sum
+++ b/go.sum
@@ -545,8 +545,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
 github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
 github.com/swaggo/http-swagger v0.0.0-20200308142732-58ac5e232fba/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0=
diff --git a/tools/Makefile b/tools/Makefile
index 5a8f387..468f984 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -1,7 +1,4 @@
-all: bin/gofumports bin/govet bin/revive bin/golangci-lint bin/failpoint-ctl
-
-bin/gofumports:
-	go build -o $@ mvdan.cc/gofumpt/gofumports
+all: bin/govet bin/revive bin/golangci-lint bin/failpoint-ctl
 
 bin/govet:
 	go build -o $@ github.com/dnephin/govet
diff --git a/tools/go.mod b/tools/go.mod
index 9036d82..dae27d2 100644
--- a/tools/go.mod
+++ b/tools/go.mod
@@ -7,5 +7,4 @@ require (
 	github.com/golangci/golangci-lint v1.33.0
 	github.com/mgechev/revive v1.0.2
 	github.com/pingcap/failpoint v0.0.0-20200702092429-9f69995143ce
-	mvdan.cc/gofumpt v0.0.0-20201123090407-3077abae40c0
 )
diff --git a/tools/go.sum b/tools/go.sum
index b9c528a..cf55c6e 100644
--- a/tools/go.sum
+++ b/tools/go.sum
@@ -151,9 +151,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -335,8 +334,6 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.6.2 h1:aIihoIOHCiLZHxyoNQ+ABL4NKhFTgKLBdMLyEAh98m0=
-github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryancurrah/gomodguard v1.1.0 h1:DWbye9KyMgytn8uYpuHkwf0RHqAYO6Ay/D0TbCpPtVU=
 github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUccKSJBU0UMXJFVM=
@@ -476,9 +473,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -489,7 +485,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -515,14 +510,12 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 h1:bNEHhJCnrwMKNMmOx3yAynp5vs5/gRy+XWFtZFu7NBM=
 golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -570,9 +563,8 @@ golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4X
 golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
 golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
 golang.org/x/tools v0.0.0-20201011145850-ed2f50202694/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.0.0-20201013201025-64a9e34f3752 h1:2ntEwh02rqo2jSsrYmp4yKHHjh0CbXP3ZtSUetSB+q8=
 golang.org/x/tools v0.0.0-20201013201025-64a9e34f3752/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
-golang.org/x/tools v0.0.0-20201121010211-780cb80bd7fb h1:z5+u0pkAUPUWd3taoTialQ2JAMo4Wo1Z3L25U4ZV9r0=
-golang.org/x/tools v0.0.0-20201121010211-780cb80bd7fb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -611,7 +603,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
@@ -635,9 +626,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
 honnef.co/go/tools v0.0.1-2020.1.6 h1:W18jzjh8mfPez+AwGLxmOImucz/IFjpNlrKVnaj2YVc=
 honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
 k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+mvdan.cc/gofumpt v0.0.0-20200802201014-ab5a8192947d h1:t8TAw9WgTLghti7RYkpPmqk4JtQ3+wcP5GgZqgWeWLQ=
 mvdan.cc/gofumpt v0.0.0-20200802201014-ab5a8192947d/go.mod h1:bzrjFmaD6+xqohD3KYP0H2FEuxknnBmyyOxdhLdaIws=
-mvdan.cc/gofumpt v0.0.0-20201123090407-3077abae40c0 h1:kQ90sSvDP83HYKOsmrpDtcKkbWPjWYL2LmSCz1oE6vE=
-mvdan.cc/gofumpt v0.0.0-20201123090407-3077abae40c0/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0=
 mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
 mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
 mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
diff --git a/tools/go_mod_guard.go b/tools/go_mod_guard.go
index 969d4f2..15cf078 100644
--- a/tools/go_mod_guard.go
+++ b/tools/go_mod_guard.go
@@ -14,7 +14,5 @@ import (
 
 	// failpoint enables manual 'failure' of some execution points.
 	_ "github.com/pingcap/failpoint"
-
-	// A stricter gofmt
-	_ "mvdan.cc/gofumpt/gofumports"
+	_ "github.com/pingcap/failpoint/code"
 )
diff --git a/v4/export/block_allow_list.go b/v4/export/block_allow_list.go
index 381bea6..194be0e 100644
--- a/v4/export/block_allow_list.go
+++ b/v4/export/block_allow_list.go
@@ -8,6 +8,23 @@ import (
 	tcontext "github.com/pingcap/dumpling/v4/context"
 )
 
+func filterDatabases(tctx *tcontext.Context, conf *Config, databases []string) []string {
+	tctx.L().Debug("start to filter databases")
+	newDatabases := make([]string, 0, len(databases))
+	ignoreDatabases := make([]string, 0, len(databases))
+	for _, database := range databases {
+		if conf.TableFilter.MatchSchema(database) {
+			newDatabases = append(newDatabases, database)
+		} else {
+			ignoreDatabases = append(ignoreDatabases, database)
+		}
+	}
+	if len(ignoreDatabases) > 0 {
+		tctx.L().Debug("ignore database", zap.Strings("databases", ignoreDatabases))
+	}
+	return newDatabases
+}
+
 func filterTables(tctx *tcontext.Context, conf *Config) {
 	filterTablesFunc(tctx, conf, conf.TableFilter.MatchTable)
 }
diff --git a/v4/export/config.go b/v4/export/config.go
index 4ef6d45..5a3093c 100644
--- a/v4/export/config.go
+++ b/v4/export/config.go
@@ -706,3 +706,16 @@ func adjustFileFormat(conf *Config) error {
 	}
 	return nil
 }
+
+func matchMysqlBugversion(info ServerInfo) bool {
+	// if 8.0.3 <= mysql8 version < 8.0.23
+	// FLUSH TABLES WITH READ LOCK could block other sessions from executing SHOW TABLE STATUS.
+	// see more in https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-23.html
+	if info.ServerType != ServerTypeMySQL {
+		return false
+	}
+	currentVersion := info.ServerVersion
+	bugVersionStart := semver.New("8.0.2")
+	bugVersionEnd := semver.New("8.0.23")
+	return bugVersionStart.LessThan(*currentVersion) && currentVersion.LessThan(*bugVersionEnd)
+}
diff --git a/v4/export/dump.go b/v4/export/dump.go
index c428ae2..20e227e 100755
--- a/v4/export/dump.go
+++ b/v4/export/dump.go
@@ -470,7 +470,7 @@ func (d *Dumper) concurrentDumpTable(tctx *tcontext.Context, conn *sql.Conn, met
 			return err
 		}
 		tctx.L().Warn("fallback to concurrent dump tables using rows due to tidb error",
-			zap.String("database", db), zap.String("table", tbl), zap.Error(err))
+			zap.String("database", db), zap.String("table", tbl), log.ShortError(err))
 	}
 
 	orderByClause, err := buildOrderByClause(conf, conn, db, tbl, meta.HasImplicitRowID())
@@ -482,7 +482,7 @@ func (d *Dumper) concurrentDumpTable(tctx *tcontext.Context, conn *sql.Conn, met
 	if err != nil || field == "" {
 		// skip split chunk logic if not found proper field
 		tctx.L().Warn("fallback to sequential dump due to no proper field",
-			zap.String("database", db), zap.String("table", tbl), zap.Error(err))
+			zap.String("database", db), zap.String("table", tbl), log.ShortError(err))
 		return d.dumpWholeTableDirectly(tctx, meta, taskChan, "", orderByClause, 0, 1)
 	}
 
@@ -851,8 +851,21 @@ func extractTiDBRowIDFromDecodedKey(indexField, key string) (string, error) {
 	return "", errors.Errorf("decoded key %s doesn't have %s field", key, indexField)
 }
 
+func getListTableTypeByConf(conf *Config) listTableType {
+	// use listTableByShowTableStatus by default because it has better performance
+	listType := listTableByShowTableStatus
+	if conf.Consistency == consistencyTypeLock {
+		// for consistency lock, we need to build the tables to dump as soon as possible
+		listType = listTableByInfoSchema
+	} else if conf.Consistency == consistencyTypeFlush && matchMysqlBugversion(conf.ServerInfo) {
+		// For some buggy versions of mysql, we need a workaround to get a list of table names.
+		listType = listTableByShowFullTables
+	}
+	return listType
+}
+
 func prepareTableListToDump(tctx *tcontext.Context, conf *Config, db *sql.Conn) error {
-	databases, err := prepareDumpingDatabases(conf, db)
+	databases, err := prepareDumpingDatabases(tctx, conf, db)
 	if err != nil {
 		return err
 	}
@@ -861,9 +874,7 @@ func prepareTableListToDump(tctx *tcontext.Context, conf *Config, db *sql.Conn)
 	if !conf.NoViews {
 		tableTypes = append(tableTypes, TableTypeView)
 	}
-	// for consistency lock, we need to build the tables to dump as soon as possible
-	asap := conf.Consistency == consistencyTypeLock
-	conf.Tables, err = ListAllDatabasesTables(tctx, db, databases, asap, tableTypes...)
+	conf.Tables, err = ListAllDatabasesTables(tctx, db, databases, getListTableTypeByConf(conf), tableTypes...)
 	if err != nil {
 		return err
 	}
@@ -1012,7 +1023,7 @@ func startHTTPService(d *Dumper) error {
 		go func() {
 			err := startDumplingService(d.tctx, conf.StatusAddr)
 			if err != nil {
-				d.L().Warn("meet error when stopping dumpling http service", zap.Error(err))
+				d.L().Warn("meet error when stopping dumpling http service", log.ShortError(err))
 			}
 		}()
 	}
@@ -1045,16 +1056,16 @@ func detectServerInfo(d *Dumper) error {
 // resolveAutoConsistency is an initialization step of Dumper.
 func resolveAutoConsistency(d *Dumper) error {
 	conf := d.conf
-	if conf.Consistency != "auto" {
+	if conf.Consistency != consistencyTypeAuto {
 		return nil
 	}
 	switch conf.ServerInfo.ServerType {
 	case ServerTypeTiDB:
-		conf.Consistency = "snapshot"
+		conf.Consistency = consistencyTypeSnapshot
 	case ServerTypeMySQL, ServerTypeMariaDB:
-		conf.Consistency = "flush"
+		conf.Consistency = consistencyTypeFlush
 	default:
-		conf.Consistency = "none"
+		conf.Consistency = consistencyTypeNone
 	}
 	return nil
 }
@@ -1069,16 +1080,17 @@ func tidbSetPDClientForGC(d *Dumper) error {
 	}
 	pdAddrs, err := GetPdAddrs(tctx, pool)
 	if err != nil {
-		return err
+		tctx.L().Warn("meet error while fetching pd addrs", log.ShortError(err))
+		return nil
 	}
 	if len(pdAddrs) > 0 {
 		doPdGC, err := checkSameCluster(tctx, pool, pdAddrs)
 		if err != nil {
-			tctx.L().Warn("meet error while check whether fetched pd addr and TiDB belong to one cluster", zap.Error(err), zap.Strings("pdAddrs", pdAddrs))
+			tctx.L().Warn("meet error while check whether fetched pd addr and TiDB belong to one cluster", log.ShortError(err), zap.Strings("pdAddrs", pdAddrs))
 		} else if doPdGC {
 			pdClient, err := pd.NewClientWithContext(tctx, pdAddrs, pd.SecurityOption{})
 			if err != nil {
-				tctx.L().Warn("create pd client to control GC failed", zap.Error(err), zap.Strings("pdAddrs", pdAddrs))
+				tctx.L().Warn("create pd client to control GC failed", log.ShortError(err), zap.Strings("pdAddrs", pdAddrs))
 			}
 			d.tidbPDClientForGC = pdClient
 		}
@@ -1094,13 +1106,13 @@ func tidbGetSnapshot(d *Dumper) error {
 	if conf.Snapshot == "" && (doPdGC || consistency == "snapshot") {
 		conn, err := pool.Conn(tctx)
 		if err != nil {
-			tctx.L().Warn("cannot get snapshot from TiDB", zap.Error(err))
+			tctx.L().Warn("cannot get snapshot from TiDB", log.ShortError(err))
 			return nil
 		}
 		snapshot, err := getSnapshot(conn)
 		_ = conn.Close()
 		if err != nil {
-			tctx.L().Warn("cannot get snapshot from TiDB", zap.Error(err))
+			tctx.L().Warn("cannot get snapshot from TiDB", log.ShortError(err))
 			return nil
 		}
 		conf.Snapshot = snapshot
@@ -1175,7 +1187,7 @@ func setSessionParam(d *Dumper) error {
 		if consistency == consistencyTypeSnapshot {
 			conf.ServerInfo.HasTiKV, err = CheckTiDBWithTiKV(pool)
 			if err != nil {
-				return err
+				d.L().Warn("fail to check whether TiDB has TiKV", log.ShortError(err))
 			}
 			if conf.ServerInfo.HasTiKV {
 				sessionParam["tidb_snapshot"] = snapshot
diff --git a/v4/export/http_handler.go b/v4/export/http_handler.go
index 656e68e..d7a6a6c 100644
--- a/v4/export/http_handler.go
+++ b/v4/export/http_handler.go
@@ -10,11 +10,11 @@ import (
 	"time"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/pingcap/dumpling/v4/log"
 
 	"github.com/pingcap/errors"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
 	"github.com/soheilhy/cmux"
-	"go.uber.org/zap"
 )
 
 var cmuxReadTimeout = 10 * time.Second
@@ -35,7 +35,7 @@ func startHTTPServer(tctx *tcontext.Context, lis net.Listener) {
 	err := httpServer.Serve(lis)
 	err = errors.Cause(err)
 	if err != nil && !isErrNetClosing(err) && err != http.ErrServerClosed {
-		tctx.L().Warn("http server return with error", zap.Error(err))
+		tctx.L().Warn("dumpling http handler return with error", log.ShortError(err))
 	}
 }
 
diff --git a/v4/export/prepare.go b/v4/export/prepare.go
index e995f8a..2a6d7a1 100644
--- a/v4/export/prepare.go
+++ b/v4/export/prepare.go
@@ -10,6 +10,8 @@ import (
 	"text/template"
 
 	"github.com/pingcap/errors"
+
+	tcontext "github.com/pingcap/dumpling/v4/context"
 )
 
 const (
@@ -75,10 +77,14 @@ func ParseOutputFileTemplate(text string) (*template.Template, error) {
 	return template.Must(DefaultOutputFileTemplate.Clone()).Parse(text)
 }
 
-func prepareDumpingDatabases(conf *Config, db *sql.Conn) ([]string, error) {
+func prepareDumpingDatabases(tctx *tcontext.Context, conf *Config, db *sql.Conn) ([]string, error) {
 	databases, err := ShowDatabases(db)
+	if err != nil {
+		return nil, err
+	}
+	databases = filterDatabases(tctx, conf, databases)
 	if len(conf.Databases) == 0 {
-		return databases, err
+		return databases, nil
 	}
 	dbMap := make(map[string]interface{}, len(databases))
 	for _, database := range databases {
diff --git a/v4/export/sql.go b/v4/export/sql.go
index 8011115..93aa4d7 100644
--- a/v4/export/sql.go
+++ b/v4/export/sql.go
@@ -13,6 +13,7 @@ import (
 	"strings"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/pingcap/dumpling/v4/log"
 
 	"github.com/go-sql-driver/mysql"
 	"github.com/pingcap/errors"
@@ -21,7 +22,17 @@ import (
 	"go.uber.org/zap"
 )
 
-const orderByTiDBRowID = "ORDER BY `_tidb_rowid`"
+const (
+	orderByTiDBRowID = "ORDER BY `_tidb_rowid`"
+)
+
+type listTableType int
+
+const (
+	listTableByInfoSchema listTableType = iota
+	listTableByShowFullTables
+	listTableByShowTableStatus
+)
 
 // ShowDatabases shows the databases of a database server.
 func ShowDatabases(db *sql.Conn) ([]string, error) {
@@ -151,9 +162,11 @@ func RestoreCharset(w io.StringWriter) {
 }
 
 // ListAllDatabasesTables lists all the databases and tables from the database
-// if asap is true, will use information_schema to get table info in one query
-// if asap is false, will use show table status for each database because it has better performance according to our tests
-func ListAllDatabasesTables(tctx *tcontext.Context, db *sql.Conn, databaseNames []string, asap bool, tableTypes ...TableType) (DatabaseTables, error) { // revive:disable-line:flag-parameter
+// listTableByInfoSchema list tables by table information_schema in MySQL
+// listTableByShowTableStatus has better performance than listTableByInfoSchema
+// listTableByShowFullTables is used in mysql8 version [8.0.3,8.0.23), more details can be found in the comments of func matchMysqlBugversion
+func ListAllDatabasesTables(tctx *tcontext.Context, db *sql.Conn, databaseNames []string,
+	listType listTableType, tableTypes ...TableType) (DatabaseTables, error) { // revive:disable-line:flag-parameter
 	dbTables := DatabaseTables{}
 	var (
 		schema, table, tableTypeStr string
@@ -161,11 +174,13 @@ func ListAllDatabasesTables(tctx *tcontext.Context, db *sql.Conn, databaseNames
 		avgRowLength                uint64
 		err                         error
 	)
-	if asap {
-		tableTypeConditions := make([]string, len(tableTypes))
-		for i, tableType := range tableTypes {
-			tableTypeConditions[i] = fmt.Sprintf("TABLE_TYPE='%s'", tableType)
-		}
+
+	tableTypeConditions := make([]string, len(tableTypes))
+	for i, tableType := range tableTypes {
+		tableTypeConditions[i] = fmt.Sprintf("TABLE_TYPE='%s'", tableType)
+	}
+	switch listType {
+	case listTableByInfoSchema:
 		query := fmt.Sprintf("SELECT TABLE_SCHEMA,TABLE_NAME,TABLE_TYPE,AVG_ROW_LENGTH FROM INFORMATION_SCHEMA.TABLES WHERE %s", strings.Join(tableTypeConditions, " OR "))
 		for _, schema := range databaseNames {
 			dbTables[schema] = make([]*TableInfo, 0)
@@ -196,7 +211,28 @@ func ListAllDatabasesTables(tctx *tcontext.Context, db *sql.Conn, databaseNames
 		}, query); err != nil {
 			return nil, errors.Annotatef(err, "sql: %s", query)
 		}
-	} else {
+	case listTableByShowFullTables:
+		for _, schema = range databaseNames {
+			dbTables[schema] = make([]*TableInfo, 0)
+			query := fmt.Sprintf("SHOW FULL TABLES FROM `%s` WHERE %s",
+				escapeString(schema), strings.Join(tableTypeConditions, " OR "))
+			if err = simpleQueryWithArgs(db, func(rows *sql.Rows) error {
+				var err2 error
+				if err2 = rows.Scan(&table, &tableTypeStr); err != nil {
+					return errors.Trace(err2)
+				}
+				tableType, err2 = ParseTableType(tableTypeStr)
+				if err2 != nil {
+					return errors.Trace(err2)
+				}
+				avgRowLength = 0 // can't get avgRowLength from the result of `show full tables` so hardcode to 0 here
+				dbTables[schema] = append(dbTables[schema], &TableInfo{table, avgRowLength, tableType})
+				return nil
+			}, query); err != nil {
+				return nil, errors.Annotatef(err, "sql: %s", query)
+			}
+		}
+	default:
 		queryTemplate := "SHOW TABLE STATUS FROM `%s`"
 		selectedTableType := make(map[TableType]struct{})
 		for _, tableType = range tableTypes {
@@ -550,11 +586,10 @@ func GetPdAddrs(tctx *tcontext.Context, db *sql.DB) ([]string, error) {
 	query := "SELECT * FROM information_schema.cluster_info where type = 'pd';"
 	rows, err := db.QueryContext(tctx, query)
 	if err != nil {
-		tctx.L().Warn("can't execute query from db",
-			zap.String("query", query), zap.Error(err))
 		return []string{}, errors.Annotatef(err, "sql: %s", query)
 	}
-	return GetSpecifiedColumnValueAndClose(rows, "STATUS_ADDRESS")
+	pdAddrs, err := GetSpecifiedColumnValueAndClose(rows, "STATUS_ADDRESS")
+	return pdAddrs, errors.Annotatef(err, "sql: %s", query)
 }
 
 // GetTiDBDDLIDs gets DDL IDs from TiDB
@@ -562,11 +597,10 @@ func GetTiDBDDLIDs(tctx *tcontext.Context, db *sql.DB) ([]string, error) {
 	query := "SELECT * FROM information_schema.tidb_servers_info;"
 	rows, err := db.QueryContext(tctx, query)
 	if err != nil {
-		tctx.L().Warn("can't execute query from db",
-			zap.String("query", query), zap.Error(err))
 		return []string{}, errors.Annotatef(err, "sql: %s", query)
 	}
-	return GetSpecifiedColumnValueAndClose(rows, "DDL_ID")
+	ddlIDs, err := GetSpecifiedColumnValueAndClose(rows, "DDL_ID")
+	return ddlIDs, errors.Annotatef(err, "sql: %s", query)
 }
 
 // CheckTiDBWithTiKV use sql to check whether current TiDB has TiKV
@@ -576,7 +610,9 @@ func CheckTiDBWithTiKV(db *sql.DB) (bool, error) {
 	row := db.QueryRow(query)
 	err := row.Scan(&count)
 	if err != nil {
-		return false, errors.Annotatef(err, "sql: %s", query)
+		// still return true here. Because sometimes users may not have privileges for MySQL.TiDB database
+		// In most production cases TiDB has TiKV
+		return true, errors.Annotatef(err, "sql: %s", query)
 	}
 	return count > 0, nil
 }
@@ -960,8 +996,8 @@ func estimateCount(tctx *tcontext.Context, dbName, tableName string, db *sql.Con
 func detectEstimateRows(tctx *tcontext.Context, db *sql.Conn, query string, fieldNames []string) uint64 {
 	rows, err := db.QueryContext(tctx, query)
 	if err != nil {
-		tctx.L().Warn("can't execute query from db",
-			zap.String("query", query), zap.Error(err))
+		tctx.L().Warn("can't detect estimate rows from db",
+			zap.String("query", query), log.ShortError(err))
 		return 0
 	}
 	defer rows.Close()
@@ -969,13 +1005,13 @@ func detectEstimateRows(tctx *tcontext.Context, db *sql.Conn, query string, fiel
 	columns, err := rows.Columns()
 	if err != nil {
 		tctx.L().Warn("can't get columns from db",
-			zap.String("query", query), zap.Error(err))
+			zap.String("query", query), log.ShortError(err))
 		return 0
 	}
 	err = rows.Err()
 	if err != nil {
 		tctx.L().Warn("rows meet some error during the query",
-			zap.String("query", query), zap.Error(err))
+			zap.String("query", query), log.ShortError(err))
 		return 0
 	}
 	addr := make([]interface{}, len(columns))
@@ -996,14 +1032,14 @@ found:
 	err = rows.Scan(addr...)
 	if err != nil || fieldIndex < 0 {
 		tctx.L().Warn("can't get estimate count from db",
-			zap.String("query", query), zap.Error(err))
+			zap.String("query", query), log.ShortError(err))
 		return 0
 	}
 
 	estRows, err := strconv.ParseFloat(oneRow[fieldIndex].String, 64)
 	if err != nil {
 		tctx.L().Warn("can't get parse rows from db",
-			zap.String("query", query), zap.Error(err))
+			zap.String("query", query), log.ShortError(err))
 		return 0
 	}
 	return uint64(estRows)
diff --git a/v4/export/writer_util.go b/v4/export/writer_util.go
index 18bcc6e..434fbf0 100755
--- a/v4/export/writer_util.go
+++ b/v4/export/writer_util.go
@@ -13,6 +13,7 @@ import (
 	"time"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/pingcap/dumpling/v4/log"
 
 	"github.com/pingcap/br/pkg/storage"
 	"github.com/pingcap/br/pkg/summary"
@@ -177,12 +178,12 @@ func WriteInsert(pCtx *tcontext.Context, cfg *Config, meta TableMeta, tblIR Tabl
 
 	defer func() {
 		if err != nil {
-			pCtx.L().Warn("fail to dumping table(chunk), will revert some metrics now",
-				zap.Error(err),
+			pCtx.L().Warn("fail to dumping table(chunk), will revert some metrics and start a retry if possible",
 				zap.String("database", meta.DatabaseName()),
 				zap.String("table", meta.TableName()),
 				zap.Uint64("finished rows", lastCounter),
-				zap.Uint64("finished size", wp.finishedFileSize))
+				zap.Uint64("finished size", wp.finishedFileSize),
+				log.ShortError(err))
 			SubGauge(finishedRowsGauge, cfg.Labels, float64(lastCounter))
 			SubGauge(finishedSizeGauge, cfg.Labels, float64(wp.finishedFileSize))
 		} else {
@@ -315,12 +316,12 @@ func WriteInsertInCsv(pCtx *tcontext.Context, cfg *Config, meta TableMeta, tblIR
 
 	defer func() {
 		if err != nil {
-			pCtx.L().Warn("fail to dumping table(chunk), will revert some metrics now",
-				zap.Error(err),
+			pCtx.L().Warn("fail to dumping table(chunk), will revert some metrics and start a retry if possible",
 				zap.String("database", meta.DatabaseName()),
 				zap.String("table", meta.TableName()),
 				zap.Uint64("finished rows", lastCounter),
-				zap.Uint64("finished size", wp.finishedFileSize))
+				zap.Uint64("finished size", wp.finishedFileSize),
+				log.ShortError(err))
 			SubGauge(finishedRowsGauge, cfg.Labels, float64(lastCounter))
 			SubGauge(finishedSizeGauge, cfg.Labels, float64(wp.finishedFileSize))
 		} else {
diff --git a/v4/log/log.go b/v4/log/log.go
index 79f840d..78df0a2 100644
--- a/v4/log/log.go
+++ b/v4/log/log.go
@@ -59,3 +59,12 @@ func InitAppLogger(cfg *Config) (Logger, *pclog.ZapProperties, error) {
 func NewAppLogger(logger *zap.Logger) Logger {
 	return Logger{logger}
 }
+
+// ShortError contructs a field which only records the error message without the
+// verbose text (i.e. excludes the stack trace).
+func ShortError(err error) zap.Field {
+	if err == nil {
+		return zap.Skip()
+	}
+	return zap.String("error", err.Error())
+}


Test patch:
diff --git a/tests/quote/data/quote-database-schema-create-mysql57.sql b/tests/quote/data/quote-database-schema-create-mysql57.sql
new file mode 100755
index 0000000..a5df1c2
--- /dev/null
+++ b/tests/quote/data/quote-database-schema-create-mysql57.sql
@@ -0,0 +1,2 @@
+/*!40101 SET NAMES binary*/;
+CREATE DATABASE `quo``te/database` /*!40100 DEFAULT CHARACTER SET latin1 */;
diff --git a/tests/quote/data/quote-database.quote-table-schema-mysql57.sql b/tests/quote/data/quote-database.quote-table-schema-mysql57.sql
new file mode 100755
index 0000000..b3c55de
--- /dev/null
+++ b/tests/quote/data/quote-database.quote-table-schema-mysql57.sql
@@ -0,0 +1,7 @@
+/*!40101 SET NAMES binary*/;
+CREATE TABLE `quo``te/table` (
+  `quo``te/col` int(11) NOT NULL,
+  `a` int(11) DEFAULT NULL,
+  `gen``id` int(11) GENERATED ALWAYS AS (`quo``te/col`) VIRTUAL,
+  PRIMARY KEY (`quo``te/col`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
diff --git a/tests/quote/data/quote-database.quote-table.000000000-mysql57.sql b/tests/quote/data/quote-database.quote-table.000000000-mysql57.sql
new file mode 100755
index 0000000..5cee6b7
--- /dev/null
+++ b/tests/quote/data/quote-database.quote-table.000000000-mysql57.sql
@@ -0,0 +1,13 @@
+/*!40101 SET NAMES binary*/;
+INSERT INTO `quo``te/table` (`quo``te/col`,`a`) VALUES
+(0,10),
+(1,9),
+(2,8),
+(3,7),
+(4,6),
+(5,5),
+(6,4),
+(7,3),
+(8,2),
+(9,1),
+(10,0);
diff --git a/tests/quote/run.sh b/tests/quote/run.sh
index 232d153..eafe61e 100644
--- a/tests/quote/run.sh
+++ b/tests/quote/run.sh
@@ -1,13 +1,23 @@
-#!/bin/sh
+#!/bin/bash
 #
 # Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
 
 set -eu
 
 mkdir -p "$DUMPLING_OUTPUT_DIR"/data
-cp "$DUMPLING_BASE_NAME/data/quote-database.quote-table.000000000.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase.quo\`te%2Ftable.000000000.sql"
-cp "$DUMPLING_BASE_NAME/data/quote-database.quote-table-schema.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase.quo\`te%2Ftable-schema.sql"
-cp "$DUMPLING_BASE_NAME/data/quote-database-schema-create.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase-schema-create.sql"
+
+mysql_version=$(echo "select version()" | mysql -uroot -h127.0.0.1 -P3306 | awk 'NR==2' | awk '{print $1}')
+echo "current user mysql version is $mysql_version"
+if [[ $mysql_version = 5* ]]; then
+  # there is a bug in mysql 5.x, see https://bugs.mysql.com/bug.php?id=96994, so we use different create db/table sql
+  cp "$DUMPLING_BASE_NAME/data/quote-database.quote-table.000000000-mysql57.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase.quo\`te%2Ftable.000000000.sql"
+  cp "$DUMPLING_BASE_NAME/data/quote-database.quote-table-schema-mysql57.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase.quo\`te%2Ftable-schema.sql"
+  cp "$DUMPLING_BASE_NAME/data/quote-database-schema-create-mysql57.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase-schema-create.sql"
+else
+  cp "$DUMPLING_BASE_NAME/data/quote-database.quote-table.000000000.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase.quo\`te%2Ftable.000000000.sql"
+  cp "$DUMPLING_BASE_NAME/data/quote-database.quote-table-schema.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase.quo\`te%2Ftable-schema.sql"
+  cp "$DUMPLING_BASE_NAME/data/quote-database-schema-create.sql" "$DUMPLING_OUTPUT_DIR/data/quo\`te%2Fdatabase-schema-create.sql"
+fi
 
 db="quo\`te/database"
 run_sql "drop database if exists \`quo\`\`te/database\`"
diff --git a/tests/run.sh b/tests/run.sh
index 407a1af..bc535c2 100755
--- a/tests/run.sh
+++ b/tests/run.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 #
 # Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
 
@@ -17,7 +17,6 @@ mkdir -p "$DUMPLING_TEST_DIR"
 PATH="tests/_utils:$PATH"
 . "tests/_utils/run_services"
 
-
 file_should_exist bin/tidb-server
 file_should_exist bin/tidb-lightning
 file_should_exist bin/dumpling
@@ -26,8 +25,9 @@ file_should_exist bin/sync_diff_inspector
 trap stop_services EXIT
 start_services
 
-for script in tests/*/run.sh; do
-    echo "****************** Running test $script..."
+run_case_by_fullpath() {
+    script="$1"
+    echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Running test $script..."
     DUMPLING_BASE_NAME="$(dirname "$script")"
     export DUMPLING_BASE_NAME
     TEST_NAME="$(basename "$(dirname "$script")")"
@@ -35,11 +35,24 @@ for script in tests/*/run.sh; do
     export DUMPLING_OUTPUT_DIR
 
     PATH="tests/_utils:$PATH" \
-    sh "$script"
-
-    echo "Cleaning up test output dir: $DUMPLING_OUTPUT_DIR"
+        bash "$script"
+    echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>TEST: $script Passed Cleaning up test output dir: $DUMPLING_OUTPUT_DIR"
     rm -rf "$DUMPLING_OUTPUT_DIR"
-
-done
-
-echo "Passed integration tests."
+}
+
+if [ "$#" -ge 1 ]; then
+    test_case="$@"
+else
+    test_case="*"
+fi
+
+if [ "$test_case" == "*" ]; then
+    for script in tests/*/run.sh; do
+        run_case_by_fullpath "$script"
+    done
+else
+    script="tests/$test_case/run.sh"
+    run_case_by_fullpath "$script"
+fi
+
+echo "Passed integration tests."
\ No newline at end of file
diff --git a/tests/views/run.sh b/tests/views/run.sh
index c9462e6..b975135 100644
--- a/tests/views/run.sh
+++ b/tests/views/run.sh
@@ -9,7 +9,8 @@ run_sql_file "$DUMPLING_BASE_NAME/data/views-schema-create.sql"
 export DUMPLING_TEST_DATABASE="views"
 
 run_sql "create table t (a bigint, b varchar(255))"
-run_sql "set session collation_connection='utf8mb4_general_ci'; create definer = 'root'@'localhost' view v as select * from t;"
+run_sql_file "$DUMPLING_BASE_NAME/data/views.v-schema-view.sql"
+
 # insert 20 records to `t`.
 run_sql "insert into t values $(seq -s, 20 | sed 's/,*$//g' | sed 's/[0-9]*/(\0,"\0")/g')"
 
diff --git a/v4/export/block_allow_list_test.go b/v4/export/block_allow_list_test.go
index fddc771..cdc784a 100644
--- a/v4/export/block_allow_list_test.go
+++ b/v4/export/block_allow_list_test.go
@@ -4,19 +4,18 @@ package export
 
 import (
 	"strings"
+	"testing"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
-
-	. "github.com/pingcap/check"
 	"github.com/pingcap/tidb-tools/pkg/filter"
 	tf "github.com/pingcap/tidb-tools/pkg/table-filter"
+	"github.com/stretchr/testify/require"
 )
 
-var _ = Suite(&testBWListSuite{})
-
-type testBWListSuite struct{}
+func TestFilterTables(t *testing.T) {
+	t.Parallel()
 
-func (s *testBWListSuite) TestFilterTables(c *C) {
+	tctx := tcontext.Background().WithLogger(appLogger)
 	dbTables := DatabaseTables{}
 	expectedDBTables := DatabaseTables{}
 
@@ -27,7 +26,8 @@ func (s *testBWListSuite) TestFilterTables(c *C) {
 	dbTables.AppendTables("yyy", []string{"xxx"}, []uint64{0})
 
 	tableFilter, err := tf.Parse([]string{"*.*"})
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	conf := &Config{
 		ServerInfo: ServerInfo{
 			ServerType: ServerTypeTiDB,
@@ -35,14 +35,20 @@ func (s *testBWListSuite) TestFilterTables(c *C) {
 		Tables:      dbTables,
 		TableFilter: tableFilter,
 	}
+	databases := []string{filter.InformationSchemaName, filter.PerformanceSchemaName, "xxx", "yyy"}
+	require.Equal(t, databases, filterDatabases(tctx, conf, databases))
 
 	conf.TableFilter = tf.NewSchemasFilter("xxx")
+	require.Equal(t, []string{"xxx"}, filterDatabases(tctx, conf, databases))
+
 	filterTables(tcontext.Background(), conf)
-	c.Assert(conf.Tables, HasLen, 1)
-	c.Assert(conf.Tables, DeepEquals, expectedDBTables)
+	require.Len(t, conf.Tables, 1)
+	require.Equal(t, expectedDBTables, conf.Tables)
 }
 
-func (s *testBWListSuite) TestFilterDatabaseWithNoTable(c *C) {
+func TestFilterDatabaseWithNoTable(t *testing.T) {
+	t.Parallel()
+
 	dbTables := DatabaseTables{}
 	expectedDBTables := DatabaseTables{}
 
@@ -56,21 +62,21 @@ func (s *testBWListSuite) TestFilterDatabaseWithNoTable(c *C) {
 		DumpEmptyDatabase: true,
 	}
 	filterTables(tcontext.Background(), conf)
-	c.Assert(conf.Tables, HasLen, 0)
+	require.Len(t, conf.Tables, 0)
 
 	dbTables["xxx"] = []*TableInfo{}
 	expectedDBTables["xxx"] = []*TableInfo{}
 	conf.Tables = dbTables
 	conf.TableFilter = tf.NewSchemasFilter("xxx")
 	filterTables(tcontext.Background(), conf)
-	c.Assert(conf.Tables, HasLen, 1)
-	c.Assert(conf.Tables, DeepEquals, expectedDBTables)
+	require.Len(t, conf.Tables, 1)
+	require.Equal(t, expectedDBTables, conf.Tables)
 
 	dbTables["xxx"] = []*TableInfo{}
 	expectedDBTables = DatabaseTables{}
 	conf.Tables = dbTables
 	conf.DumpEmptyDatabase = false
 	filterTables(tcontext.Background(), conf)
-	c.Assert(conf.Tables, HasLen, 0)
-	c.Assert(conf.Tables, DeepEquals, expectedDBTables)
+	require.Len(t, conf.Tables, 0)
+	require.Equal(t, expectedDBTables, conf.Tables)
 }
diff --git a/v4/export/config_test.go b/v4/export/config_test.go
index 466492d..c8a1531 100644
--- a/v4/export/config_test.go
+++ b/v4/export/config_test.go
@@ -3,18 +3,33 @@
 package export
 
 import (
-	"context"
+	"testing"
 
-	. "github.com/pingcap/check"
+	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/stretchr/testify/require"
 )
 
-var _ = Suite(&testConfigSuite{})
-
-type testConfigSuite struct{}
+func TestCreateExternalStorage(t *testing.T) {
+	t.Parallel()
+	mockConfig := defaultConfigForTest(t)
+	loc, err := mockConfig.createExternalStorage(tcontext.Background())
+	require.NoError(t, err)
+	require.Regexp(t, "file:.*", loc.URI())
+}
 
-func (s *testConfigSuite) TestCreateExternalStorage(c *C) {
-	mockConfig := defaultConfigForTest(c)
-	loc, err := mockConfig.createExternalStorage(context.Background())
-	c.Assert(err, IsNil)
-	c.Assert(loc.URI(), Matches, "file:.*")
+func TestMatchMysqlBugVersion(t *testing.T) {
+	t.Parallel()
+	cases := []struct {
+		serverInfo ServerInfo
+		expected   bool
+	}{
+		{ParseServerInfo(tcontext.Background(), "5.7.25-TiDB-3.0.6"), false},
+		{ParseServerInfo(tcontext.Background(), "8.0.2"), false},
+		{ParseServerInfo(tcontext.Background(), "8.0.3"), true},
+		{ParseServerInfo(tcontext.Background(), "8.0.22"), true},
+		{ParseServerInfo(tcontext.Background(), "8.0.23"), false},
+	}
+	for _, x := range cases {
+		require.Equalf(t, x.expected, matchMysqlBugversion(x.serverInfo), "server info: %s", x.serverInfo)
+	}
 }
diff --git a/v4/export/consistency_test.go b/v4/export/consistency_test.go
index 931dc49..5e54fcf 100644
--- a/v4/export/consistency_test.go
+++ b/v4/export/consistency_test.go
@@ -5,63 +5,55 @@ package export
 import (
 	"context"
 	"errors"
-	"strings"
+	"testing"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/stretchr/testify/require"
 
 	"github.com/DATA-DOG/go-sqlmock"
 	"github.com/go-sql-driver/mysql"
-	. "github.com/pingcap/check"
 )
 
-var _ = Suite(&testConsistencySuite{})
+func TestConsistencyController(t *testing.T) {
+	t.Parallel()
 
-type testConsistencySuite struct{}
-
-func (s *testConsistencySuite) assertNil(err error, c *C) {
-	if err != nil {
-		c.Fatal(err.Error())
-	}
-}
-
-func (s *testConsistencySuite) assertLifetimeErrNil(tctx *tcontext.Context, ctrl ConsistencyController, c *C) {
-	s.assertNil(ctrl.Setup(tctx), c)
-	s.assertNil(ctrl.TearDown(tctx), c)
-}
-
-func (s *testConsistencySuite) TestConsistencyController(c *C) {
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		_ = db.Close()
+	}()
+
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
+
 	tctx := tcontext.Background().WithContext(ctx)
-	conf := defaultConfigForTest(c)
+	conf := defaultConfigForTest(t)
 	resultOk := sqlmock.NewResult(0, 1)
 
 	conf.Consistency = consistencyTypeNone
 	ctrl, _ := NewConsistencyController(ctx, conf, db)
 	_, ok := ctrl.(*ConsistencyNone)
-	c.Assert(ok, IsTrue)
-	s.assertLifetimeErrNil(tctx, ctrl, c)
+	require.True(t, ok)
+	require.NoError(t, ctrl.Setup(tctx))
+	require.NoError(t, ctrl.TearDown(tctx))
 
 	conf.Consistency = consistencyTypeFlush
 	mock.ExpectExec("FLUSH TABLES WITH READ LOCK").WillReturnResult(resultOk)
 	mock.ExpectExec("UNLOCK TABLES").WillReturnResult(resultOk)
 	ctrl, _ = NewConsistencyController(ctx, conf, db)
 	_, ok = ctrl.(*ConsistencyFlushTableWithReadLock)
-	c.Assert(ok, IsTrue)
-	s.assertLifetimeErrNil(tctx, ctrl, c)
-	if err = mock.ExpectationsWereMet(); err != nil {
-		c.Fatal(err.Error())
-	}
+	require.True(t, ok)
+	require.NoError(t, ctrl.Setup(tctx))
+	require.NoError(t, ctrl.TearDown(tctx))
+	require.NoError(t, mock.ExpectationsWereMet())
 
 	conf.Consistency = consistencyTypeSnapshot
 	conf.ServerInfo.ServerType = ServerTypeTiDB
 	ctrl, _ = NewConsistencyController(ctx, conf, db)
 	_, ok = ctrl.(*ConsistencyNone)
-	c.Assert(ok, IsTrue)
-	s.assertLifetimeErrNil(tctx, ctrl, c)
+	require.True(t, ok)
+	require.NoError(t, ctrl.Setup(tctx))
+	require.NoError(t, ctrl.TearDown(tctx))
 
 	conf.Consistency = consistencyTypeLock
 	conf.Tables = NewDatabaseTables().
@@ -71,19 +63,25 @@ func (s *testConsistencySuite) TestConsistencyController(c *C) {
 	mock.ExpectExec("UNLOCK TABLES").WillReturnResult(resultOk)
 	ctrl, _ = NewConsistencyController(ctx, conf, db)
 	_, ok = ctrl.(*ConsistencyLockDumpingTables)
-	c.Assert(ok, IsTrue)
-	s.assertLifetimeErrNil(tctx, ctrl, c)
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.True(t, ok)
+	require.NoError(t, ctrl.Setup(tctx))
+	require.NoError(t, ctrl.TearDown(tctx))
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testConsistencySuite) TestConsistencyLockControllerRetry(c *C) {
+func TestConsistencyLockControllerRetry(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		_ = db.Close()
+	}()
+
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 	tctx := tcontext.Background().WithContext(ctx)
-	conf := defaultConfigForTest(c)
+	conf := defaultConfigForTest(t)
 	resultOk := sqlmock.NewResult(0, 1)
 
 	conf.Consistency = consistencyTypeLock
@@ -96,18 +94,22 @@ func (s *testConsistencySuite) TestConsistencyLockControllerRetry(c *C) {
 	mock.ExpectExec("UNLOCK TABLES").WillReturnResult(resultOk)
 	ctrl, _ := NewConsistencyController(ctx, conf, db)
 	_, ok := ctrl.(*ConsistencyLockDumpingTables)
-	c.Assert(ok, IsTrue)
-	s.assertLifetimeErrNil(tctx, ctrl, c)
+	require.True(t, ok)
+	require.NoError(t, ctrl.Setup(tctx))
+	require.NoError(t, ctrl.TearDown(tctx))
+
 	// should remove table db1.t3 in tables to dump
 	expectedDumpTables := NewDatabaseTables().
 		AppendTables("db1", []string{"t1", "t2"}, []uint64{1, 2}).
 		AppendViews("db2", "t4")
-	c.Assert(conf.Tables, DeepEquals, expectedDumpTables)
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.Equal(t, expectedDumpTables, conf.Tables)
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testConsistencySuite) TestResolveAutoConsistency(c *C) {
-	conf := defaultConfigForTest(c)
+func TestResolveAutoConsistency(t *testing.T) {
+	t.Parallel()
+
+	conf := defaultConfigForTest(t)
 	cases := []struct {
 		serverTp            ServerType
 		resolvedConsistency string
@@ -122,38 +124,42 @@ func (s *testConsistencySuite) TestResolveAutoConsistency(c *C) {
 		conf.Consistency = consistencyTypeAuto
 		conf.ServerInfo.ServerType = x.serverTp
 		d := &Dumper{conf: conf}
-		c.Assert(resolveAutoConsistency(d), IsNil)
-		cmt := Commentf("server type %s", x.serverTp.String())
-		c.Assert(conf.Consistency, Equals, x.resolvedConsistency, cmt)
+		require.NoError(t, resolveAutoConsistency(d))
+		require.Equalf(t, x.resolvedConsistency, conf.Consistency, "server type: %s", x.serverTp.String())
 	}
 }
 
-func (s *testConsistencySuite) TestConsistencyControllerError(c *C) {
+func TestConsistencyControllerError(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 	tctx := tcontext.Background().WithContext(ctx)
-	conf := defaultConfigForTest(c)
+	conf := defaultConfigForTest(t)
 
 	conf.Consistency = "invalid_str"
 	_, err = NewConsistencyController(ctx, conf, db)
-	c.Assert(err, NotNil)
-	c.Assert(strings.Contains(err.Error(), "invalid consistency option"), IsTrue)
+	require.Error(t, err)
+	require.Contains(t, err.Error(), "invalid consistency option")
 
 	// snapshot consistency is only available in TiDB
 	conf.Consistency = consistencyTypeSnapshot
 	conf.ServerInfo.ServerType = ServerTypeUnknown
 	_, err = NewConsistencyController(ctx, conf, db)
-	c.Assert(err, NotNil)
+	require.Error(t, err)
 
 	// flush consistency is unavailable in TiDB
 	conf.Consistency = consistencyTypeFlush
 	conf.ServerInfo.ServerType = ServerTypeTiDB
 	ctrl, _ := NewConsistencyController(ctx, conf, db)
 	err = ctrl.Setup(tctx)
-	c.Assert(err, NotNil)
+	require.Error(t, err)
 
 	// lock table fail
 	conf.Consistency = consistencyTypeLock
@@ -161,5 +167,5 @@ func (s *testConsistencySuite) TestConsistencyControllerError(c *C) {
 	mock.ExpectExec("LOCK TABLE").WillReturnError(errors.New(""))
 	ctrl, _ = NewConsistencyController(ctx, conf, db)
 	err = ctrl.Setup(tctx)
-	c.Assert(err, NotNil)
+	require.Error(t, err)
 }
diff --git a/v4/export/dump_test.go b/v4/export/dump_test.go
index ae584f1..a461ee3 100644
--- a/v4/export/dump_test.go
+++ b/v4/export/dump_test.go
@@ -5,20 +5,25 @@ package export
 import (
 	"context"
 	"fmt"
+	"testing"
 	"time"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/stretchr/testify/require"
 
 	"github.com/DATA-DOG/go-sqlmock"
-	. "github.com/pingcap/check"
 	"github.com/pingcap/errors"
 	"golang.org/x/sync/errgroup"
 )
 
-func (s *testSQLSuite) TestDumpBlock(c *C) {
+func TestDumpBlock(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
 
 	mock.ExpectQuery(fmt.Sprintf("SHOW CREATE DATABASE `%s`", escapeString(database))).
 		WillReturnRows(sqlmock.NewRows([]string{"Database", "Create Database"}).
@@ -27,7 +32,7 @@ func (s *testSQLSuite) TestDumpBlock(c *C) {
 	tctx, cancel := tcontext.Background().WithLogger(appLogger).WithCancel()
 	defer cancel()
 	conn, err := db.Conn(tctx)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	d := &Dumper{
 		tctx:      tctx,
@@ -49,19 +54,24 @@ func (s *testSQLSuite) TestDumpBlock(c *C) {
 	taskChan := make(chan Task, 1)
 	taskChan <- &TaskDatabaseMeta{}
 	d.conf.Tables = DatabaseTables{}.AppendTable(database, nil)
-	c.Assert(errors.ErrorEqual(d.dumpDatabases(writerCtx, conn, taskChan), context.Canceled), IsTrue)
-	c.Assert(errors.ErrorEqual(wg.Wait(), writerErr), IsTrue)
+	require.ErrorIs(t, d.dumpDatabases(writerCtx, conn, taskChan), context.Canceled)
+	require.ErrorIs(t, wg.Wait(), writerErr)
 }
 
-func (s *testSQLSuite) TestDumpTableMeta(c *C) {
+func TestDumpTableMeta(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
 
 	tctx, cancel := tcontext.Background().WithLogger(appLogger).WithCancel()
 	defer cancel()
 	conn, err := db.Conn(tctx)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	conf := DefaultConfig()
 	conf.NoSchemas = true
 
@@ -79,12 +89,41 @@ func (s *testSQLSuite) TestDumpTableMeta(c *C) {
 		mock.ExpectQuery(fmt.Sprintf("SELECT \\* FROM `%s`.`%s`", database, table)).
 			WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
 		meta, err := dumpTableMeta(conf, conn, database, &TableInfo{Type: TableTypeBase, Name: table})
-		c.Assert(err, IsNil)
-		c.Assert(meta.DatabaseName(), Equals, database)
-		c.Assert(meta.TableName(), Equals, table)
-		c.Assert(meta.SelectedField(), Equals, "*")
-		c.Assert(meta.SelectedLen(), Equals, 1)
-		c.Assert(meta.ShowCreateTable(), Equals, "")
-		c.Assert(meta.HasImplicitRowID(), Equals, hasImplicitRowID)
+		require.NoError(t, err)
+		require.Equal(t, database, meta.DatabaseName())
+		require.Equal(t, table, meta.TableName())
+		require.Equal(t, "*", meta.SelectedField())
+		require.Equal(t, 1, meta.SelectedLen())
+		require.Equal(t, "", meta.ShowCreateTable())
+		require.Equal(t, hasImplicitRowID, meta.HasImplicitRowID())
+	}
+}
+
+func TestGetListTableTypeByConf(t *testing.T) {
+	t.Parallel()
+
+	conf := defaultConfigForTest(t)
+	tctx := tcontext.Background().WithLogger(appLogger)
+	cases := []struct {
+		serverInfo  ServerInfo
+		consistency string
+		expected    listTableType
+	}{
+		{ParseServerInfo(tctx, "5.7.25-TiDB-3.0.6"), consistencyTypeSnapshot, listTableByShowTableStatus},
+		// no bug version
+		{ParseServerInfo(tctx, "8.0.2"), consistencyTypeLock, listTableByInfoSchema},
+		{ParseServerInfo(tctx, "8.0.2"), consistencyTypeFlush, listTableByShowTableStatus},
+		{ParseServerInfo(tctx, "8.0.23"), consistencyTypeNone, listTableByShowTableStatus},
+
+		// bug version
+		{ParseServerInfo(tctx, "8.0.3"), consistencyTypeLock, listTableByInfoSchema},
+		{ParseServerInfo(tctx, "8.0.3"), consistencyTypeFlush, listTableByShowFullTables},
+		{ParseServerInfo(tctx, "8.0.3"), consistencyTypeNone, listTableByShowTableStatus},
+	}
+
+	for _, x := range cases {
+		conf.Consistency = x.consistency
+		conf.ServerInfo = x.serverInfo
+		require.Equalf(t, x.expected, getListTableTypeByConf(conf), "server info: %s, consistency: %s", x.serverInfo, x.consistency)
 	}
 }
diff --git a/v4/export/ir_impl_test.go b/v4/export/ir_impl_test.go
index 8241078..97a5a47 100644
--- a/v4/export/ir_impl_test.go
+++ b/v4/export/ir_impl_test.go
@@ -4,15 +4,12 @@ package export
 
 import (
 	"strings"
+	"testing"
 
 	"github.com/DATA-DOG/go-sqlmock"
-	. "github.com/pingcap/check"
+	"github.com/stretchr/testify/require"
 )
 
-var _ = Suite(&testIRImplSuite{})
-
-type testIRImplSuite struct{}
-
 type simpleRowReceiver struct {
 	data []string
 }
@@ -27,10 +24,14 @@ func (s *simpleRowReceiver) BindAddress(args []interface{}) {
 	}
 }
 
-func (s *testIRImplSuite) TestRowIter(c *C) {
+func TestRowIter(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		_ = db.Close()
+	}()
 
 	expectedRows := mock.NewRows([]string{"id"}).
 		AddRow("1").
@@ -38,32 +39,40 @@ func (s *testIRImplSuite) TestRowIter(c *C) {
 		AddRow("3")
 	mock.ExpectQuery("SELECT id from t").WillReturnRows(expectedRows)
 	rows, err := db.Query("SELECT id from t")
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	iter := newRowIter(rows, 1)
 	for i := 0; i < 100; i++ {
-		c.Assert(iter.HasNext(), IsTrue)
+		require.True(t, iter.HasNext())
 	}
+
 	res := newSimpleRowReceiver(1)
-	c.Assert(iter.Decode(res), IsNil)
-	c.Assert(res.data, DeepEquals, []string{"1"})
+	require.NoError(t, iter.Decode(res))
+	require.Equal(t, []string{"1"}, res.data)
+
 	iter.Next()
-	c.Assert(iter.HasNext(), IsTrue)
-	c.Assert(iter.HasNext(), IsTrue)
-	c.Assert(iter.Decode(res), IsNil)
-	c.Assert(res.data, DeepEquals, []string{"2"})
+	require.True(t, iter.HasNext())
+	require.True(t, iter.HasNext())
+	require.NoError(t, iter.Decode(res))
+	require.Equal(t, []string{"2"}, res.data)
+
 	iter.Next()
-	c.Assert(iter.HasNext(), IsTrue)
-	c.Assert(iter.Decode(res), IsNil)
+	require.True(t, iter.HasNext())
+	require.NoError(t, iter.Decode(res))
+
 	iter.Next()
-	c.Assert(res.data, DeepEquals, []string{"3"})
-	c.Assert(iter.HasNext(), IsFalse)
+	require.Equal(t, []string{"3"}, res.data)
+	require.False(t, iter.HasNext())
 }
 
-func (s *testIRImplSuite) TestChunkRowIter(c *C) {
+func TestChunkRowIter(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		_ = db.Close()
+	}()
 
 	twentyBytes := strings.Repeat("x", 20)
 	thirtyBytes := strings.Repeat("x", 30)
@@ -73,8 +82,10 @@ func (s *testIRImplSuite) TestChunkRowIter(c *C) {
 	}
 	mock.ExpectQuery("SELECT a, b FROM t").WillReturnRows(expectedRows)
 	rows, err := db.Query("SELECT a, b FROM t")
-	c.Assert(err, IsNil)
-	defer rows.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, rows.Close())
+	}()
 
 	var (
 		testFileSize      uint64 = 200
@@ -97,7 +108,7 @@ func (s *testIRImplSuite) TestChunkRowIter(c *C) {
 	for sqlRowIter.HasNext() {
 		wp.currentStatementSize = 0
 		for sqlRowIter.HasNext() {
-			c.Assert(sqlRowIter.Decode(res), IsNil)
+			require.NoError(t, sqlRowIter.Decode(res))
 			sz := uint64(len(res.data[0]) + len(res.data[1]))
 			wp.AddFileSize(sz)
 			sqlRowIter.Next()
@@ -111,11 +122,11 @@ func (s *testIRImplSuite) TestChunkRowIter(c *C) {
 		}
 	}
 
-	c.Assert(resSize, DeepEquals, expectedSize)
-	c.Assert(sqlRowIter.HasNext(), IsTrue)
-	c.Assert(wp.ShouldSwitchFile(), IsTrue)
-	c.Assert(wp.ShouldSwitchStatement(), IsTrue)
-	rows.Close()
-	c.Assert(sqlRowIter.Decode(res), NotNil)
+	require.Equal(t, expectedSize, resSize)
+	require.True(t, sqlRowIter.HasNext())
+	require.True(t, wp.ShouldSwitchFile())
+	require.True(t, wp.ShouldSwitchStatement())
+	require.NoError(t, rows.Close())
+	require.Error(t, sqlRowIter.Decode(res))
 	sqlRowIter.Next()
 }
diff --git a/v4/export/main_test.go b/v4/export/main_test.go
new file mode 100644
index 0000000..b312b93
--- /dev/null
+++ b/v4/export/main_test.go
@@ -0,0 +1,61 @@
+// Copyright 2021 PingCAP, Inc.
+//
+// Licensed 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 export
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/pingcap/dumpling/v4/log"
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/stretchr/testify/require"
+	"go.uber.org/goleak"
+)
+
+var appLogger log.Logger
+
+func TestMain(m *testing.M) {
+	initColTypeRowReceiverMap()
+
+	logger, _, err := log.InitAppLogger(&log.Config{
+		Level:  "debug",
+		File:   "",
+		Format: "text",
+	})
+
+	if err != nil {
+		_, _ = fmt.Fprintf(os.Stderr, "fail to init logger: %v\n", err)
+		os.Exit(1)
+	}
+
+	appLogger = logger
+	registry := prometheus.NewRegistry()
+	registry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
+	registry.MustRegister(prometheus.NewGoCollector())
+	RegisterMetrics(registry)
+
+	opts := []goleak.Option{
+		goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"),
+	}
+
+	goleak.VerifyTestMain(m, opts...)
+}
+
+func defaultConfigForTest(t *testing.T) *Config {
+	config := DefaultConfig()
+	require.NoError(t, adjustFileFormat(config))
+	return config
+}
diff --git a/v4/export/metadata_test.go b/v4/export/metadata_test.go
index 0974b9c..5e764b4 100644
--- a/v4/export/metadata_test.go
+++ b/v4/export/metadata_test.go
@@ -6,30 +6,33 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"os"
+	"testing"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/stretchr/testify/require"
 
 	"github.com/DATA-DOG/go-sqlmock"
 	"github.com/pingcap/br/pkg/storage"
-	. "github.com/pingcap/check"
 )
 
-var _ = Suite(&testMetaDataSuite{})
-
 const (
 	logFile = "ON.000001"
 	pos     = "7502"
 	gtidSet = "6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29"
 )
 
-type testMetaDataSuite struct{}
+func TestMysqlMetaData(t *testing.T) {
+	t.Parallel()
 
-func (s *testMetaDataSuite) TestMysqlMetaData(c *C) {
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	rows := sqlmock.NewRows([]string{"File", "Position", "Binlog_Do_DB", "Binlog_Ignore_DB", "Executed_Gtid_Set"}).
 		AddRow(logFile, pos, "", "", gtidSet)
@@ -38,29 +41,28 @@ func (s *testMetaDataSuite) TestMysqlMetaData(c *C) {
 	mock.ExpectQuery("SHOW SLAVE STATUS").WillReturnRows(
 		sqlmock.NewRows([]string{"exec_master_log_pos", "relay_master_log_file", "master_host", "Executed_Gtid_Set", "Seconds_Behind_Master"}))
 
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeMySQL, false), IsNil)
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeMySQL, false))
 
-	c.Assert(m.buffer.String(), Equals, "SHOW MASTER STATUS:\n"+
-		"\tLog: ON.000001\n"+
-		"\tPos: 7502\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n")
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	expected := "SHOW MASTER STATUS:\n" +
+		"\tLog: ON.000001\n" +
+		"\tPos: 7502\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"
+	require.Equal(t, expected, m.buffer.String())
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testMetaDataSuite) createStorage(c *C) storage.ExternalStorage {
-	backend, err := storage.ParseBackend("file:///"+c.MkDir(), nil)
-	c.Assert(err, IsNil)
-	testLoc, _ := storage.Create(context.Background(), backend, true)
-	return testLoc
-}
+func TestMetaDataAfterConn(t *testing.T) {
+	t.Parallel()
 
-func (s *testMetaDataSuite) TestMetaDataAfterConn(c *C) {
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	rows := sqlmock.NewRows([]string{"File", "Position", "Binlog_Do_DB", "Binlog_Ignore_DB", "Executed_Gtid_Set"}).
 		AddRow(logFile, pos, "", "", gtidSet)
@@ -73,28 +75,35 @@ func (s *testMetaDataSuite) TestMetaDataAfterConn(c *C) {
 		sqlmock.NewRows([]string{"exec_master_log_pos", "relay_master_log_file", "master_host", "Executed_Gtid_Set", "Seconds_Behind_Master"}))
 	mock.ExpectQuery("SHOW MASTER STATUS").WillReturnRows(rows2)
 
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeMySQL, false), IsNil)
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeMySQL, true), IsNil)
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeMySQL, false))
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeMySQL, true))
+
 	m.buffer.Write(m.afterConnBuffer.Bytes())
 
-	c.Assert(m.buffer.String(), Equals, "SHOW MASTER STATUS:\n"+
-		"\tLog: ON.000001\n"+
-		"\tPos: 7502\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"+
-		"SHOW MASTER STATUS: /* AFTER CONNECTION POOL ESTABLISHED */\n"+
-		"\tLog: ON.000001\n"+
-		"\tPos: 7510\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n")
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	expected := "SHOW MASTER STATUS:\n" +
+		"\tLog: ON.000001\n" +
+		"\tPos: 7502\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n" +
+		"SHOW MASTER STATUS: /* AFTER CONNECTION POOL ESTABLISHED */\n" +
+		"\tLog: ON.000001\n" +
+		"\tPos: 7510\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"
+	require.Equal(t, expected, m.buffer.String())
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testMetaDataSuite) TestMysqlWithFollowersMetaData(c *C) {
+func TestMysqlWithFollowersMetaData(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	rows := sqlmock.NewRows([]string{"File", "Position", "Binlog_Do_DB", "Binlog_Ignore_DB", "Executed_Gtid_Set"}).
 		AddRow(logFile, pos, "", "", gtidSet)
@@ -104,27 +113,33 @@ func (s *testMetaDataSuite) TestMysqlWithFollowersMetaData(c *C) {
 	mock.ExpectQuery("SELECT @@default_master_connection").WillReturnError(fmt.Errorf("mock error"))
 	mock.ExpectQuery("SHOW SLAVE STATUS").WillReturnRows(followerRows)
 
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeMySQL, false), IsNil)
-
-	c.Assert(m.buffer.String(), Equals, "SHOW MASTER STATUS:\n"+
-		"\tLog: ON.000001\n"+
-		"\tPos: 7502\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"+
-		"SHOW SLAVE STATUS:\n"+
-		"\tHost: 192.168.1.100\n"+
-		"\tLog: mysql-bin.001821\n"+
-		"\tPos: 256529431\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n")
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeMySQL, false))
+
+	expected := "SHOW MASTER STATUS:\n" +
+		"\tLog: ON.000001\n" +
+		"\tPos: 7502\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n" +
+		"SHOW SLAVE STATUS:\n" +
+		"\tHost: 192.168.1.100\n" +
+		"\tLog: mysql-bin.001821\n" +
+		"\tPos: 256529431\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"
+	require.Equal(t, expected, m.buffer.String())
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testMetaDataSuite) TestMysqlWithNullFollowersMetaData(c *C) {
+func TestMysqlWithNullFollowersMetaData(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	rows := sqlmock.NewRows([]string{"File", "Position", "Binlog_Do_DB", "Binlog_Ignore_DB", "Executed_Gtid_Set"}).
 		AddRow(logFile, pos, "", "", gtidSet)
@@ -132,22 +147,28 @@ func (s *testMetaDataSuite) TestMysqlWithNullFollowersMetaData(c *C) {
 	mock.ExpectQuery("SELECT @@default_master_connection").WillReturnError(fmt.Errorf("mock error"))
 	mock.ExpectQuery("SHOW SLAVE STATUS").WillReturnRows(sqlmock.NewRows([]string{"SQL_Remaining_Delay"}).AddRow(nil))
 
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeMySQL, false), IsNil)
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeMySQL, false))
 
-	c.Assert(m.buffer.String(), Equals, "SHOW MASTER STATUS:\n"+
-		"\tLog: ON.000001\n"+
-		"\tPos: 7502\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n")
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	expected := "SHOW MASTER STATUS:\n" +
+		"\tLog: ON.000001\n" +
+		"\tPos: 7502\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"
+	require.Equal(t, expected, m.buffer.String())
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testMetaDataSuite) TestMariaDBMetaData(c *C) {
+func TestMariaDBMetaData(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	logFile := "mariadb-bin.000016"
 	pos := "475"
@@ -159,18 +180,22 @@ func (s *testMetaDataSuite) TestMariaDBMetaData(c *C) {
 		AddRow(gtidSet)
 	mock.ExpectQuery("SELECT @@global.gtid_binlog_pos").WillReturnRows(rows)
 	mock.ExpectQuery("SHOW SLAVE STATUS").WillReturnRows(rows)
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeMariaDB, false), IsNil)
-
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeMariaDB, false))
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testMetaDataSuite) TestMariaDBWithFollowersMetaData(c *C) {
+func TestMariaDBWithFollowersMetaData(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	rows := sqlmock.NewRows([]string{"File", "Position", "Binlog_Do_DB", "Binlog_Ignore_DB", "Executed_Gtid_Set"}).
 		AddRow(logFile, pos, "", "", gtidSet)
@@ -183,34 +208,40 @@ func (s *testMetaDataSuite) TestMariaDBWithFollowersMetaData(c *C) {
 			AddRow("connection_1"))
 	mock.ExpectQuery("SHOW ALL SLAVES STATUS").WillReturnRows(followerRows)
 
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeMySQL, false), IsNil)
-
-	c.Assert(m.buffer.String(), Equals, "SHOW MASTER STATUS:\n"+
-		"\tLog: ON.000001\n"+
-		"\tPos: 7502\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"+
-		"SHOW SLAVE STATUS:\n"+
-		"\tConnection name: connection_1\n"+
-		"\tHost: 192.168.1.100\n"+
-		"\tLog: mysql-bin.001821\n"+
-		"\tPos: 256529431\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"+
-		"SHOW SLAVE STATUS:\n"+
-		"\tConnection name: connection_2\n"+
-		"\tHost: 192.168.1.102\n"+
-		"\tLog: mysql-bin.001820\n"+
-		"\tPos: 256529451\n"+
-		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n")
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeMySQL, false))
+
+	expected := "SHOW MASTER STATUS:\n" +
+		"\tLog: ON.000001\n" +
+		"\tPos: 7502\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n" +
+		"SHOW SLAVE STATUS:\n" +
+		"\tConnection name: connection_1\n" +
+		"\tHost: 192.168.1.100\n" +
+		"\tLog: mysql-bin.001821\n" +
+		"\tPos: 256529431\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n" +
+		"SHOW SLAVE STATUS:\n" +
+		"\tConnection name: connection_2\n" +
+		"\tHost: 192.168.1.102\n" +
+		"\tLog: mysql-bin.001820\n" +
+		"\tPos: 256529451\n" +
+		"\tGTID:6ce40be3-e359-11e9-87e0-36933cb0ca5a:1-29\n\n"
+	require.Equal(t, expected, m.buffer.String())
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testMetaDataSuite) TestEarlierMysqlMetaData(c *C) {
+func TestEarlierMysqlMetaData(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	logFile := "mysql-bin.000001"
 	pos := "4879"
@@ -221,22 +252,28 @@ func (s *testMetaDataSuite) TestEarlierMysqlMetaData(c *C) {
 	mock.ExpectQuery("SHOW SLAVE STATUS").WillReturnRows(
 		sqlmock.NewRows([]string{"exec_master_log_pos", "relay_master_log_file", "master_host", "Executed_Gtid_Set", "Seconds_Behind_Master"}))
 
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeMySQL, false), IsNil)
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeMySQL, false))
 
-	c.Assert(m.buffer.String(), Equals, "SHOW MASTER STATUS:\n"+
-		"\tLog: mysql-bin.000001\n"+
-		"\tPos: 4879\n"+
-		"\tGTID:\n\n")
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	expected := "SHOW MASTER STATUS:\n" +
+		"\tLog: mysql-bin.000001\n" +
+		"\tPos: 4879\n" +
+		"\tGTID:\n\n"
+	require.Equal(t, expected, m.buffer.String())
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testMetaDataSuite) TestTiDBSnapshotMetaData(c *C) {
+func TestTiDBSnapshotMetaData(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	logFile := "tidb-binlog"
 	pos := "420633329401856001"
@@ -244,37 +281,53 @@ func (s *testMetaDataSuite) TestTiDBSnapshotMetaData(c *C) {
 		AddRow(logFile, pos, "", "")
 	mock.ExpectQuery("SHOW MASTER STATUS").WillReturnRows(rows)
 
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeTiDB, false), IsNil)
-	c.Assert(m.buffer.String(), Equals, "SHOW MASTER STATUS:\n"+
-		"\tLog: tidb-binlog\n"+
-		"\tPos: 420633329401856001\n"+
-		"\tGTID:\n\n")
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeTiDB, false))
+
+	expected := "SHOW MASTER STATUS:\n" +
+		"\tLog: tidb-binlog\n" +
+		"\tPos: 420633329401856001\n" +
+		"\tGTID:\n\n"
+	require.Equal(t, expected, m.buffer.String())
 
 	snapshot := "420633273211289601"
 	rows = sqlmock.NewRows([]string{"File", "Position", "Binlog_Do_DB", "Binlog_Ignore_DB"}).
 		AddRow(logFile, pos, "", "")
 	mock.ExpectQuery("SHOW MASTER STATUS").WillReturnRows(rows)
-	m = newGlobalMetadata(tcontext.Background(), s.createStorage(c), snapshot)
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeTiDB, false), IsNil)
-	c.Assert(m.buffer.String(), Equals, "SHOW MASTER STATUS:\n"+
-		"\tLog: tidb-binlog\n"+
-		"\tPos: 420633273211289601\n"+
-		"\tGTID:\n\n")
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	m = newGlobalMetadata(tcontext.Background(), createStorage(t), snapshot)
+	require.NoError(t, m.recordGlobalMetaData(conn, ServerTypeTiDB, false))
+
+	expected = "SHOW MASTER STATUS:\n" +
+		"\tLog: tidb-binlog\n" +
+		"\tPos: 420633273211289601\n" +
+		"\tGTID:\n\n"
+	require.Equal(t, expected, m.buffer.String())
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testMetaDataSuite) TestNoPrivilege(c *C) {
+func TestNoPrivilege(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	mock.ExpectQuery("SHOW MASTER STATUS").WillReturnError(errors.New("lack SUPER or REPLICATION CLIENT privilege"))
 
-	m := newGlobalMetadata(tcontext.Background(), s.createStorage(c), "")
+	m := newGlobalMetadata(tcontext.Background(), createStorage(t), "")
 	// some consistencyType will ignore this error, this test make sure no extra message is written
-	c.Assert(m.recordGlobalMetaData(conn, ServerTypeTiDB, false), NotNil)
-	c.Assert(m.buffer.String(), Equals, "")
+	require.Error(t, m.recordGlobalMetaData(conn, ServerTypeTiDB, false))
+	require.Equal(t, "", m.buffer.String())
+}
+
+func createStorage(t *testing.T) storage.ExternalStorage {
+	backend, err := storage.ParseBackend("file:///"+os.TempDir(), nil)
+	require.NoError(t, err)
+	testLoc, _ := storage.Create(context.Background(), backend, true)
+	return testLoc
 }
diff --git a/v4/export/prepare_test.go b/v4/export/prepare_test.go
index acf0691..bfe3fa9 100644
--- a/v4/export/prepare_test.go
+++ b/v4/export/prepare_test.go
@@ -5,23 +5,27 @@ package export
 import (
 	"context"
 	"fmt"
+	"strings"
+	"testing"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/stretchr/testify/require"
 
 	"github.com/DATA-DOG/go-sqlmock"
-	. "github.com/pingcap/check"
 )
 
-var _ = Suite(&testPrepareSuite{})
+func TestPrepareDumpingDatabases(t *testing.T) {
+	t.Parallel()
 
-type testPrepareSuite struct{}
-
-func (s *testPrepareSuite) TestPrepareDumpingDatabases(c *C) {
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
-	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
+	tctx := tcontext.Background().WithLogger(appLogger)
+	conn, err := db.Conn(tctx)
+	require.NoError(t, err)
 
 	rows := sqlmock.NewRows([]string{"Database"}).
 		AddRow("db1").
@@ -29,24 +33,24 @@ func (s *testPrepareSuite) TestPrepareDumpingDatabases(c *C) {
 		AddRow("db3").
 		AddRow("db5")
 	mock.ExpectQuery("SHOW DATABASES").WillReturnRows(rows)
-	conf := defaultConfigForTest(c)
+	conf := defaultConfigForTest(t)
 	conf.Databases = []string{"db1", "db2", "db3"}
-	result, err := prepareDumpingDatabases(conf, conn)
-	c.Assert(err, IsNil)
-	c.Assert(result, DeepEquals, []string{"db1", "db2", "db3"})
+	result, err := prepareDumpingDatabases(tctx, conf, conn)
+	require.NoError(t, err)
+	require.Equal(t, []string{"db1", "db2", "db3"}, result)
 
 	conf.Databases = nil
 	rows = sqlmock.NewRows([]string{"Database"}).
 		AddRow("db1").
 		AddRow("db2")
 	mock.ExpectQuery("SHOW DATABASES").WillReturnRows(rows)
-	result, err = prepareDumpingDatabases(conf, conn)
-	c.Assert(err, IsNil)
-	c.Assert(result, DeepEquals, []string{"db1", "db2"})
+	result, err = prepareDumpingDatabases(tctx, conf, conn)
+	require.NoError(t, err)
+	require.Equal(t, []string{"db1", "db2"}, result)
 
 	mock.ExpectQuery("SHOW DATABASES").WillReturnError(fmt.Errorf("err"))
-	_, err = prepareDumpingDatabases(conf, conn)
-	c.Assert(err, NotNil)
+	_, err = prepareDumpingDatabases(tctx, conf, conn)
+	require.Error(t, err)
 
 	rows = sqlmock.NewRows([]string{"Database"}).
 		AddRow("db1").
@@ -55,17 +59,22 @@ func (s *testPrepareSuite) TestPrepareDumpingDatabases(c *C) {
 		AddRow("db5")
 	mock.ExpectQuery("SHOW DATABASES").WillReturnRows(rows)
 	conf.Databases = []string{"db1", "db2", "db4", "db6"}
-	_, err = prepareDumpingDatabases(conf, conn)
-	c.Assert(err, ErrorMatches, `Unknown databases \[db4,db6\]`)
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	_, err = prepareDumpingDatabases(tctx, conf, conn)
+	require.EqualError(t, err, `Unknown databases [db4,db6]`)
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testPrepareSuite) TestListAllTables(c *C) {
+func TestListAllTables(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	tctx := tcontext.Background().WithLogger(appLogger)
 
 	// Test list all tables and skipping views.
@@ -89,15 +98,14 @@ func (s *testPrepareSuite) TestListAllTables(c *C) {
 	query := "SELECT TABLE_SCHEMA,TABLE_NAME,TABLE_TYPE,AVG_ROW_LENGTH FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'"
 	mock.ExpectQuery(query).WillReturnRows(rows)
 
-	tables, err := ListAllDatabasesTables(tctx, conn, dbNames, true, TableTypeBase)
-	c.Assert(err, IsNil)
+	tables, err := ListAllDatabasesTables(tctx, conn, dbNames, listTableByInfoSchema, TableTypeBase)
+	require.NoError(t, err)
 
-	for d, t := range tables {
+	for d, table := range tables {
 		expectedTbs, ok := data[d]
-		c.Assert(ok, IsTrue)
-		for i := 0; i < len(t); i++ {
-			cmt := Commentf("%v mismatch: %v", t[i], expectedTbs[i])
-			c.Assert(t[i].Equals(expectedTbs[i]), IsTrue, cmt)
+		require.True(t, ok)
+		for i := 0; i < len(table); i++ {
+			require.Truef(t, table[i].Equals(expectedTbs[i]), "%v mismatches expected: %v", table[i], expectedTbs[i])
 		}
 	}
 
@@ -108,24 +116,30 @@ func (s *testPrepareSuite) TestListAllTables(c *C) {
 	query = "SELECT TABLE_SCHEMA,TABLE_NAME,TABLE_TYPE,AVG_ROW_LENGTH FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' OR TABLE_TYPE='VIEW'"
 	mock.ExpectQuery(query).WillReturnRows(sqlmock.NewRows([]string{"TABLE_SCHEMA", "TABLE_NAME", "TABLE_TYPE", "AVG_ROW_LENGTH"}).
 		AddRow("db", "t1", TableTypeBaseStr, 1).AddRow("db", "t2", TableTypeViewStr, nil))
-	tables, err = ListAllDatabasesTables(tctx, conn, []string{"db"}, true, TableTypeBase, TableTypeView)
-	c.Assert(err, IsNil)
-	c.Assert(len(tables), Equals, 1)
-	c.Assert(len(tables["db"]), Equals, 2)
+	tables, err = ListAllDatabasesTables(tctx, conn, []string{"db"}, listTableByInfoSchema, TableTypeBase, TableTypeView)
+	require.NoError(t, err)
+	require.Len(t, tables, 1)
+	require.Len(t, tables["db"], 2)
+
 	for i := 0; i < len(tables["db"]); i++ {
-		cmt := Commentf("%v mismatch: %v", tables["db"][i], data["db"][i])
-		c.Assert(tables["db"][i].Equals(data["db"][i]), IsTrue, cmt)
+		require.Truef(t, tables["db"][i].Equals(data["db"][i]), "%v mismatches expected: %v", tables["db"][i], data["db"][i])
 	}
 
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testPrepareSuite) TestListAllTablesByTableStatus(c *C) {
+func TestListAllTablesByTableStatus(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	tctx := tcontext.Background().WithLogger(appLogger)
 
 	// Test list all tables and skipping views.
@@ -145,21 +159,21 @@ func (s *testPrepareSuite) TestListAllTablesByTableStatus(c *C) {
 			if tbInfo.Type == TableTypeBase {
 				rows.AddRow(tbInfo.Name, "InnoDB", 10, "Dynamic", 0, 0, 16384, 0, 0, 0, nil, "2021-07-08 03:04:07", nil, nil, "latin1_swedish_ci", nil, "", "")
 			} else {
-				rows.AddRow(tbInfo.Name, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, "VIEW")
+				rows.AddRow(tbInfo.Name, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, TableTypeView.String())
 			}
 		}
 		mock.ExpectQuery(fmt.Sprintf(query, dbName)).WillReturnRows(rows)
 	}
 
-	tables, err := ListAllDatabasesTables(tctx, conn, dbNames, false, TableTypeBase)
-	c.Assert(err, IsNil)
+	tables, err := ListAllDatabasesTables(tctx, conn, dbNames, listTableByShowTableStatus, TableTypeBase)
+	require.NoError(t, err)
 
-	for d, t := range tables {
+	for d, table := range tables {
 		expectedTbs, ok := data[d]
-		c.Assert(ok, IsTrue)
-		for i := 0; i < len(t); i++ {
-			cmt := Commentf("%v mismatch: %v", t[i], expectedTbs[i])
-			c.Assert(t[i].Equals(expectedTbs[i]), IsTrue, cmt)
+		require.True(t, ok)
+
+		for i := 0; i < len(table); i++ {
+			require.Truef(t, table[i].Equals(expectedTbs[i]), "%v mismatches expected: %v", table[i], expectedTbs[i])
 		}
 	}
 
@@ -169,41 +183,127 @@ func (s *testPrepareSuite) TestListAllTablesByTableStatus(c *C) {
 		AppendViews("db", "t2")
 	mock.ExpectQuery(fmt.Sprintf(query, "db")).WillReturnRows(sqlmock.NewRows(showTableStatusColumnNames).
 		AddRow("t1", "InnoDB", 10, "Dynamic", 0, 1, 16384, 0, 0, 0, nil, "2021-07-08 03:04:07", nil, nil, "latin1_swedish_ci", nil, "", "").
-		AddRow("t2", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, "VIEW"))
-	tables, err = ListAllDatabasesTables(tctx, conn, []string{"db"}, false, TableTypeBase, TableTypeView)
-	c.Assert(err, IsNil)
-	c.Assert(len(tables), Equals, 1)
-	c.Assert(len(tables["db"]), Equals, 2)
+		AddRow("t2", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, TableTypeView.String()))
+	tables, err = ListAllDatabasesTables(tctx, conn, []string{"db"}, listTableByShowTableStatus, TableTypeBase, TableTypeView)
+	require.NoError(t, err)
+	require.Len(t, tables, 1)
+	require.Len(t, tables["db"], 2)
+
+	for i := 0; i < len(tables["db"]); i++ {
+		require.Truef(t, tables["db"][i].Equals(data["db"][i]), "%v mismatches expected: %v", tables["db"][i], data["db"][i])
+	}
+
+	require.NoError(t, mock.ExpectationsWereMet())
+}
+
+func TestListAllTablesByShowFullTables(t *testing.T) {
+	t.Parallel()
+
+	db, mock, err := sqlmock.New()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
+	conn, err := db.Conn(context.Background())
+	require.NoError(t, err)
+
+	tctx := tcontext.Background().WithLogger(appLogger)
+
+	// Test list all tables and skipping views.
+	data := NewDatabaseTables().
+		AppendTables("db1", []string{"t1", "t2"}, []uint64{1, 2}).
+		AppendTables("db2", []string{"t3", "t4", "t5"}, []uint64{3, 4, 5}).
+		AppendViews("db3", "t6", "t7", "t8")
+
+	query := "SHOW FULL TABLES FROM `%s` WHERE TABLE_TYPE='BASE TABLE'"
+	dbNames := make([]databaseName, 0, len(data))
+	for dbName, tableInfos := range data {
+		dbNames = append(dbNames, dbName)
+		columnNames := []string{strings.ToUpper(fmt.Sprintf("Tables_in_%s", dbName)), "TABLE_TYPE"}
+		rows := sqlmock.NewRows(columnNames)
+		for _, tbInfo := range tableInfos {
+			if tbInfo.Type == TableTypeBase {
+				rows.AddRow(tbInfo.Name, TableTypeBase.String())
+			} else {
+				rows.AddRow(tbInfo.Name, TableTypeView.String())
+			}
+		}
+		mock.ExpectQuery(fmt.Sprintf(query, dbName)).WillReturnRows(rows)
+	}
+
+	tables, err := ListAllDatabasesTables(tctx, conn, dbNames, listTableByShowFullTables, TableTypeBase)
+	require.NoError(t, err)
+
+	for d, table := range tables {
+		expectedTbs, ok := data[d]
+		require.True(t, ok)
+
+		for i := 0; i < len(table); i++ {
+			require.Truef(t, table[i].Equals(expectedTbs[i]), "%v mismatches expected: %v", table[i], expectedTbs[i])
+		}
+	}
+
+	// Test list all tables and not skipping views.
+	query = "SHOW FULL TABLES FROM `%s` WHERE TABLE_TYPE='BASE TABLE' OR TABLE_TYPE='VIEW'"
+	data = NewDatabaseTables().
+		AppendTables("db", []string{"t1"}, []uint64{1}).
+		AppendViews("db", "t2")
+	for dbName, tableInfos := range data {
+		columnNames := []string{strings.ToUpper(fmt.Sprintf("Tables_in_%s", dbName)), "TABLE_TYPE"}
+		rows := sqlmock.NewRows(columnNames)
+		for _, tbInfo := range tableInfos {
+			if tbInfo.Type == TableTypeBase {
+				rows.AddRow(tbInfo.Name, TableTypeBase.String())
+			} else {
+				rows.AddRow(tbInfo.Name, TableTypeView.String())
+			}
+		}
+		mock.ExpectQuery(fmt.Sprintf(query, dbName)).WillReturnRows(rows)
+	}
+	tables, err = ListAllDatabasesTables(tctx, conn, []string{"db"}, listTableByShowFullTables, TableTypeBase, TableTypeView)
+	require.NoError(t, err)
+	require.Len(t, tables, 1)
+	require.Len(t, tables["db"], 2)
+
 	for i := 0; i < len(tables["db"]); i++ {
-		cmt := Commentf("%v mismatch: %v", tables["db"][i], data["db"][i])
-		c.Assert(tables["db"][i].Equals(data["db"][i]), IsTrue, cmt)
+		require.Truef(t, tables["db"][i].Equals(data["db"][i]), "%v mismatches expected: %v", tables["db"][i], data["db"][i])
 	}
 
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testPrepareSuite) TestConfigValidation(c *C) {
-	conf := defaultConfigForTest(c)
+func TestConfigValidation(t *testing.T) {
+	t.Parallel()
+
+	conf := defaultConfigForTest(t)
 	conf.Where = "id < 5"
 	conf.SQL = "select * from t where id > 3"
-	c.Assert(validateSpecifiedSQL(conf), ErrorMatches, "can't specify both --sql and --where at the same time. Please try to combine them into --sql")
+	require.EqualError(t, validateSpecifiedSQL(conf), "can't specify both --sql and --where at the same time. Please try to combine them into --sql")
+
 	conf.Where = ""
-	c.Assert(validateSpecifiedSQL(conf), IsNil)
+	require.NoError(t, validateSpecifiedSQL(conf))
 
 	conf.FileType = FileFormatSQLTextString
-	c.Assert(adjustFileFormat(conf), ErrorMatches, ".*please unset --filetype or set it to 'csv'.*")
+	err := adjustFileFormat(conf)
+	require.Error(t, err)
+	require.Regexp(t, ".*please unset --filetype or set it to 'csv'.*", err.Error())
+
 	conf.FileType = FileFormatCSVString
-	c.Assert(adjustFileFormat(conf), IsNil)
+	require.NoError(t, adjustFileFormat(conf))
+
 	conf.FileType = ""
-	c.Assert(adjustFileFormat(conf), IsNil)
-	c.Assert(conf.FileType, Equals, FileFormatCSVString)
+	require.NoError(t, adjustFileFormat(conf))
+	require.Equal(t, FileFormatCSVString, conf.FileType)
+
 	conf.SQL = ""
 	conf.FileType = FileFormatSQLTextString
-	c.Assert(adjustFileFormat(conf), IsNil)
+	require.NoError(t, adjustFileFormat(conf))
+
 	conf.FileType = ""
-	c.Assert(adjustFileFormat(conf), IsNil)
-	c.Assert(conf.FileType, Equals, FileFormatSQLTextString)
+	require.NoError(t, adjustFileFormat(conf))
+	require.Equal(t, FileFormatSQLTextString, conf.FileType)
 
 	conf.FileType = "rand_str"
-	c.Assert(adjustFileFormat(conf), ErrorMatches, "unknown config.FileType 'rand_str'")
+	require.EqualError(t, adjustFileFormat(conf), "unknown config.FileType 'rand_str'")
 }
diff --git a/v4/export/sql_test.go b/v4/export/sql_test.go
index 9de2de5..dac6ff6 100644
--- a/v4/export/sql_test.go
+++ b/v4/export/sql_test.go
@@ -14,31 +14,45 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
+	"testing"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/stretchr/testify/require"
 
 	"github.com/DATA-DOG/go-sqlmock"
 	"github.com/coreos/go-semver/semver"
-	. "github.com/pingcap/check"
 	"github.com/pingcap/errors"
 )
 
-var (
-	_                = Suite(&testSQLSuite{})
-	showIndexHeaders = []string{"Table", "Non_unique", "Key_name", "Seq_in_index", "Column_name", "Collation", "Cardinality", "Sub_part", "Packed", "Null", "Index_type", "Comment", "Index_comment"}
-)
+var showIndexHeaders = []string{
+	"Table",
+	"Non_unique",
+	"Key_name",
+	"Seq_in_index",
+	"Column_name",
+	"Collation",
+	"Cardinality",
+	"Sub_part",
+	"Packed",
+	"Null",
+	"Index_type",
+	"Comment",
+	"Index_comment",
+}
 
 const (
 	database = "foo"
 	table    = "bar"
 )
 
-type testSQLSuite struct{}
+func TestDetectServerInfo(t *testing.T) {
+	t.Parallel()
 
-func (s *testSQLSuite) TestDetectServerInfo(c *C) {
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		_ = db.Close()
+	}()
 
 	mkVer := makeVersion
 	data := [][]interface{}{
@@ -55,66 +69,75 @@ func (s *testSQLSuite) TestDetectServerInfo(c *C) {
 
 	for _, datum := range data {
 		tag, r, serverTp, expectVer := dec(datum)
-		cmt := Commentf("test case number: %d", tag)
-
+		comment := fmt.Sprintf("test case number: %d", tag)
 		rows := sqlmock.NewRows([]string{"version"}).AddRow(r)
 		mock.ExpectQuery("SELECT version()").WillReturnRows(rows)
 
 		verStr, err := SelectVersion(db)
-		c.Assert(err, IsNil, cmt)
+		require.NoError(t, err, comment)
+
 		info := ParseServerInfo(tcontext.Background(), verStr)
-		c.Assert(info.ServerType, Equals, serverTp, cmt)
-		c.Assert(info.ServerVersion == nil, Equals, expectVer == nil, cmt)
+		require.Equal(t, serverTp, info.ServerType, comment)
+		require.Equal(t, expectVer == nil, info.ServerVersion == nil, comment)
+
 		if info.ServerVersion == nil {
-			c.Assert(expectVer, IsNil, cmt)
+			require.Nil(t, expectVer, comment)
 		} else {
-			c.Assert(info.ServerVersion.Equal(*expectVer), IsTrue)
+			require.True(t, info.ServerVersion.Equal(*expectVer), comment)
 		}
-		c.Assert(mock.ExpectationsWereMet(), IsNil, cmt)
+
+		require.NoError(t, mock.ExpectationsWereMet(), comment)
 	}
 }
 
-func (s *testSQLSuite) TestBuildSelectAllQuery(c *C) {
+func TestBuildSelectAllQuery(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
-	mockConf := defaultConfigForTest(c)
+	mockConf := defaultConfigForTest(t)
 	mockConf.SortByPk = true
 
 	// Test TiDB server.
 	mockConf.ServerInfo.ServerType = ServerTypeTiDB
 
 	orderByClause, err := buildOrderByClause(mockConf, conn, database, table, true)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	mock.ExpectQuery("SHOW COLUMNS FROM").
 		WillReturnRows(sqlmock.NewRows([]string{"Field", "Type", "Null", "Key", "Default", "Extra"}).
 			AddRow("id", "int(11)", "NO", "PRI", nil, ""))
 
 	selectedField, _, err := buildSelectField(conn, database, table, false)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	q := buildSelectQuery(database, table, selectedField, "", "", orderByClause)
-	c.Assert(q, Equals, fmt.Sprintf("SELECT * FROM `%s`.`%s` ORDER BY `_tidb_rowid`", database, table))
+	require.Equal(t, fmt.Sprintf("SELECT * FROM `%s`.`%s` ORDER BY `_tidb_rowid`", database, table), q)
 
 	mock.ExpectQuery(fmt.Sprintf("SHOW INDEX FROM `%s`.`%s`", database, table)).
 		WillReturnRows(sqlmock.NewRows(showIndexHeaders).
 			AddRow(table, 0, "PRIMARY", 1, "id", "A", 0, nil, nil, "", "BTREE", "", ""))
 
 	orderByClause, err = buildOrderByClause(mockConf, conn, database, table, false)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	mock.ExpectQuery("SHOW COLUMNS FROM").
 		WillReturnRows(sqlmock.NewRows([]string{"Field", "Type", "Null", "Key", "Default", "Extra"}).
 			AddRow("id", "int(11)", "NO", "PRI", nil, ""))
 
 	selectedField, _, err = buildSelectField(conn, database, table, false)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	q = buildSelectQuery(database, table, selectedField, "", "", orderByClause)
-	c.Assert(q, Equals, fmt.Sprintf("SELECT * FROM `%s`.`%s` ORDER BY `id`", database, table))
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.Equal(t, fmt.Sprintf("SELECT * FROM `%s`.`%s` ORDER BY `id`", database, table), q)
+	require.NoError(t, mock.ExpectationsWereMet())
 
 	// Test other servers.
 	otherServers := []ServerType{ServerTypeUnknown, ServerTypeMySQL, ServerTypeMariaDB}
@@ -122,97 +145,109 @@ func (s *testSQLSuite) TestBuildSelectAllQuery(c *C) {
 	// Test table with primary key.
 	for _, serverTp := range otherServers {
 		mockConf.ServerInfo.ServerType = serverTp
-		cmt := Commentf("server type: %s", serverTp)
+		comment := fmt.Sprintf("server type: %s", serverTp)
+
 		mock.ExpectQuery(fmt.Sprintf("SHOW INDEX FROM `%s`.`%s`", database, table)).
 			WillReturnRows(sqlmock.NewRows(showIndexHeaders).
 				AddRow(table, 0, "PRIMARY", 1, "id", "A", 0, nil, nil, "", "BTREE", "", ""))
 		orderByClause, err := buildOrderByClause(mockConf, conn, database, table, false)
-		c.Assert(err, IsNil, cmt)
+		require.NoError(t, err, comment)
 
 		mock.ExpectQuery("SHOW COLUMNS FROM").
 			WillReturnRows(sqlmock.NewRows([]string{"Field", "Type", "Null", "Key", "Default", "Extra"}).
 				AddRow("id", "int(11)", "NO", "PRI", nil, ""))
 
 		selectedField, _, err = buildSelectField(conn, database, table, false)
-		c.Assert(err, IsNil)
+		require.NoError(t, err, comment)
+
 		q = buildSelectQuery(database, table, selectedField, "", "", orderByClause)
-		c.Assert(q, Equals, fmt.Sprintf("SELECT * FROM `%s`.`%s` ORDER BY `id`", database, table), cmt)
+		require.Equal(t, fmt.Sprintf("SELECT * FROM `%s`.`%s` ORDER BY `id`", database, table), q, comment)
+
 		err = mock.ExpectationsWereMet()
-		c.Assert(err, IsNil, cmt)
-		c.Assert(mock.ExpectationsWereMet(), IsNil, cmt)
+		require.NoError(t, err, comment)
+		require.NoError(t, mock.ExpectationsWereMet(), comment)
 	}
 
 	// Test table without primary key.
 	for _, serverTp := range otherServers {
 		mockConf.ServerInfo.ServerType = serverTp
-		cmt := Commentf("server type: %s", serverTp)
+		comment := fmt.Sprintf("server type: %s", serverTp)
+
 		mock.ExpectQuery(fmt.Sprintf("SHOW INDEX FROM `%s`.`%s`", database, table)).
 			WillReturnRows(sqlmock.NewRows(showIndexHeaders))
 
 		orderByClause, err := buildOrderByClause(mockConf, conn, database, table, false)
-		c.Assert(err, IsNil, cmt)
+		require.NoError(t, err, comment)
 
 		mock.ExpectQuery("SHOW COLUMNS FROM").
 			WillReturnRows(sqlmock.NewRows([]string{"Field", "Type", "Null", "Key", "Default", "Extra"}).
 				AddRow("id", "int(11)", "NO", "PRI", nil, ""))
 
 		selectedField, _, err = buildSelectField(conn, "test", "t", false)
-		c.Assert(err, IsNil)
+		require.NoError(t, err, comment)
+
 		q := buildSelectQuery(database, table, selectedField, "", "", orderByClause)
-		c.Assert(q, Equals, fmt.Sprintf("SELECT * FROM `%s`.`%s`", database, table), cmt)
+		require.Equal(t, fmt.Sprintf("SELECT * FROM `%s`.`%s`", database, table), q, comment)
+
 		err = mock.ExpectationsWereMet()
-		c.Assert(err, IsNil, cmt)
-		c.Assert(mock.ExpectationsWereMet(), IsNil)
+		require.NoError(t, err, comment)
+		require.NoError(t, mock.ExpectationsWereMet(), comment)
 	}
 
 	// Test when config.SortByPk is disabled.
 	mockConf.SortByPk = false
 	for tp := ServerTypeUnknown; tp < ServerTypeAll; tp++ {
 		mockConf.ServerInfo.ServerType = ServerType(tp)
-		cmt := Commentf("current server type: ", tp)
+		comment := fmt.Sprintf("current server type: %v", tp)
 
 		mock.ExpectQuery("SHOW COLUMNS FROM").
 			WillReturnRows(sqlmock.NewRows([]string{"Field", "Type", "Null", "Key", "Default", "Extra"}).
 				AddRow("id", "int(11)", "NO", "PRI", nil, ""))
 
 		selectedField, _, err := buildSelectField(conn, "test", "t", false)
-		c.Assert(err, IsNil)
+		require.NoError(t, err, comment)
+
 		q := buildSelectQuery(database, table, selectedField, "", "", "")
-		c.Assert(q, Equals, fmt.Sprintf("SELECT * FROM `%s`.`%s`", database, table), cmt)
-		c.Assert(mock.ExpectationsWereMet(), IsNil, cmt)
+		require.Equal(t, fmt.Sprintf("SELECT * FROM `%s`.`%s`", database, table), q, comment)
+		require.NoError(t, mock.ExpectationsWereMet(), comment)
 	}
 }
 
-func (s *testSQLSuite) TestBuildOrderByClause(c *C) {
+func TestBuildOrderByClause(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
-	mockConf := defaultConfigForTest(c)
+	mockConf := defaultConfigForTest(t)
 	mockConf.SortByPk = true
 
 	// Test TiDB server.
 	mockConf.ServerInfo.ServerType = ServerTypeTiDB
 
 	orderByClause, err := buildOrderByClause(mockConf, conn, database, table, true)
-	c.Assert(err, IsNil)
-	c.Assert(orderByClause, Equals, orderByTiDBRowID)
+	require.NoError(t, err)
+	require.Equal(t, orderByTiDBRowID, orderByClause)
 
 	mock.ExpectQuery(fmt.Sprintf("SHOW INDEX FROM `%s`.`%s`", database, table)).
 		WillReturnRows(sqlmock.NewRows(showIndexHeaders).AddRow(table, 0, "PRIMARY", 1, "id", "A", 0, nil, nil, "", "BTREE", "", ""))
 
 	orderByClause, err = buildOrderByClause(mockConf, conn, database, table, false)
-	c.Assert(err, IsNil)
-	c.Assert(orderByClause, Equals, "ORDER BY `id`")
+	require.NoError(t, err)
+	require.Equal(t, "ORDER BY `id`", orderByClause)
 
 	// Test table with primary key.
 	mock.ExpectQuery(fmt.Sprintf("SHOW INDEX FROM `%s`.`%s`", database, table)).
 		WillReturnRows(sqlmock.NewRows(showIndexHeaders).AddRow(table, 0, "PRIMARY", 1, "id", "A", 0, nil, nil, "", "BTREE", "", ""))
 	orderByClause, err = buildOrderByClause(mockConf, conn, database, table, false)
-	c.Assert(err, IsNil)
-	c.Assert(orderByClause, Equals, "ORDER BY `id`")
+	require.NoError(t, err)
+	require.Equal(t, "ORDER BY `id`", orderByClause)
 
 	// Test table with joint primary key.
 	mock.ExpectQuery(fmt.Sprintf("SHOW INDEX FROM `%s`.`%s`", database, table)).
@@ -220,34 +255,39 @@ func (s *testSQLSuite) TestBuildOrderByClause(c *C) {
 			AddRow(table, 0, "PRIMARY", 1, "id", "A", 0, nil, nil, "", "BTREE", "", "").
 			AddRow(table, 0, "PRIMARY", 2, "name", "A", 0, nil, nil, "", "BTREE", "", ""))
 	orderByClause, err = buildOrderByClause(mockConf, conn, database, table, false)
-	c.Assert(err, IsNil)
-	c.Assert(orderByClause, Equals, "ORDER BY `id`,`name`")
+	require.NoError(t, err)
+	require.Equal(t, "ORDER BY `id`,`name`", orderByClause)
 
 	// Test table without primary key.
 	mock.ExpectQuery(fmt.Sprintf("SHOW INDEX FROM `%s`.`%s`", database, table)).
 		WillReturnRows(sqlmock.NewRows(showIndexHeaders))
 
 	orderByClause, err = buildOrderByClause(mockConf, conn, database, table, false)
-	c.Assert(err, IsNil)
-	c.Assert(orderByClause, Equals, "")
+	require.NoError(t, err)
+	require.Equal(t, "", orderByClause)
 
 	// Test when config.SortByPk is disabled.
 	mockConf.SortByPk = false
 	for _, hasImplicitRowID := range []bool{false, true} {
-		cmt := Commentf("current hasImplicitRowID: ", hasImplicitRowID)
+		comment := fmt.Sprintf("current hasImplicitRowID: %v", hasImplicitRowID)
 
 		orderByClause, err := buildOrderByClause(mockConf, conn, database, table, hasImplicitRowID)
-		c.Assert(err, IsNil, cmt)
-		c.Assert(orderByClause, Equals, "", cmt)
+		require.NoError(t, err, comment)
+		require.Equal(t, "", orderByClause, comment)
 	}
 }
 
-func (s *testSQLSuite) TestBuildSelectField(c *C) {
+func TestBuildSelectField(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	// generate columns not found
 	mock.ExpectQuery("SHOW COLUMNS FROM").
@@ -255,9 +295,9 @@ func (s *testSQLSuite) TestBuildSelectField(c *C) {
 			AddRow("id", "int(11)", "NO", "PRI", nil, ""))
 
 	selectedField, _, err := buildSelectField(conn, "test", "t", false)
-	c.Assert(selectedField, Equals, "*")
-	c.Assert(err, IsNil)
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.Equal(t, "*", selectedField)
+	require.NoError(t, err)
+	require.NoError(t, mock.ExpectationsWereMet())
 
 	// user assigns completeInsert
 	mock.ExpectQuery("SHOW COLUMNS FROM").
@@ -267,9 +307,9 @@ func (s *testSQLSuite) TestBuildSelectField(c *C) {
 			AddRow("quo`te", "varchar(12)", "NO", "UNI", nil, ""))
 
 	selectedField, _, err = buildSelectField(conn, "test", "t", true)
-	c.Assert(selectedField, Equals, "`id`,`name`,`quo``te`")
-	c.Assert(err, IsNil)
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.Equal(t, "`id`,`name`,`quo``te`", selectedField)
+	require.NoError(t, err)
+	require.NoError(t, mock.ExpectationsWereMet())
 
 	// found generate columns, rest columns is `id`,`name`
 	mock.ExpectQuery("SHOW COLUMNS FROM").
@@ -280,15 +320,19 @@ func (s *testSQLSuite) TestBuildSelectField(c *C) {
 			AddRow("generated", "varchar(12)", "NO", "", nil, "VIRTUAL GENERATED"))
 
 	selectedField, _, err = buildSelectField(conn, "test", "t", false)
-	c.Assert(selectedField, Equals, "`id`,`name`,`quo``te`")
-	c.Assert(err, IsNil)
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.Equal(t, "`id`,`name`,`quo``te`", selectedField)
+	require.NoError(t, err)
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testSQLSuite) TestParseSnapshotToTSO(c *C) {
+func TestParseSnapshotToTSO(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		_ = db.Close()
+	}()
 
 	snapshot := "2020/07/18 20:31:50"
 	var unixTimeStamp uint64 = 1595075510
@@ -297,26 +341,31 @@ func (s *testSQLSuite) TestParseSnapshotToTSO(c *C) {
 		WithArgs(sqlmock.AnyArg()).
 		WillReturnRows(sqlmock.NewRows([]string{`unix_timestamp("2020/07/18 20:31:50")`}).AddRow(1595075510))
 	tso, err := parseSnapshotToTSO(db, snapshot)
-	c.Assert(err, IsNil)
-	c.Assert(tso, Equals, (unixTimeStamp<<18)*1000)
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.NoError(t, err)
+	require.Equal(t, (unixTimeStamp<<18)*1000, tso)
+	require.NoError(t, mock.ExpectationsWereMet())
 
 	// generate columns not valid snapshot
 	mock.ExpectQuery(`SELECT unix_timestamp(?)`).
 		WithArgs(sqlmock.AnyArg()).
 		WillReturnRows(sqlmock.NewRows([]string{`unix_timestamp("XXYYZZ")`}).AddRow(nil))
 	tso, err = parseSnapshotToTSO(db, "XXYYZZ")
-	c.Assert(err, ErrorMatches, "snapshot XXYYZZ format not supported. please use tso or '2006-01-02 15:04:05' format time")
-	c.Assert(tso, Equals, uint64(0))
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.EqualError(t, err, "snapshot XXYYZZ format not supported. please use tso or '2006-01-02 15:04:05' format time")
+	require.Equal(t, uint64(0), tso)
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testSQLSuite) TestShowCreateView(c *C) {
+func TestShowCreateView(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	mock.ExpectQuery("SHOW FIELDS FROM `test`.`v`").
 		WillReturnRows(sqlmock.NewRows([]string{"Field", "Type", "Null", "Key", "Default", "Extra"}).
@@ -327,13 +376,15 @@ func (s *testSQLSuite) TestShowCreateView(c *C) {
 			AddRow("v", "CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` (`a`) AS SELECT `t`.`a` AS `a` FROM `test`.`t`", "utf8", "utf8_general_ci"))
 
 	createTableSQL, createViewSQL, err := ShowCreateView(conn, "test", "v")
-	c.Assert(err, IsNil)
-	c.Assert(createTableSQL, Equals, "CREATE TABLE `v`(\n`a` int\n)ENGINE=MyISAM;\n")
-	c.Assert(createViewSQL, Equals, "DROP TABLE IF EXISTS `v`;\nDROP VIEW IF EXISTS `v`;\nSET @PREV_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT;\nSET @PREV_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS;\nSET @PREV_COLLATION_CONNECTION=@@COLLATION_CONNECTION;\nSET character_set_client = utf8;\nSET character_set_results = utf8;\nSET collation_connection = utf8_general_ci;\nCREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` (`a`) AS SELECT `t`.`a` AS `a` FROM `test`.`t`;\nSET character_set_client = @PREV_CHARACTER_SET_CLIENT;\nSET character_set_results = @PREV_CHARACTER_SET_RESULTS;\nSET collation_connection = @PREV_COLLATION_CONNECTION;\n")
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.NoError(t, err)
+	require.Equal(t, "CREATE TABLE `v`(\n`a` int\n)ENGINE=MyISAM;\n", createTableSQL)
+	require.Equal(t, "DROP TABLE IF EXISTS `v`;\nDROP VIEW IF EXISTS `v`;\nSET @PREV_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT;\nSET @PREV_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS;\nSET @PREV_COLLATION_CONNECTION=@@COLLATION_CONNECTION;\nSET character_set_client = utf8;\nSET character_set_results = utf8;\nSET collation_connection = utf8_general_ci;\nCREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` (`a`) AS SELECT `t`.`a` AS `a` FROM `test`.`t`;\nSET character_set_client = @PREV_CHARACTER_SET_CLIENT;\nSET character_set_results = @PREV_CHARACTER_SET_RESULTS;\nSET collation_connection = @PREV_COLLATION_CONNECTION;\n", createViewSQL)
+	require.NoError(t, mock.ExpectationsWereMet())
 }
 
-func (s *testSQLSuite) TestGetSuitableRows(c *C) {
+func TestGetSuitableRows(t *testing.T) {
+	t.Parallel()
+
 	testCases := []struct {
 		avgRowLength uint64
 		expectedRows uint64
@@ -357,47 +408,59 @@ func (s *testSQLSuite) TestGetSuitableRows(c *C) {
 	}
 	for _, testCase := range testCases {
 		rows := GetSuitableRows(testCase.avgRowLength)
-		c.Assert(rows, Equals, testCase.expectedRows)
+		require.Equal(t, testCase.expectedRows, rows)
 	}
 }
 
-func (s *testSQLSuite) TestSelectTiDBRowID(c *C) {
+func TestSelectTiDBRowID(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	database, table := "test", "t"
 
 	// _tidb_rowid is unavailable, or PKIsHandle.
 	mock.ExpectExec("SELECT _tidb_rowid from `test`.`t`").
 		WillReturnError(errors.New(`1054, "Unknown column '_tidb_rowid' in 'field list'"`))
 	hasImplicitRowID, err := SelectTiDBRowID(conn, database, table)
-	c.Assert(err, IsNil)
-	c.Assert(hasImplicitRowID, IsFalse)
+	require.NoError(t, err)
+	require.False(t, hasImplicitRowID)
 
 	// _tidb_rowid is available.
 	mock.ExpectExec("SELECT _tidb_rowid from `test`.`t`").
 		WillReturnResult(sqlmock.NewResult(0, 0))
 	hasImplicitRowID, err = SelectTiDBRowID(conn, database, table)
-	c.Assert(err, IsNil)
-	c.Assert(hasImplicitRowID, IsTrue)
+	require.NoError(t, err)
+	require.True(t, hasImplicitRowID)
 
 	// _tidb_rowid returns error
 	expectedErr := errors.New("mock error")
 	mock.ExpectExec("SELECT _tidb_rowid from `test`.`t`").
 		WillReturnError(expectedErr)
 	hasImplicitRowID, err = SelectTiDBRowID(conn, database, table)
-	c.Assert(errors.Cause(err), Equals, expectedErr)
-	c.Assert(hasImplicitRowID, IsFalse)
+	require.ErrorIs(t, errors.Cause(err), expectedErr)
+	require.False(t, hasImplicitRowID)
 }
 
-func (s *testSQLSuite) TestBuildTableSampleQueries(c *C) {
+func TestBuildTableSampleQueries(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	tctx, cancel := tcontext.Background().WithLogger(appLogger).WithCancel()
 
 	d := &Dumper{
@@ -615,7 +678,7 @@ func (s *testSQLSuite) TestBuildTableSampleQueries(c *C) {
 	}
 
 	for caseID, testCase := range testCases {
-		c.Log(fmt.Sprintf("case #%d", caseID))
+		t.Logf("case #%d", caseID)
 		handleColNames := testCase.handleColNames
 		handleColTypes := testCase.handleColTypes
 		handleVals := testCase.handleVals
@@ -623,7 +686,7 @@ func (s *testSQLSuite) TestBuildTableSampleQueries(c *C) {
 
 		// Test build whereClauses
 		whereClauses := buildWhereClauses(handleColNames, handleValStrings)
-		c.Assert(whereClauses, DeepEquals, testCase.expectedWhereClauses)
+		require.Equal(t, testCase.expectedWhereClauses, whereClauses)
 
 		// Test build tasks through table sample
 		if len(handleColNames) > 0 {
@@ -675,18 +738,19 @@ func (s *testSQLSuite) TestBuildTableSampleQueries(c *C) {
 				}
 			}
 
-			c.Assert(d.concurrentDumpTable(tctx, conn, meta, taskChan), IsNil)
-			c.Assert(mock.ExpectationsWereMet(), IsNil)
+			require.NoError(t, d.concurrentDumpTable(tctx, conn, meta, taskChan))
+			require.NoError(t, mock.ExpectationsWereMet())
 			orderByClause := buildOrderByClauseString(handleColNames)
 
 			checkQuery := func(i int, query string) {
 				task := <-taskChan
 				taskTableData, ok := task.(*TaskTableData)
-				c.Assert(ok, IsTrue)
-				c.Assert(taskTableData.ChunkIndex, Equals, i)
+				require.True(t, ok)
+				require.Equal(t, i, taskTableData.ChunkIndex)
+
 				data, ok := taskTableData.Data.(*tableData)
-				c.Assert(ok, IsTrue)
-				c.Assert(data.query, Equals, query)
+				require.True(t, ok)
+				require.Equal(t, query, data.query)
 			}
 
 			// special case, no value found
@@ -704,7 +768,9 @@ func (s *testSQLSuite) TestBuildTableSampleQueries(c *C) {
 	}
 }
 
-func (s *testSQLSuite) TestBuildPartitionClauses(c *C) {
+func TestBuildPartitionClauses(t *testing.T) {
+	t.Parallel()
+
 	const (
 		dbName        = "test"
 		tbName        = "t"
@@ -758,16 +824,22 @@ func (s *testSQLSuite) TestBuildPartitionClauses(c *C) {
 	}
 	for _, testCase := range testCases {
 		query := buildSelectQuery(dbName, tbName, fields, testCase.partition, testCase.where, testCase.orderByClause)
-		c.Assert(query, Equals, testCase.expectedQuery)
+		require.Equal(t, testCase.expectedQuery, query)
 	}
 }
 
-func (s *testSQLSuite) TestBuildRegionQueriesWithoutPartition(c *C) {
+func TestBuildRegionQueriesWithoutPartition(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	tctx, cancel := tcontext.Background().WithLogger(appLogger).WithCancel()
 
 	d := &Dumper{
@@ -852,8 +924,8 @@ func (s *testSQLSuite) TestBuildRegionQueriesWithoutPartition(c *C) {
 		},
 	}
 
-	for caseID, testCase := range testCases {
-		c.Log(fmt.Sprintf("case #%d", caseID))
+	for i, testCase := range testCases {
+		t.Logf("case #%d", i)
 		handleColNames := testCase.handleColNames
 		handleColTypes := testCase.handleColTypes
 		regionResults := testCase.regionResults
@@ -901,28 +973,34 @@ func (s *testSQLSuite) TestBuildRegionQueriesWithoutPartition(c *C) {
 			mock.ExpectQuery(fmt.Sprintf("SHOW INDEX FROM `%s`.`%s`", database, table)).WillReturnRows(rows)
 			mock.ExpectQuery("SHOW INDEX FROM").WillReturnRows(sqlmock.NewRows(showIndexHeaders))
 		}
-		c.Assert(d.concurrentDumpTable(tctx, conn, meta, taskChan), IsNil)
-		c.Assert(mock.ExpectationsWereMet(), IsNil)
+		require.NoError(t, d.concurrentDumpTable(tctx, conn, meta, taskChan))
+		require.NoError(t, mock.ExpectationsWereMet())
 
 		for i, w := range testCase.expectedWhereClauses {
 			query := buildSelectQuery(database, table, "*", "", buildWhereCondition(d.conf, w), orderByClause)
 			task := <-taskChan
 			taskTableData, ok := task.(*TaskTableData)
-			c.Assert(ok, IsTrue)
-			c.Assert(taskTableData.ChunkIndex, Equals, i)
+			require.True(t, ok)
+			require.Equal(t, i, taskTableData.ChunkIndex)
 			data, ok := taskTableData.Data.(*tableData)
-			c.Assert(ok, IsTrue)
-			c.Assert(data.query, Equals, query)
+			require.True(t, ok)
+			require.Equal(t, query, data.query)
 		}
 	}
 }
 
-func (s *testSQLSuite) TestBuildRegionQueriesWithPartitions(c *C) {
+func TestBuildRegionQueriesWithPartitions(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	tctx, cancel := tcontext.Background().WithLogger(appLogger).WithCancel()
 
 	d := &Dumper{
@@ -1050,7 +1128,7 @@ func (s *testSQLSuite) TestBuildRegionQueriesWithPartitions(c *C) {
 	}
 
 	for i, testCase := range testCases {
-		c.Log(fmt.Sprintf("case #%d", i))
+		t.Logf("case #%d", i)
 		handleColNames := testCase.handleColNames
 		handleColTypes := testCase.handleColTypes
 		regionResults := testCase.regionResults
@@ -1095,8 +1173,8 @@ func (s *testSQLSuite) TestBuildRegionQueriesWithPartitions(c *C) {
 		}
 
 		orderByClause := buildOrderByClauseString(handleColNames)
-		c.Assert(d.concurrentDumpTable(tctx, conn, meta, taskChan), IsNil)
-		c.Assert(mock.ExpectationsWereMet(), IsNil)
+		require.NoError(t, d.concurrentDumpTable(tctx, conn, meta, taskChan))
+		require.NoError(t, mock.ExpectationsWereMet())
 
 		chunkIdx := 0
 		for i, partition := range partitions {
@@ -1104,11 +1182,11 @@ func (s *testSQLSuite) TestBuildRegionQueriesWithPartitions(c *C) {
 				query := buildSelectQuery(database, table, "*", partition, buildWhereCondition(d.conf, w), orderByClause)
 				task := <-taskChan
 				taskTableData, ok := task.(*TaskTableData)
-				c.Assert(ok, IsTrue)
-				c.Assert(taskTableData.ChunkIndex, Equals, chunkIdx)
+				require.True(t, ok)
+				require.Equal(t, chunkIdx, taskTableData.ChunkIndex)
 				data, ok := taskTableData.Data.(*tableData)
-				c.Assert(ok, IsTrue)
-				c.Assert(data.query, Equals, query)
+				require.True(t, ok)
+				require.Equal(t, query, data.query)
 				chunkIdx++
 			}
 		}
@@ -1123,12 +1201,12 @@ func buildMockNewRows(mock sqlmock.Sqlmock, columns []string, driverValues [][]d
 	return rows
 }
 
-func readRegionCsvDriverValues(c *C) [][]driver.Value {
+func readRegionCsvDriverValues(t *testing.T) [][]driver.Value {
 	// nolint: dogsled
 	_, filename, _, _ := runtime.Caller(0)
 	csvFilename := path.Join(path.Dir(filename), "region_results.csv")
 	file, err := os.Open(csvFilename)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	csvReader := csv.NewReader(file)
 	values := make([][]driver.Value, 0, 990)
 	for {
@@ -1136,24 +1214,30 @@ func readRegionCsvDriverValues(c *C) [][]driver.Value {
 		if err == io.EOF {
 			break
 		}
-		c.Assert(err, IsNil)
+		require.NoError(t, err)
 		if len(results) != 3 {
 			continue
 		}
 		regionID, err := strconv.Atoi(results[0])
-		c.Assert(err, IsNil)
+		require.NoError(t, err)
 		startKey, endKey := results[1], results[2]
 		values = append(values, []driver.Value{regionID, startKey, endKey})
 	}
 	return values
 }
 
-func (s *testSQLSuite) TestBuildVersion3RegionQueries(c *C) {
+func TestBuildVersion3RegionQueries(t *testing.T) {
+	t.Parallel()
+
 	db, mock, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	defer db.Close()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, db.Close())
+	}()
+
 	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	tctx, cancel := tcontext.Background().WithLogger(appLogger).WithCancel()
 	oldOpenFunc := openDBFunc
 	defer func() {
@@ -1258,7 +1342,7 @@ func (s *testSQLSuite) TestBuildVersion3RegionQueries(c *C) {
 			{2142, "2021-06-28 10:59:03", 1959424},
 			{2142, "2021-06-28 10:59:04", 2000000},
 		})
-	selectRegionStatusHistograms := buildMockNewRows(mock, []string{"REGION_ID", "START_KEY", "END_KEY"}, readRegionCsvDriverValues(c))
+	selectRegionStatusHistograms := buildMockNewRows(mock, []string{"REGION_ID", "START_KEY", "END_KEY"}, readRegionCsvDriverValues(t))
 	selectInformationSchemaTables := buildMockNewRows(mock, []string{"TABLE_SCHEMA", "TABLE_NAME", "TIDB_TABLE_ID"},
 		[][]driver.Value{
 			{"mysql", "expr_pushdown_blacklist", 39},
@@ -1295,8 +1379,8 @@ func (s *testSQLSuite) TestBuildVersion3RegionQueries(c *C) {
 	mock.ExpectQuery("SELECT REGION_ID,START_KEY,END_KEY FROM INFORMATION_SCHEMA.TIKV_REGION_STATUS ORDER BY START_KEY;").
 		WillReturnRows(selectRegionStatusHistograms)
 
-	c.Assert(d.renewSelectTableRegionFuncForLowerTiDB(tctx), IsNil)
-	c.Assert(mock.ExpectationsWereMet(), IsNil)
+	require.NoError(t, d.renewSelectTableRegionFuncForLowerTiDB(tctx))
+	require.NoError(t, mock.ExpectationsWereMet())
 
 	testCases := []struct {
 		tableName            string
@@ -1371,7 +1455,7 @@ func (s *testSQLSuite) TestBuildVersion3RegionQueries(c *C) {
 	}
 
 	for i, testCase := range testCases {
-		c.Log(fmt.Sprintf("case #%d", i))
+		t.Logf("case #%d", i)
 		table := testCase.tableName
 		handleColNames := testCase.handleColNames
 		handleColTypes := testCase.handleColTypes
@@ -1399,19 +1483,21 @@ func (s *testSQLSuite) TestBuildVersion3RegionQueries(c *C) {
 		}
 
 		orderByClause := buildOrderByClauseString(handleColNames)
-		c.Assert(d.concurrentDumpTable(tctx, conn, meta, taskChan), IsNil)
-		c.Assert(mock.ExpectationsWereMet(), IsNil)
+		require.NoError(t, d.concurrentDumpTable(tctx, conn, meta, taskChan))
+		require.NoError(t, mock.ExpectationsWereMet())
 
 		chunkIdx := 0
 		for _, w := range testCase.expectedWhereClauses {
 			query := buildSelectQuery(database, table, "*", "", buildWhereCondition(d.conf, w), orderByClause)
 			task := <-taskChan
 			taskTableData, ok := task.(*TaskTableData)
-			c.Assert(ok, IsTrue)
-			c.Assert(taskTableData.ChunkIndex, Equals, chunkIdx)
+			require.True(t, ok)
+			require.Equal(t, chunkIdx, taskTableData.ChunkIndex)
+
 			data, ok := taskTableData.Data.(*tableData)
-			c.Assert(ok, IsTrue)
-			c.Assert(data.query, Equals, query)
+			require.True(t, ok)
+			require.Equal(t, query, data.query)
+
 			chunkIdx++
 		}
 	}
diff --git a/v4/export/sql_type_test.go b/v4/export/sql_type_test.go
index 7f7c1cd..97ae6d0 100644
--- a/v4/export/sql_type_test.go
+++ b/v4/export/sql_type_test.go
@@ -4,15 +4,14 @@ package export
 
 import (
 	"bytes"
+	"testing"
 
-	. "github.com/pingcap/check"
+	"github.com/stretchr/testify/require"
 )
 
-var _ = Suite(&testSQLByteSuite{})
+func TestEscape(t *testing.T) {
+	t.Parallel()
 
-type testSQLByteSuite struct{}
-
-func (s *testSQLByteSuite) TestEscape(c *C) {
 	var bf bytes.Buffer
 	str := []byte(`MWQeWw""'\rNmtGxzGp`)
 	expectStrBackslash := `MWQeWw\"\"\'\\rNmtGxzGp`
@@ -20,37 +19,39 @@ func (s *testSQLByteSuite) TestEscape(c *C) {
 	expectStrBackslashDoubleQuote := `MWQeWw\"\"'\\rNmtGxzGp`
 	expectStrWithoutBackslashDoubleQuote := `MWQeWw""""'\rNmtGxzGp`
 	escapeSQL(str, &bf, true)
-	c.Assert(bf.String(), Equals, expectStrBackslash)
+	require.Equal(t, expectStrBackslash, bf.String())
+
 	bf.Reset()
 	escapeSQL(str, &bf, false)
-	c.Assert(bf.String(), Equals, expectStrWithoutBackslash)
+	require.Equal(t, expectStrWithoutBackslash, bf.String())
+
 	bf.Reset()
 	opt := &csvOption{
 		delimiter: []byte(`"`),
 		separator: []byte(`,`),
 	}
 	escapeCSV(str, &bf, true, opt)
-	c.Assert(bf.String(), Equals, expectStrBackslashDoubleQuote)
+	require.Equal(t, expectStrBackslashDoubleQuote, bf.String())
+
 	bf.Reset()
 	escapeCSV(str, &bf, false, opt)
-	c.Assert(bf.String(), Equals, expectStrWithoutBackslashDoubleQuote)
-	bf.Reset()
+	require.Equal(t, expectStrWithoutBackslashDoubleQuote, bf.String())
 
+	bf.Reset()
 	str = []byte(`a|*|b"cd`)
 	expectedStrWithDelimiter := `a|*|b""cd`
 	expectedStrBackslashWithoutDelimiter := `a\|*\|b"cd`
 	expectedStrWithoutDelimiter := `a|*|b"cd`
-
 	escapeCSV(str, &bf, false, opt)
-	c.Assert(bf.String(), Equals, expectedStrWithDelimiter)
-	bf.Reset()
+	require.Equal(t, expectedStrWithDelimiter, bf.String())
 
+	bf.Reset()
 	opt.delimiter = []byte("")
 	opt.separator = []byte(`|*|`)
 	escapeCSV(str, &bf, true, opt)
-	c.Assert(bf.String(), Equals, expectedStrBackslashWithoutDelimiter)
+	require.Equal(t, expectedStrBackslashWithoutDelimiter, bf.String())
+
 	bf.Reset()
 	escapeCSV(str, &bf, false, opt)
-	c.Assert(bf.String(), Equals, expectedStrWithoutDelimiter)
-	bf.Reset()
+	require.Equal(t, expectedStrWithoutDelimiter, bf.String())
 }
diff --git a/v4/export/writer_util_test.go b/v4/export/writer_serial_test.go
similarity index 65%
rename from v4/export/writer_util_test.go
rename to v4/export/writer_serial_test.go
index 63b3afe..441a38f 100644
--- a/v4/export/writer_util_test.go
+++ b/v4/export/writer_serial_test.go
@@ -9,56 +9,13 @@ import (
 	"testing"
 
 	tcontext "github.com/pingcap/dumpling/v4/context"
-	"github.com/pingcap/dumpling/v4/log"
+	"github.com/stretchr/testify/require"
 
 	"github.com/pingcap/br/pkg/storage"
-	. "github.com/pingcap/check"
 	"github.com/pingcap/errors"
-	"github.com/prometheus/client_golang/prometheus"
 )
 
-var appLogger log.Logger
-
-func TestT(t *testing.T) {
-	initColTypeRowReceiverMap()
-	logger, _, err := log.InitAppLogger(&log.Config{
-		Level:  "debug",
-		File:   "",
-		Format: "text",
-	})
-	if err != nil {
-		t.Log("fail to init logger, err: " + err.Error())
-		t.Fail()
-	}
-	appLogger = logger
-	registry := prometheus.NewRegistry()
-	registry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
-	registry.MustRegister(prometheus.NewGoCollector())
-	RegisterMetrics(registry)
-	TestingT(t)
-}
-
-var _ = SerialSuites(&testWriteSuite{})
-
-type testWriteSuite struct {
-	mockCfg *Config
-}
-
-func (s *testWriteSuite) SetUpSuite(_ *C) {
-	s.mockCfg = &Config{
-		FileSize: UnspecifiedSize,
-	}
-	InitMetricsVector(s.mockCfg.Labels)
-}
-
-func (s *testWriteSuite) TearDownTest(c *C) {
-	RemoveLabelValuesWithTaskInMetrics(s.mockCfg.Labels)
-
-	c.Assert(ReadGauge(finishedRowsGauge, s.mockCfg.Labels), Equals, float64(0))
-	c.Assert(ReadGauge(finishedSizeGauge, s.mockCfg.Labels), Equals, float64(0))
-}
-
-func (s *testWriteSuite) TestWriteMeta(c *C) {
+func TestWriteMeta(t *testing.T) {
 	createTableStmt := "CREATE TABLE `t1` (\n" +
 		"  `a` int(11) DEFAULT NULL\n" +
 		") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n"
@@ -67,15 +24,19 @@ func (s *testWriteSuite) TestWriteMeta(c *C) {
 	writer := storage.NewBufferWriter()
 
 	err := WriteMeta(tcontext.Background(), meta, writer)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	expected := "/*!40103 SET TIME_ZONE='+00:00' */;\n" +
 		"CREATE TABLE `t1` (\n" +
 		"  `a` int(11) DEFAULT NULL\n" +
 		") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;\n"
-	c.Assert(writer.String(), Equals, expected)
+	require.Equal(t, expected, writer.String())
 }
 
-func (s *testWriteSuite) TestWriteInsert(c *C) {
+func TestWriteInsert(t *testing.T) {
+	cfg, clean := createMockConfig(t)
+	defer clean()
+
 	data := [][]driver.Value{
 		{"1", "male", "bob@mail.com", "020-1234", nil},
 		{"2", "female", "sarah@mail.com", "020-1253", "healthy"},
@@ -90,10 +51,11 @@ func (s *testWriteSuite) TestWriteInsert(c *C) {
 	tableIR := newMockTableIR("test", "employee", data, specCmts, colTypes)
 	bf := storage.NewBufferWriter()
 
-	conf := configForWriteSQL(s.mockCfg, UnspecifiedSize, UnspecifiedSize)
+	conf := configForWriteSQL(cfg, UnspecifiedSize, UnspecifiedSize)
 	n, err := WriteInsert(tcontext.Background(), conf, tableIR, tableIR, bf)
-	c.Assert(n, Equals, uint64(4))
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+	require.Equal(t, uint64(4), n)
+
 	expected := "/*!40101 SET NAMES binary*/;\n" +
 		"/*!40014 SET FOREIGN_KEY_CHECKS=0*/;\n" +
 		"INSERT INTO `employee` VALUES\n" +
@@ -101,12 +63,15 @@ func (s *testWriteSuite) TestWriteInsert(c *C) {
 		"(2,'female','sarah@mail.com','020-1253','healthy'),\n" +
 		"(3,'male','john@mail.com','020-1256','healthy'),\n" +
 		"(4,'female','sarah@mail.com','020-1235','healthy');\n"
-	c.Assert(bf.String(), Equals, expected)
-	c.Assert(ReadGauge(finishedRowsGauge, conf.Labels), Equals, float64(len(data)))
-	c.Assert(ReadGauge(finishedSizeGauge, conf.Labels), Equals, float64(len(expected)))
+	require.Equal(t, expected, bf.String())
+	require.Equal(t, ReadGauge(finishedRowsGauge, conf.Labels), float64(len(data)))
+	require.Equal(t, ReadGauge(finishedSizeGauge, conf.Labels), float64(len(expected)))
 }
 
-func (s *testWriteSuite) TestWriteInsertReturnsError(c *C) {
+func TestWriteInsertReturnsError(t *testing.T) {
+	cfg, clean := createMockConfig(t)
+	defer clean()
+
 	data := [][]driver.Value{
 		{"1", "male", "bob@mail.com", "020-1234", nil},
 		{"2", "female", "sarah@mail.com", "020-1253", "healthy"},
@@ -124,23 +89,27 @@ func (s *testWriteSuite) TestWriteInsertReturnsError(c *C) {
 	tableIR.rowErr = rowErr
 	bf := storage.NewBufferWriter()
 
-	conf := configForWriteSQL(s.mockCfg, UnspecifiedSize, UnspecifiedSize)
+	conf := configForWriteSQL(cfg, UnspecifiedSize, UnspecifiedSize)
 	n, err := WriteInsert(tcontext.Background(), conf, tableIR, tableIR, bf)
-	c.Assert(n, Equals, uint64(3))
-	c.Assert(err, Equals, rowErr)
+	require.ErrorIs(t, err, rowErr)
+	require.Equal(t, uint64(3), n)
+
 	expected := "/*!40101 SET NAMES binary*/;\n" +
 		"/*!40014 SET FOREIGN_KEY_CHECKS=0*/;\n" +
 		"INSERT INTO `employee` VALUES\n" +
 		"(1,'male','bob@mail.com','020-1234',NULL),\n" +
 		"(2,'female','sarah@mail.com','020-1253','healthy'),\n" +
 		"(3,'male','john@mail.com','020-1256','healthy');\n"
-	c.Assert(bf.String(), Equals, expected)
+	require.Equal(t, expected, bf.String())
 	// error occurred, should revert pointer to zero
-	c.Assert(ReadGauge(finishedRowsGauge, conf.Labels), Equals, float64(0))
-	c.Assert(ReadGauge(finishedSizeGauge, conf.Labels), Equals, float64(0))
+	require.Equal(t, ReadGauge(finishedRowsGauge, conf.Labels), float64(0))
+	require.Equal(t, ReadGauge(finishedSizeGauge, conf.Labels), float64(0))
 }
 
-func (s *testWriteSuite) TestWriteInsertInCsv(c *C) {
+func TestWriteInsertInCsv(t *testing.T) {
+	cfg, clean := createMockConfig(t)
+	defer clean()
+
 	data := [][]driver.Value{
 		{"1", "male", "bob@mail.com", "020-1234", nil},
 		{"2", "female", "sarah@mail.com", "020-1253", "healthy"},
@@ -153,51 +122,57 @@ func (s *testWriteSuite) TestWriteInsertInCsv(c *C) {
 
 	// test nullValue
 	opt := &csvOption{separator: []byte(","), delimiter: doubleQuotationMark, nullValue: "\\N"}
-	conf := configForWriteCSV(s.mockCfg, true, opt)
+	conf := configForWriteCSV(cfg, true, opt)
 	n, err := WriteInsertInCsv(tcontext.Background(), conf, tableIR, tableIR, bf)
-	c.Assert(n, Equals, uint64(4))
-	c.Assert(err, IsNil)
+	require.Equal(t, uint64(4), n)
+	require.NoError(t, err)
+
 	expected := "1,\"male\",\"bob@mail.com\",\"020-1234\",\\N\n" +
 		"2,\"female\",\"sarah@mail.com\",\"020-1253\",\"healthy\"\n" +
 		"3,\"male\",\"john@mail.com\",\"020-1256\",\"healthy\"\n" +
 		"4,\"female\",\"sarah@mail.com\",\"020-1235\",\"healthy\"\n"
-	c.Assert(bf.String(), Equals, expected)
-	c.Assert(ReadGauge(finishedRowsGauge, conf.Labels), Equals, float64(len(data)))
-	c.Assert(ReadGauge(finishedSizeGauge, conf.Labels), Equals, float64(len(expected)))
+	require.Equal(t, expected, bf.String())
+	require.Equal(t, float64(len(data)), ReadGauge(finishedRowsGauge, conf.Labels))
+	require.Equal(t, float64(len(expected)), ReadGauge(finishedSizeGauge, conf.Labels))
+
 	RemoveLabelValuesWithTaskInMetrics(conf.Labels)
 
 	// test delimiter
 	bf.Reset()
 	opt.delimiter = quotationMark
 	tableIR = newMockTableIR("test", "employee", data, nil, colTypes)
-	conf = configForWriteCSV(s.mockCfg, true, opt)
+	conf = configForWriteCSV(cfg, true, opt)
 	n, err = WriteInsertInCsv(tcontext.Background(), conf, tableIR, tableIR, bf)
-	c.Assert(n, Equals, uint64(4))
-	c.Assert(err, IsNil)
+	require.Equal(t, uint64(4), n)
+	require.NoError(t, err)
+
 	expected = "1,'male','bob@mail.com','020-1234',\\N\n" +
 		"2,'female','sarah@mail.com','020-1253','healthy'\n" +
 		"3,'male','john@mail.com','020-1256','healthy'\n" +
 		"4,'female','sarah@mail.com','020-1235','healthy'\n"
-	c.Assert(bf.String(), Equals, expected)
-	c.Assert(ReadGauge(finishedRowsGauge, conf.Labels), Equals, float64(len(data)))
-	c.Assert(ReadGauge(finishedSizeGauge, conf.Labels), Equals, float64(len(expected)))
+	require.Equal(t, expected, bf.String())
+	require.Equal(t, float64(len(data)), ReadGauge(finishedRowsGauge, conf.Labels))
+	require.Equal(t, float64(len(expected)), ReadGauge(finishedSizeGauge, conf.Labels))
+
 	RemoveLabelValuesWithTaskInMetrics(conf.Labels)
 
 	// test separator
 	bf.Reset()
 	opt.separator = []byte(";")
 	tableIR = newMockTableIR("test", "employee", data, nil, colTypes)
-	conf = configForWriteCSV(s.mockCfg, true, opt)
+	conf = configForWriteCSV(cfg, true, opt)
 	n, err = WriteInsertInCsv(tcontext.Background(), conf, tableIR, tableIR, bf)
-	c.Assert(n, Equals, uint64(4))
-	c.Assert(err, IsNil)
+	require.Equal(t, uint64(4), n)
+	require.NoError(t, err)
+
 	expected = "1;'male';'bob@mail.com';'020-1234';\\N\n" +
 		"2;'female';'sarah@mail.com';'020-1253';'healthy'\n" +
 		"3;'male';'john@mail.com';'020-1256';'healthy'\n" +
 		"4;'female';'sarah@mail.com';'020-1235';'healthy'\n"
-	c.Assert(bf.String(), Equals, expected)
-	c.Assert(ReadGauge(finishedRowsGauge, conf.Labels), Equals, float64(len(data)))
-	c.Assert(ReadGauge(finishedSizeGauge, conf.Labels), Equals, float64(len(expected)))
+	require.Equal(t, expected, bf.String())
+	require.Equal(t, float64(len(data)), ReadGauge(finishedRowsGauge, conf.Labels))
+	require.Equal(t, float64(len(expected)), ReadGauge(finishedSizeGauge, conf.Labels))
+
 	RemoveLabelValuesWithTaskInMetrics(conf.Labels)
 
 	// test delimiter that included in values
@@ -206,22 +181,27 @@ func (s *testWriteSuite) TestWriteInsertInCsv(c *C) {
 	opt.delimiter = []byte("ma")
 	tableIR = newMockTableIR("test", "employee", data, nil, colTypes)
 	tableIR.colNames = []string{"id", "gender", "email", "phone_number", "status"}
-	conf = configForWriteCSV(s.mockCfg, false, opt)
+	conf = configForWriteCSV(cfg, false, opt)
 	n, err = WriteInsertInCsv(tcontext.Background(), conf, tableIR, tableIR, bf)
-	c.Assert(n, Equals, uint64(4))
-	c.Assert(err, IsNil)
+	require.Equal(t, uint64(4), n)
+	require.NoError(t, err)
+
 	expected = "maidma&;,?magenderma&;,?maemamailma&;,?maphone_numberma&;,?mastatusma\n" +
 		"1&;,?mamamalema&;,?mabob@mamail.comma&;,?ma020-1234ma&;,?\\N\n" +
 		"2&;,?mafemamalema&;,?masarah@mamail.comma&;,?ma020-1253ma&;,?mahealthyma\n" +
 		"3&;,?mamamalema&;,?majohn@mamail.comma&;,?ma020-1256ma&;,?mahealthyma\n" +
 		"4&;,?mafemamalema&;,?masarah@mamail.comma&;,?ma020-1235ma&;,?mahealthyma\n"
-	c.Assert(bf.String(), Equals, expected)
-	c.Assert(ReadGauge(finishedRowsGauge, conf.Labels), Equals, float64(len(data)))
-	c.Assert(ReadGauge(finishedSizeGauge, conf.Labels), Equals, float64(len(expected)))
+	require.Equal(t, expected, bf.String())
+	require.Equal(t, float64(len(data)), ReadGauge(finishedRowsGauge, conf.Labels))
+	require.Equal(t, float64(len(expected)), ReadGauge(finishedSizeGauge, conf.Labels))
+
 	RemoveLabelValuesWithTaskInMetrics(conf.Labels)
 }
 
-func (s *testWriteSuite) TestWriteInsertInCsvReturnsError(c *C) {
+func TestWriteInsertInCsvReturnsError(t *testing.T) {
+	cfg, clean := createMockConfig(t)
+	defer clean()
+
 	data := [][]driver.Value{
 		{"1", "male", "bob@mail.com", "020-1234", nil},
 		{"2", "female", "sarah@mail.com", "020-1253", "healthy"},
@@ -238,20 +218,25 @@ func (s *testWriteSuite) TestWriteInsertInCsvReturnsError(c *C) {
 
 	// test nullValue
 	opt := &csvOption{separator: []byte(","), delimiter: doubleQuotationMark, nullValue: "\\N"}
-	conf := configForWriteCSV(s.mockCfg, true, opt)
+	conf := configForWriteCSV(cfg, true, opt)
 	n, err := WriteInsertInCsv(tcontext.Background(), conf, tableIR, tableIR, bf)
-	c.Assert(n, Equals, uint64(3))
-	c.Assert(err, Equals, rowErr)
+	require.Equal(t, uint64(3), n)
+	require.ErrorIs(t, err, rowErr)
+
 	expected := "1,\"male\",\"bob@mail.com\",\"020-1234\",\\N\n" +
 		"2,\"female\",\"sarah@mail.com\",\"020-1253\",\"healthy\"\n" +
 		"3,\"male\",\"john@mail.com\",\"020-1256\",\"healthy\"\n"
-	c.Assert(bf.String(), Equals, expected)
-	c.Assert(ReadGauge(finishedRowsGauge, conf.Labels), Equals, float64(0))
-	c.Assert(ReadGauge(finishedSizeGauge, conf.Labels), Equals, float64(0))
+	require.Equal(t, expected, bf.String())
+	require.Equal(t, float64(0), ReadGauge(finishedRowsGauge, conf.Labels))
+	require.Equal(t, float64(0), ReadGauge(finishedSizeGauge, conf.Labels))
+
 	RemoveLabelValuesWithTaskInMetrics(conf.Labels)
 }
 
-func (s *testWriteSuite) TestSQLDataTypes(c *C) {
+func TestSQLDataTypes(t *testing.T) {
+	cfg, clean := createMockConfig(t)
+	defer clean()
+
 	data := [][]driver.Value{
 		{"CHAR", "char1", `'char1'`},
 		{"INT", 12345, `12345`},
@@ -266,20 +251,22 @@ func (s *testWriteSuite) TestSQLDataTypes(c *C) {
 		tableIR := newMockTableIR("test", "t", tableData, nil, colType)
 		bf := storage.NewBufferWriter()
 
-		conf := configForWriteSQL(s.mockCfg, UnspecifiedSize, UnspecifiedSize)
+		conf := configForWriteSQL(cfg, UnspecifiedSize, UnspecifiedSize)
 		n, err := WriteInsert(tcontext.Background(), conf, tableIR, tableIR, bf)
-		c.Assert(n, Equals, uint64(1))
-		c.Assert(err, IsNil)
+		require.NoError(t, err)
+		require.Equal(t, uint64(1), n)
+
 		lines := strings.Split(bf.String(), "\n")
-		c.Assert(len(lines), Equals, 3)
-		c.Assert(lines[1], Equals, fmt.Sprintf("(%s);", result))
-		c.Assert(ReadGauge(finishedRowsGauge, conf.Labels), Equals, float64(1))
-		c.Assert(ReadGauge(finishedSizeGauge, conf.Labels), Equals, float64(len(bf.String())))
+		require.Len(t, lines, 3)
+		require.Equal(t, fmt.Sprintf("(%s);", result), lines[1])
+		require.Equal(t, float64(1), ReadGauge(finishedRowsGauge, conf.Labels))
+		require.Equal(t, float64(len(bf.String())), ReadGauge(finishedSizeGauge, conf.Labels))
+
 		RemoveLabelValuesWithTaskInMetrics(conf.Labels)
 	}
 }
 
-func (s *testWriteSuite) TestWrite(c *C) {
+func TestWrite(t *testing.T) {
 	mocksw := &mockPoisonWriter{}
 	src := []string{"test", "loooooooooooooooooooong", "poison"}
 	exp := []string{"test", "loooooooooooooooooooong", "poison_error"}
@@ -287,14 +274,13 @@ func (s *testWriteSuite) TestWrite(c *C) {
 	for i, s := range src {
 		err := write(tcontext.Background(), mocksw, s)
 		if err != nil {
-			c.Assert(err.Error(), Equals, exp[i])
+			require.EqualError(t, err, exp[i])
 		} else {
-			c.Assert(s, Equals, mocksw.buf)
-			c.Assert(mocksw.buf, Equals, exp[i])
+			require.Equal(t, s, mocksw.buf)
+			require.Equal(t, exp[i], mocksw.buf)
 		}
 	}
-	err := write(tcontext.Background(), mocksw, "test")
-	c.Assert(err, IsNil)
+	require.NoError(t, write(tcontext.Background(), mocksw, "test"))
 }
 
 // cloneConfigForTest clones a dumpling config.
@@ -320,3 +306,19 @@ func configForWriteCSV(config *Config, noHeader bool, opt *csvOption) *Config {
 	cfg.FileSize = UnspecifiedSize
 	return cfg
 }
+
+func createMockConfig(t *testing.T) (cfg *Config, clean func()) {
+	cfg = &Config{
+		FileSize: UnspecifiedSize,
+	}
+
+	InitMetricsVector(cfg.Labels)
+
+	clean = func() {
+		RemoveLabelValuesWithTaskInMetrics(cfg.Labels)
+		require.Equal(t, float64(0), ReadGauge(finishedRowsGauge, cfg.Labels))
+		require.Equal(t, float64(0), ReadGauge(finishedSizeGauge, cfg.Labels))
+	}
+
+	return
+}
diff --git a/v4/export/writer_test.go b/v4/export/writer_test.go
index 08b6272..27a5ad7 100644
--- a/v4/export/writer_test.go
+++ b/v4/export/writer_test.go
@@ -8,102 +8,97 @@ import (
 	"io/ioutil"
 	"os"
 	"path"
-
-	tcontext "github.com/pingcap/dumpling/v4/context"
+	"sync"
+	"testing"
 
 	"github.com/DATA-DOG/go-sqlmock"
-	. "github.com/pingcap/check"
+	tcontext "github.com/pingcap/dumpling/v4/context"
+	"github.com/stretchr/testify/require"
 )
 
-var _ = Suite(&testWriterSuite{})
-
-type testWriterSuite struct{}
+func TestWriteDatabaseMeta(t *testing.T) {
+	t.Parallel()
 
-func defaultConfigForTest(c *C) *Config {
-	config := DefaultConfig()
-	c.Assert(adjustFileFormat(config), IsNil)
-	return config
-}
-
-func (s *testWriterSuite) newWriter(conf *Config, c *C) *Writer {
-	extStore, err := conf.createExternalStorage(context.Background())
-	c.Assert(err, IsNil)
-	db, _, err := sqlmock.New()
-	c.Assert(err, IsNil)
-	conn, err := db.Conn(context.Background())
-	c.Assert(err, IsNil)
-	return NewWriter(tcontext.Background(), 0, conf, conn, extStore)
-}
-
-func (s *testWriterSuite) TestWriteDatabaseMeta(c *C) {
-	dir := c.MkDir()
-
-	config := defaultConfigForTest(c)
+	dir := t.TempDir()
+	config := defaultConfigForTest(t)
 	config.OutputDirPath = dir
 
-	writer := s.newWriter(config, c)
+	writer, clean := createTestWriter(config, t)
+	defer clean()
+
 	err := writer.WriteDatabaseMeta("test", "CREATE DATABASE `test`")
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	p := path.Join(dir, "test-schema-create.sql")
 	_, err = os.Stat(p)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
+
 	bytes, err := ioutil.ReadFile(p)
-	c.Assert(err, IsNil)
-	c.Assert(string(bytes), Equals, "/*!40101 SET NAMES binary*/;\nCREATE DATABASE `test`;\n")
+	require.NoError(t, err)
+	require.Equal(t, "/*!40101 SET NAMES binary*/;\nCREATE DATABASE `test`;\n", string(bytes))
 }
 
-func (s *testWriterSuite) TestWriteTableMeta(c *C) {
-	dir := c.MkDir()
+func TestWriteTableMeta(t *testing.T) {
+	t.Parallel()
 
-	config := defaultConfigForTest(c)
+	dir := t.TempDir()
+
+	config := defaultConfigForTest(t)
 	config.OutputDirPath = dir
 
-	writer := s.newWriter(config, c)
+	writer, clean := createTestWriter(config, t)
+	defer clean()
+
 	err := writer.WriteTableMeta("test", "t", "CREATE TABLE t (a INT)")
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	p := path.Join(dir, "test.t-schema.sql")
 	_, err = os.Stat(p)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	bytes, err := ioutil.ReadFile(p)
-	c.Assert(err, IsNil)
-	c.Assert(string(bytes), Equals, "/*!40101 SET NAMES binary*/;\nCREATE TABLE t (a INT);\n")
+	require.NoError(t, err)
+	require.Equal(t, "/*!40101 SET NAMES binary*/;\nCREATE TABLE t (a INT);\n", string(bytes))
 }
 
-func (s *testWriterSuite) TestWriteViewMeta(c *C) {
-	dir := c.MkDir()
+func TestWriteViewMeta(t *testing.T) {
+	t.Parallel()
 
-	config := defaultConfigForTest(c)
+	dir := t.TempDir()
+	config := defaultConfigForTest(t)
 	config.OutputDirPath = dir
 
-	writer := s.newWriter(config, c)
+	writer, clean := createTestWriter(config, t)
+	defer clean()
+
 	specCmt := "/*!40101 SET NAMES binary*/;\n"
 	createTableSQL := "CREATE TABLE `v`(\n`a` int\n)ENGINE=MyISAM;\n"
 	createViewSQL := "DROP TABLE IF EXISTS `v`;\nDROP VIEW IF EXISTS `v`;\nSET @PREV_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT;\nSET @PREV_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS;\nSET @PREV_COLLATION_CONNECTION=@@COLLATION_CONNECTION;\nSET character_set_client = utf8;\nSET character_set_results = utf8;\nSET collation_connection = utf8_general_ci;\nCREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` (`a`) AS SELECT `t`.`a` AS `a` FROM `test`.`t`;\nSET character_set_client = @PREV_CHARACTER_SET_CLIENT;\nSET character_set_results = @PREV_CHARACTER_SET_RESULTS;\nSET collation_connection = @PREV_COLLATION_CONNECTION;\n"
 	err := writer.WriteViewMeta("test", "v", createTableSQL, createViewSQL)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	p := path.Join(dir, "test.v-schema.sql")
 	_, err = os.Stat(p)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	bytes, err := ioutil.ReadFile(p)
-	c.Assert(err, IsNil)
-	c.Assert(string(bytes), Equals, specCmt+createTableSQL)
+	require.NoError(t, err)
+	require.Equal(t, specCmt+createTableSQL, string(bytes))
 
 	p = path.Join(dir, "test.v-schema-view.sql")
 	_, err = os.Stat(p)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	bytes, err = ioutil.ReadFile(p)
-	c.Assert(err, IsNil)
-	c.Assert(string(bytes), Equals, specCmt+createViewSQL)
+	require.NoError(t, err)
+	require.Equal(t, specCmt+createViewSQL, string(bytes))
 }
 
-func (s *testWriterSuite) TestWriteTableData(c *C) {
-	dir := c.MkDir()
+func TestWriteTableData(t *testing.T) {
+	t.Parallel()
 
-	config := defaultConfigForTest(c)
+	dir := t.TempDir()
+	config := defaultConfigForTest(t)
 	config.OutputDirPath = dir
 
-	writer := s.newWriter(config, c)
+	writer, clean := createTestWriter(config, t)
+	defer clean()
 
 	data := [][]driver.Value{
 		{"1", "male", "bob@mail.com", "020-1234", nil},
@@ -118,13 +113,13 @@ func (s *testWriterSuite) TestWriteTableData(c *C) {
 	}
 	tableIR := newMockTableIR("test", "employee", data, specCmts, colTypes)
 	err := writer.WriteTableData(tableIR, tableIR, 0)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	p := path.Join(dir, "test.employee.000000000.sql")
 	_, err = os.Stat(p)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	bytes, err := ioutil.ReadFile(p)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	expected := "/*!40101 SET NAMES binary*/;\n" +
 		"/*!40014 SET FOREIGN_KEY_CHECKS=0*/;\n" +
@@ -133,13 +128,14 @@ func (s *testWriterSuite) TestWriteTableData(c *C) {
 		"(2,'female','sarah@mail.com','020-1253','healthy'),\n" +
 		"(3,'male','john@mail.com','020-1256','healthy'),\n" +
 		"(4,'female','sarah@mail.com','020-1235','healthy');\n"
-	c.Assert(string(bytes), Equals, expected)
+	require.Equal(t, expected, string(bytes))
 }
 
-func (s *testWriterSuite) TestWriteTableDataWithFileSize(c *C) {
-	dir := c.MkDir()
+func TestWriteTableDataWithFileSize(t *testing.T) {
+	t.Parallel()
 
-	config := defaultConfigForTest(c)
+	dir := t.TempDir()
+	config := defaultConfigForTest(t)
 	config.OutputDirPath = dir
 	config.FileSize = 50
 	specCmts := []string{
@@ -150,7 +146,8 @@ func (s *testWriterSuite) TestWriteTableDataWithFileSize(c *C) {
 	config.FileSize += uint64(len(specCmts[1]) + 1)
 	config.FileSize += uint64(len("INSERT INTO `employees` VALUES\n"))
 
-	writer := s.newWriter(config, c)
+	writer, clean := createTestWriter(config, t)
+	defer clean()
 
 	data := [][]driver.Value{
 		{"1", "male", "bob@mail.com", "020-1234", nil},
@@ -161,7 +158,7 @@ func (s *testWriterSuite) TestWriteTableDataWithFileSize(c *C) {
 	colTypes := []string{"INT", "SET", "VARCHAR", "VARCHAR", "TEXT"}
 	tableIR := newMockTableIR("test", "employee", data, specCmts, colTypes)
 	err := writer.WriteTableData(tableIR, tableIR, 0)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	cases := map[string]string{
 		"test.employee.000000000.sql": "/*!40101 SET NAMES binary*/;\n" +
@@ -179,17 +176,18 @@ func (s *testWriterSuite) TestWriteTableDataWithFileSize(c *C) {
 	for p, expected := range cases {
 		p = path.Join(dir, p)
 		_, err := os.Stat(p)
-		c.Assert(err, IsNil)
+		require.NoError(t, err)
 		bytes, err := ioutil.ReadFile(p)
-		c.Assert(err, IsNil)
-		c.Assert(string(bytes), Equals, expected)
+		require.NoError(t, err)
+		require.Equal(t, expected, string(bytes))
 	}
 }
 
-func (s *testWriterSuite) TestWriteTableDataWithFileSizeAndRows(c *C) {
-	dir := c.MkDir()
+func TestWriteTableDataWithFileSizeAndRows(t *testing.T) {
+	t.Parallel()
 
-	config := defaultConfigForTest(c)
+	dir := t.TempDir()
+	config := defaultConfigForTest(t)
 	config.OutputDirPath = dir
 	config.FileSize = 50
 	config.Rows = 4
@@ -201,7 +199,8 @@ func (s *testWriterSuite) TestWriteTableDataWithFileSizeAndRows(c *C) {
 	config.FileSize += uint64(len(specCmts[1]) + 1)
 	config.FileSize += uint64(len("INSERT INTO `employees` VALUES\n"))
 
-	writer := s.newWriter(config, c)
+	writer, clean := createTestWriter(config, t)
+	defer clean()
 
 	data := [][]driver.Value{
 		{"1", "male", "bob@mail.com", "020-1234", nil},
@@ -212,7 +211,7 @@ func (s *testWriterSuite) TestWriteTableDataWithFileSizeAndRows(c *C) {
 	colTypes := []string{"INT", "SET", "VARCHAR", "VARCHAR", "TEXT"}
 	tableIR := newMockTableIR("test", "employee", data, specCmts, colTypes)
 	err := writer.WriteTableData(tableIR, tableIR, 0)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	cases := map[string]string{
 		"test.employee.0000000000000.sql": "/*!40101 SET NAMES binary*/;\n" +
@@ -230,25 +229,27 @@ func (s *testWriterSuite) TestWriteTableDataWithFileSizeAndRows(c *C) {
 	for p, expected := range cases {
 		p = path.Join(dir, p)
 		_, err = os.Stat(p)
-		c.Assert(err, IsNil)
+		require.NoError(t, err)
 		bytes, err := ioutil.ReadFile(p)
-		c.Assert(err, IsNil)
-		c.Assert(string(bytes), Equals, expected)
+		require.NoError(t, err)
+		require.Equal(t, expected, string(bytes))
 	}
 }
 
-func (s *testWriterSuite) TestWriteTableDataWithStatementSize(c *C) {
-	dir := c.MkDir()
+func TestWriteTableDataWithStatementSize(t *testing.T) {
+	t.Parallel()
 
-	config := defaultConfigForTest(c)
+	dir := t.TempDir()
+	config := defaultConfigForTest(t)
 	config.OutputDirPath = dir
 	config.StatementSize = 50
 	config.StatementSize += uint64(len("INSERT INTO `employee` VALUES\n"))
 	var err error
 	config.OutputFileTemplate, err = ParseOutputFileTemplate("specified-name")
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
-	writer := s.newWriter(config, c)
+	writer, clean := createTestWriter(config, t)
+	defer clean()
 
 	data := [][]driver.Value{
 		{"1", "male", "bob@mail.com", "020-1234", nil},
@@ -263,7 +264,7 @@ func (s *testWriterSuite) TestWriteTableDataWithStatementSize(c *C) {
 	}
 	tableIR := newMockTableIR("te%/st", "employee", data, specCmts, colTypes)
 	err = writer.WriteTableData(tableIR, tableIR, 0)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 
 	// only with statement size
 	cases := map[string]string{
@@ -280,10 +281,10 @@ func (s *testWriterSuite) TestWriteTableDataWithStatementSize(c *C) {
 	for p, expected := range cases {
 		p = path.Join(config.OutputDirPath, p)
 		_, err = os.Stat(p)
-		c.Assert(err, IsNil)
+		require.NoError(t, err)
 		bytes, err1 := ioutil.ReadFile(p)
-		c.Assert(err1, IsNil)
-		c.Assert(string(bytes), Equals, expected)
+		require.NoError(t, err1)
+		require.Equal(t, expected, string(bytes))
 	}
 
 	// with file size and statement size
@@ -294,11 +295,13 @@ func (s *testWriterSuite) TestWriteTableDataWithStatementSize(c *C) {
 	config.StatementSize += uint64(len("INSERT INTO `employee` VALUES\n"))
 	// test specifying filename format
 	config.OutputFileTemplate, err = ParseOutputFileTemplate("{{.Index}}-{{.Table}}-{{fn .DB}}")
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	err = os.RemoveAll(config.OutputDirPath)
-	c.Assert(err, IsNil)
+	require.NoError(t, err)
 	config.OutputDirPath, err = ioutil.TempDir("", "dumpling")
-	writer = s.newWriter(config, c)
+
+	writer, clean = createTestWriter(config, t)
+	defer clean()
 
 	cases = map[string]string{
 		"000000000-employee-te%25%2Fst.sql": "/*!40101 SET NAMES binary*/;\n" +
@@ -315,14 +318,35 @@ func (s *testWriterSuite) TestWriteTableDataWithStatementSize(c *C) {
 	}
 
 	tableIR = newMockTableIR("te%/st", "employee", data, specCmts, colTypes)
-	c.Assert(writer.WriteTableData(tableIR, tableIR, 0), IsNil)
-	c.Assert(err, IsNil)
+	require.NoError(t, writer.WriteTableData(tableIR, tableIR, 0))
+	require.NoError(t, err)
 	for p, expected := range cases {
 		p = path.Join(config.OutputDirPath, p)
 		_, err = os.Stat(p)
-		c.Assert(err, IsNil)
+		require.NoError(t, err)
 		bytes, err := ioutil.ReadFile(p)
-		c.Assert(err, IsNil)
-		c.Assert(string(bytes), Equals, expected)
+		require.NoError(t, err)
+		require.Equal(t, expected, string(bytes))
+	}
+}
+
+var mu sync.Mutex
+
+func createTestWriter(conf *Config, t *testing.T) (w *Writer, clean func()) {
+	mu.Lock()
+	extStore, err := conf.createExternalStorage(context.Background())
+	mu.Unlock()
+
+	require.NoError(t, err)
+	db, _, err := sqlmock.New()
+	require.NoError(t, err)
+	conn, err := db.Conn(context.Background())
+	require.NoError(t, err)
+
+	w = NewWriter(tcontext.Background(), 0, conf, conn, extStore)
+	clean = func() {
+		require.NoError(t, db.Close())
 	}
+
+	return
 }