Repository: swaggest/rest
Pull request #11 (2020-12-20 00:00:08)

Add shared vars support to test helpers

codecov[bot]: # [Codecov](https://codecov.io/gh/swaggest/rest/pull/11?src=pr&el=h1) Report
> Merging [#11](https://codecov.io/gh/swaggest/rest/pull/11?src=pr&el=desc) (6d75e17) into [master](https://codecov.io/gh/swaggest/rest/commit/012b6dfd73e1339428bd20de36834b54e8e11433?el=desc) (012b6df) will **decrease** coverage by `0.34%`.
> The diff coverage is `64.81%`.

[![Impacted file tree graph](https://codecov.io/gh/swaggest/rest/pull/11/graphs/tree.svg?width=650&height=150&src=pr&token=OpLHxxPdmn)](https://codecov.io/gh/swaggest/rest/pull/11?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #11      +/-   ##
==========================================
- Coverage   68.58%   68.24%   -0.35%     
==========================================
  Files          26       26              
  Lines        1439     1455      +16     
==========================================
+ Hits          987      993       +6     
- Misses        334      340       +6     
- Partials      118      122       +4     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `68.24% <64.81%> (-0.35%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/swaggest/rest/pull/11?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [resttest/server.go](https://codecov.io/gh/swaggest/rest/pull/11/diff?src=pr&el=tree#diff-cmVzdHRlc3Qvc2VydmVyLmdv) | `65.38% <62.74%> (-0.91%)` | :arrow_down: |
| [resttest/client.go](https://codecov.io/gh/swaggest/rest/pull/11/diff?src=pr&el=tree#diff-cmVzdHRlc3QvY2xpZW50Lmdv) | `57.33% <100.00%> (-1.06%)` | :arrow_down: |
| [request/decoder.go](https://codecov.io/gh/swaggest/rest/pull/11/diff?src=pr&el=tree#diff-cmVxdWVzdC9kZWNvZGVyLmdv) | `71.42% <0.00%> (-5.72%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/swaggest/rest/pull/11?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/swaggest/rest/pull/11?src=pr&el=footer). Last update [012b6df...6d75e17](https://codecov.io/gh/swaggest/rest/pull/11?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/_examples/go.mod b/_examples/go.mod
index 58c2cd0..bbf2ed2 100644
--- a/_examples/go.mod
+++ b/_examples/go.mod
@@ -10,11 +10,11 @@ require (
 	github.com/go-chi/chi v4.1.2+incompatible
 	github.com/kelseyhightower/envconfig v1.4.0
 	github.com/stretchr/testify v1.6.1
-	github.com/swaggest/assertjson v1.5.2
+	github.com/swaggest/assertjson v1.6.0
 	github.com/swaggest/jsonschema-go v0.3.13
 	github.com/swaggest/openapi-go v0.2.7
 	github.com/swaggest/rest v0.1.4
-	github.com/swaggest/swgui v1.0.9
+	github.com/swaggest/swgui v1.1.0
 	github.com/swaggest/usecase v0.0.0-20200928062416-27f47131b0f8
 	github.com/valyala/fasthttp v1.17.0
 )
diff --git a/_examples/go.sum b/_examples/go.sum
index 0b467e1..d5c18da 100644
--- a/_examples/go.sum
+++ b/_examples/go.sum
@@ -10,6 +10,8 @@ github.com/bool64/dev v0.1.10 h1:4L6eLD+qo1QgWDy+Y7OhJxi/gLwOAuV1rd07noMc3dU=
 github.com/bool64/dev v0.1.10/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbscdg=
 github.com/bool64/httptestbench v0.1.1 h1:08112fXc5F1XuTWLdC9lPGB4FSWNACoeUWTeoR0a2qA=
 github.com/bool64/httptestbench v0.1.1/go.mod h1:gSmfFacu85Je3u6ZjmLZsFolcv7cwg1fLNUCzq2UUsM=
+github.com/bool64/shared v0.1.0 h1:yE+PJTOjlxOt/wz37HjFiH75Qr3EcpmY1glhUMv3p7k=
+github.com/bool64/shared v0.1.0/go.mod h1:khzw051JtnOZH6lX21rQ+sqbeC0FFEcU1wfchRS7EyU=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -93,6 +95,8 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U=
+github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -116,8 +120,8 @@ github.com/swaggest/assertjson v1.4.1 h1:IpRjS42lEXKHR8C409jGCNDFCm3KCvpTi9LSksa
 github.com/swaggest/assertjson v1.4.1/go.mod h1:v7atNMMlIa5ry2H1rgCHhvz7eho8yfP85UGio0+TFEI=
 github.com/swaggest/assertjson v1.5.0 h1:u4/T3sMCdqmFib0VmWssQSYECAWHKL0SXmRkYH2yLro=
 github.com/swaggest/assertjson v1.5.0/go.mod h1:f1YaHcrOYGbV3AHRwzNRP50QNMwlBXmszbqaBcCASaE=
-github.com/swaggest/assertjson v1.5.2 h1:Mh09tBwx/u20vcgjjGykaGjYvQdLmcG7OOMsGVhW7yg=
-github.com/swaggest/assertjson v1.5.2/go.mod h1:qvyygY256MXpFAWyohBoY+xO4LtQmlV6NfEYANUoARY=
+github.com/swaggest/assertjson v1.6.0 h1:/seQ1631sTRXAURjCNbPebYb+wtxnXhXg1BRIQUb7iU=
+github.com/swaggest/assertjson v1.6.0/go.mod h1:2dZU3TJoGcH+QPrekon0+zG5hJnZUpdEAxiLoxmJALI=
 github.com/swaggest/form v3.6.4+incompatible h1:K0EaZjf8C7cScuZgnJG+Sg0GUGAtjPZivTv1jN+1ni4=
 github.com/swaggest/form v3.6.4+incompatible/go.mod h1:eTJPpSayjnJlOQQbDYsSHlbREbSAtudNW/Y6HouVWr0=
 github.com/swaggest/jsonschema-go v0.2.1/go.mod h1:QFauBdPTrU1UltwocM5FzOWnVjVVtcWkJWG3NlK9sV0=
@@ -127,8 +131,6 @@ github.com/swaggest/jsonschema-go v0.3.13 h1:QKgsj3PrrNZBs68JZ6TJZTFT943AO48oGIs
 github.com/swaggest/jsonschema-go v0.3.13/go.mod h1:INO3Dt0DeyNCbQbqMXNpMQ489BcyRtSb/k62OfmHbpY=
 github.com/swaggest/openapi-go v0.1.3/go.mod h1:Zx4ZgJ7XvlFH9wCOHE7u8RAjLfiHAnCHeaD5kUDujVM=
 github.com/swaggest/openapi-go v0.1.13/go.mod h1:fju3Ka5zb8qBKf4789zLNVUqITydWLDjtPloZhhYHL8=
-github.com/swaggest/openapi-go v0.2.6 h1:fggork4s9h+tFNn6M2ciMqYrpH5upmEOwEHZdg2Y3PY=
-github.com/swaggest/openapi-go v0.2.6/go.mod h1:vJDQ4PLJhrUdNvyuGfQnJOOR+KuCYPCyOjt/3h/yHj8=
 github.com/swaggest/openapi-go v0.2.7 h1:k9t6Dh6zWliMcW8lhluHUOoueOtHE9nPhCwRpF23f/A=
 github.com/swaggest/openapi-go v0.2.7/go.mod h1:uUfCHqDFETNrU5p4k40MdDC+FSWhRl2Wv2FRXyMiNc8=
 github.com/swaggest/refl v0.1.0/go.mod h1:kmYWhxNEvjfRDdMRqpaR/vLULk/SotJs9HFUCIVMK8o=
@@ -141,8 +143,8 @@ github.com/swaggest/swgen v0.6.23 h1:9COrDC1o34xJS0NeQ2js7qGfo/Rgs16xWT0pQRvGNyY
 github.com/swaggest/swgen v0.6.23/go.mod h1:gj2yCLONy3kosKjwRtQeT5O9qqlhUvXAiDnbVwBUNFM=
 github.com/swaggest/swgen v0.6.27 h1:bO3CtX4NvuetPuABMChEEp2N/bjsJraegaJpYZ72aoI=
 github.com/swaggest/swgen v0.6.27/go.mod h1:tQLsoQVLcvtAMzNbm26JbWXDHZM4BBRuI5nemk3gEDw=
-github.com/swaggest/swgui v1.0.9 h1:GxZMOHMIvKWuN3bqLW14+FI5oOfnL35xdbbkt/kWFcc=
-github.com/swaggest/swgui v1.0.9/go.mod h1:b6hEcX7AbKHyhOgQ8DpbdPGKBQLD7lcL1RbxZLherIs=
+github.com/swaggest/swgui v1.1.0 h1:lnJ8nr3jTrN5DyOoV9JoRkyc3x7KPKt8NYeNFfXnhGk=
+github.com/swaggest/swgui v1.1.0/go.mod h1:b6hEcX7AbKHyhOgQ8DpbdPGKBQLD7lcL1RbxZLherIs=
 github.com/swaggest/usecase v0.0.0-20200928062416-27f47131b0f8 h1:XGJJai6ngqYpy/cTMvGreiO+jradIn1uq7Q5hY2OCF4=
 github.com/swaggest/usecase v0.0.0-20200928062416-27f47131b0f8/go.mod h1:rcngDv7OaBXZyEXdEtimcDeNon7sq3iqLm9hxT06s3c=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -179,6 +181,9 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
 golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
+golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -198,8 +203,12 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 h1:+CBz4km/0KPU3RGTwARGh/noP3bEwtHcq+0YcBQM2JQ=
+golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
diff --git a/_examples/task-api/internal/infra/nethttp/router.go b/_examples/task-api/internal/infra/nethttp/router.go
index d364f6f..b1a3d0f 100644
--- a/_examples/task-api/internal/infra/nethttp/router.go
+++ b/_examples/task-api/internal/infra/nethttp/router.go
@@ -1,12 +1,12 @@
 package nethttp
 
 import (
-	"github.com/swaggest/openapi-go/openapi3"
 	"net/http"
 	"time"
 
 	"github.com/go-chi/chi"
 	"github.com/go-chi/chi/middleware"
+	"github.com/swaggest/openapi-go/openapi3"
 	"github.com/swaggest/rest"
 	"github.com/swaggest/rest/_examples/task-api/internal/infra/log"
 	"github.com/swaggest/rest/_examples/task-api/internal/infra/schema"
diff --git a/go.mod b/go.mod
index 0c864ac..28d8cf2 100644
--- a/go.mod
+++ b/go.mod
@@ -4,15 +4,15 @@ go 1.13
 
 require (
 	github.com/bool64/dev v0.1.10
+	github.com/bool64/shared v0.1.0
 	github.com/cespare/xxhash/v2 v2.1.1
 	github.com/go-chi/chi v4.1.2+incompatible
 	github.com/santhosh-tekuri/jsonschema/v2 v2.2.0
 	github.com/stretchr/testify v1.6.1
-	github.com/swaggest/assertjson v1.5.2
+	github.com/swaggest/assertjson v1.6.0
 	github.com/swaggest/form v3.6.4+incompatible
 	github.com/swaggest/openapi-go v0.2.7
 	github.com/swaggest/refl v0.1.6
-	github.com/swaggest/swgen v0.6.27 // indirect
 	github.com/swaggest/usecase v0.0.0-20200928062416-27f47131b0f8
 	gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 4e5879d..43caac2 100644
--- a/go.sum
+++ b/go.sum
@@ -5,6 +5,8 @@ github.com/bool64/dev v0.1.7 h1:1xs4ff6lUFIt7euQCmfjU0Xi7IiSmwcQyMokKdq6qAM=
 github.com/bool64/dev v0.1.7/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbscdg=
 github.com/bool64/dev v0.1.10 h1:4L6eLD+qo1QgWDy+Y7OhJxi/gLwOAuV1rd07noMc3dU=
 github.com/bool64/dev v0.1.10/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbscdg=
+github.com/bool64/shared v0.1.0 h1:yE+PJTOjlxOt/wz37HjFiH75Qr3EcpmY1glhUMv3p7k=
+github.com/bool64/shared v0.1.0/go.mod h1:khzw051JtnOZH6lX21rQ+sqbeC0FFEcU1wfchRS7EyU=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -83,6 +85,8 @@ github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U=
+github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -103,8 +107,8 @@ github.com/swaggest/assertjson v1.4.1 h1:IpRjS42lEXKHR8C409jGCNDFCm3KCvpTi9LSksa
 github.com/swaggest/assertjson v1.4.1/go.mod h1:v7atNMMlIa5ry2H1rgCHhvz7eho8yfP85UGio0+TFEI=
 github.com/swaggest/assertjson v1.5.0 h1:u4/T3sMCdqmFib0VmWssQSYECAWHKL0SXmRkYH2yLro=
 github.com/swaggest/assertjson v1.5.0/go.mod h1:f1YaHcrOYGbV3AHRwzNRP50QNMwlBXmszbqaBcCASaE=
-github.com/swaggest/assertjson v1.5.2 h1:Mh09tBwx/u20vcgjjGykaGjYvQdLmcG7OOMsGVhW7yg=
-github.com/swaggest/assertjson v1.5.2/go.mod h1:qvyygY256MXpFAWyohBoY+xO4LtQmlV6NfEYANUoARY=
+github.com/swaggest/assertjson v1.6.0 h1:/seQ1631sTRXAURjCNbPebYb+wtxnXhXg1BRIQUb7iU=
+github.com/swaggest/assertjson v1.6.0/go.mod h1:2dZU3TJoGcH+QPrekon0+zG5hJnZUpdEAxiLoxmJALI=
 github.com/swaggest/form v3.6.4+incompatible h1:K0EaZjf8C7cScuZgnJG+Sg0GUGAtjPZivTv1jN+1ni4=
 github.com/swaggest/form v3.6.4+incompatible/go.mod h1:eTJPpSayjnJlOQQbDYsSHlbREbSAtudNW/Y6HouVWr0=
 github.com/swaggest/jsonschema-go v0.2.1/go.mod h1:QFauBdPTrU1UltwocM5FzOWnVjVVtcWkJWG3NlK9sV0=
@@ -114,8 +118,6 @@ github.com/swaggest/jsonschema-go v0.3.13 h1:QKgsj3PrrNZBs68JZ6TJZTFT943AO48oGIs
 github.com/swaggest/jsonschema-go v0.3.13/go.mod h1:INO3Dt0DeyNCbQbqMXNpMQ489BcyRtSb/k62OfmHbpY=
 github.com/swaggest/openapi-go v0.1.3/go.mod h1:Zx4ZgJ7XvlFH9wCOHE7u8RAjLfiHAnCHeaD5kUDujVM=
 github.com/swaggest/openapi-go v0.1.13/go.mod h1:fju3Ka5zb8qBKf4789zLNVUqITydWLDjtPloZhhYHL8=
-github.com/swaggest/openapi-go v0.2.6 h1:fggork4s9h+tFNn6M2ciMqYrpH5upmEOwEHZdg2Y3PY=
-github.com/swaggest/openapi-go v0.2.6/go.mod h1:vJDQ4PLJhrUdNvyuGfQnJOOR+KuCYPCyOjt/3h/yHj8=
 github.com/swaggest/openapi-go v0.2.7 h1:k9t6Dh6zWliMcW8lhluHUOoueOtHE9nPhCwRpF23f/A=
 github.com/swaggest/openapi-go v0.2.7/go.mod h1:uUfCHqDFETNrU5p4k40MdDC+FSWhRl2Wv2FRXyMiNc8=
 github.com/swaggest/refl v0.1.0/go.mod h1:kmYWhxNEvjfRDdMRqpaR/vLULk/SotJs9HFUCIVMK8o=
@@ -156,6 +158,9 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
 golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
 golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
+golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -175,8 +180,12 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64=
 golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 h1:+CBz4km/0KPU3RGTwARGh/noP3bEwtHcq+0YcBQM2JQ=
+golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=


Test patch:
diff --git a/resttest/client.go b/resttest/client.go
index 4858b15..5401e55 100644
--- a/resttest/client.go
+++ b/resttest/client.go
@@ -19,6 +19,7 @@ import (
 // Client keeps state of expectations.
 type Client struct {
 	ConcurrencyLevel int
+	JSONComparer     assertjson.Comparer
 
 	baseURL string
 	Headers map[string]string
@@ -55,7 +56,8 @@ func NewClient(baseURL string) *Client {
 	}
 
 	return (&Client{
-		baseURL: baseURL,
+		baseURL:      baseURL,
+		JSONComparer: assertjson.Comparer{IgnoreDiff: assertjson.IgnoreDiff},
 	}).Reset()
 }
 
@@ -349,7 +351,7 @@ func (c *Client) checkBody(expected, received []byte) error {
 			return err
 		}
 
-		err = assertjson.FailNotEqual(expected, received)
+		err = c.JSONComparer.FailNotEqual(expected, received)
 		if err != nil {
 			recCompact, cerr := assertjson.MarshalIndentCompact(json.RawMessage(received), "", " ", 100)
 			if cerr == nil {
diff --git a/resttest/client_test.go b/resttest/client_test.go
index 0aacabf..07e503f 100644
--- a/resttest/client_test.go
+++ b/resttest/client_test.go
@@ -7,6 +7,7 @@ import (
 	"sync/atomic"
 	"testing"
 
+	"github.com/bool64/shared"
 	"github.com/stretchr/testify/assert"
 	"github.com/swaggest/rest/resttest"
 )
@@ -30,14 +31,17 @@ func TestNewClient(t *testing.T) {
 			assert.NoError(t, err)
 		} else {
 			rw.WriteHeader(http.StatusAccepted)
-			_, err := rw.Write([]byte(`{"bar":"foo"}`))
+			_, err := rw.Write([]byte(`{"bar":"foo", "dyn": "abc"}`))
 			assert.NoError(t, err)
 		}
 	}))
 
 	defer srv.Close()
 
+	vars := &shared.Vars{}
+
 	c := resttest.NewClient(srv.URL)
+	c.JSONComparer.Vars = vars
 	c.ConcurrencyLevel = 50
 	c.Headers = map[string]string{
 		"X-Header": "abc",
@@ -48,11 +52,15 @@ func TestNewClient(t *testing.T) {
 		WithHeader("X-Custom", "def").
 		WithContentType("application/json").
 		WithBody([]byte(`{"foo":"bar"}`)).
-		WithPath("/foo?q=1").
+		WithURI("/foo?q=1").
 		Concurrently()
 
 	assert.NoError(t, c.ExpectResponseStatus(http.StatusAccepted))
-	assert.NoError(t, c.ExpectResponseBody([]byte(`{"bar":"foo"}`)))
+	assert.NoError(t, c.ExpectResponseBody([]byte(`{"bar":"foo","dyn":"$var1"}`)))
 	assert.NoError(t, c.ExpectOtherResponsesStatus(http.StatusConflict))
 	assert.NoError(t, c.ExpectOtherResponsesBody([]byte(`{"error":"conflict"}`)))
+
+	val, found := vars.Get("$var1")
+	assert.True(t, found)
+	assert.Equal(t, "abc", val)
 }
diff --git a/resttest/server.go b/resttest/server.go
index a17447b..9e36651 100644
--- a/resttest/server.go
+++ b/resttest/server.go
@@ -2,6 +2,7 @@ package resttest
 
 import (
 	"bytes"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"io/ioutil"
@@ -41,6 +42,9 @@ type ServerMock struct {
 	// DefaultResponseHeaders are added to every response to an expected request.
 	DefaultResponseHeaders map[string]string
 
+	// JSONComparer controls JSON equality check.
+	JSONComparer assertjson.Comparer
+
 	mu           sync.Mutex
 	server       *httptest.Server
 	expectations []Expectation
@@ -50,49 +54,50 @@ type ServerMock struct {
 func NewServerMock() (*ServerMock, string) {
 	m := ServerMock{}
 	m.server = httptest.NewServer(&m)
+	m.JSONComparer = assertjson.Comparer{IgnoreDiff: assertjson.IgnoreDiff}
 
 	return &m, m.server.URL
 }
 
 // Expect adds expected operation.
-func (c *ServerMock) Expect(e Expectation) {
-	c.mu.Lock()
-	defer c.mu.Unlock()
+func (sm *ServerMock) Expect(e Expectation) {
+	sm.mu.Lock()
+	defer sm.mu.Unlock()
 
-	c.expectations = append(c.expectations, e)
+	sm.expectations = append(sm.expectations, e)
 }
 
 // Close closes mocked server.
-func (c *ServerMock) Close() {
-	c.server.Close()
+func (sm *ServerMock) Close() {
+	sm.server.Close()
 }
 
-// ServeHTTP asserts request expectations and serves mocked response.
-func (c *ServerMock) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	c.mu.Lock()
-	defer c.mu.Unlock()
+func (sm *ServerMock) prepareBody(data []byte) ([]byte, error) {
+	if sm.JSONComparer.Vars == nil {
+		return data, nil
+	}
 
-	if len(c.expectations) == 0 {
-		body, err := ioutil.ReadAll(req.Body)
-		if err == nil && len(body) > 0 {
-			c.checkFail(rw, fmt.Errorf("unexpected request received: %s %s, body:\n%s", req.Method,
-				req.RequestURI, string(body)))
-		} else {
-			c.checkFail(rw, fmt.Errorf("unexpected request received: %s %s", req.Method, req.RequestURI))
+	if !json.Valid(data) {
+		return data, nil
+	}
+
+	for k, v := range sm.JSONComparer.Vars.GetAll() {
+		j, err := json.Marshal(v)
+		if err != nil {
+			return nil, err
 		}
 
-		return
+		data = bytes.ReplaceAll(data, []byte(`"`+k+`"`), j)
 	}
 
-	expectation := c.expectations[0]
+	return data, nil
+}
 
-	err := c.checkRequest(req, expectation)
-	if c.checkFail(rw, err) {
-		return
-	}
+func (sm *ServerMock) writeResponse(rw http.ResponseWriter, expectation Expectation) bool {
+	var err error
 
-	if c.DefaultResponseHeaders != nil {
-		for k, v := range c.DefaultResponseHeaders {
+	if sm.DefaultResponseHeaders != nil {
+		for k, v := range sm.DefaultResponseHeaders {
 			rw.Header().Set(k, v)
 		}
 	}
@@ -109,8 +114,41 @@ func (c *ServerMock) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 
 	rw.WriteHeader(expectation.Status)
 
+	expectation.ResponseBody, err = sm.prepareBody(expectation.ResponseBody)
+	if sm.checkFail(rw, err) {
+		return false
+	}
+
 	_, err = rw.Write(expectation.ResponseBody)
-	if c.checkFail(rw, err) {
+
+	return !sm.checkFail(rw, err)
+}
+
+// ServeHTTP asserts request expectations and serves mocked response.
+func (sm *ServerMock) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+	sm.mu.Lock()
+	defer sm.mu.Unlock()
+
+	if len(sm.expectations) == 0 {
+		body, err := ioutil.ReadAll(req.Body)
+		if err == nil && len(body) > 0 {
+			sm.checkFail(rw, fmt.Errorf("unexpected request received: %s %s, body:\n%s", req.Method,
+				req.RequestURI, string(body)))
+		} else {
+			sm.checkFail(rw, fmt.Errorf("unexpected request received: %s %s", req.Method, req.RequestURI))
+		}
+
+		return
+	}
+
+	expectation := sm.expectations[0]
+
+	err := sm.checkRequest(req, expectation)
+	if sm.checkFail(rw, err) {
+		return
+	}
+
+	if !sm.writeResponse(rw, expectation) {
 		return
 	}
 
@@ -120,17 +158,17 @@ func (c *ServerMock) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 
 	if expectation.Repeated > 0 {
 		expectation.Repeated--
-		c.expectations[0] = expectation
+		sm.expectations[0] = expectation
 
 		if expectation.Repeated > 0 {
 			return
 		}
 	}
 
-	c.expectations = c.expectations[1:]
+	sm.expectations = sm.expectations[1:]
 }
 
-func (c *ServerMock) checkRequest(req *http.Request, expectation Expectation) error {
+func (sm *ServerMock) checkRequest(req *http.Request, expectation Expectation) error {
 	if expectation.Method != "" && expectation.Method != req.Method {
 		return fmt.Errorf("method %q expected, %q received", expectation.Method, req.Method)
 	}
@@ -167,7 +205,7 @@ func (c *ServerMock) checkRequest(req *http.Request, expectation Expectation) er
 			return err
 		}
 
-		err := assertjson.FailNotEqual(expectation.RequestBody, reqBody)
+		err := sm.JSONComparer.FailNotEqual(expectation.RequestBody, reqBody)
 		if err != nil {
 			return fmt.Errorf("unexpected request body: %w", err)
 		}
@@ -176,17 +214,17 @@ func (c *ServerMock) checkRequest(req *http.Request, expectation Expectation) er
 	return nil
 }
 
-func (c *ServerMock) checkFail(rw http.ResponseWriter, err error) bool {
+func (sm *ServerMock) checkFail(rw http.ResponseWriter, err error) bool {
 	if err == nil {
 		return false
 	}
 
-	if c.OnError != nil {
-		c.OnError(err)
+	if sm.OnError != nil {
+		sm.OnError(err)
 	}
 
-	if c.ErrorResponder != nil {
-		c.ErrorResponder(rw, err)
+	if sm.ErrorResponder != nil {
+		sm.ErrorResponder(rw, err)
 
 		return true
 	}
@@ -194,35 +232,35 @@ func (c *ServerMock) checkFail(rw http.ResponseWriter, err error) bool {
 	rw.WriteHeader(http.StatusInternalServerError)
 
 	_, err = rw.Write([]byte(err.Error()))
-	if err != nil && c.OnError != nil {
-		c.OnError(err)
+	if err != nil && sm.OnError != nil {
+		sm.OnError(err)
 	}
 
 	return true
 }
 
 // ResetExpectations discards all expectation to reset the state of mock.
-func (c *ServerMock) ResetExpectations() {
-	c.mu.Lock()
-	defer c.mu.Unlock()
+func (sm *ServerMock) ResetExpectations() {
+	sm.mu.Lock()
+	defer sm.mu.Unlock()
 
-	c.expectations = nil
+	sm.expectations = nil
 }
 
 // ExpectationsWereMet checks whether all queued expectations
 // were met in order. If any of them was not met - an error is returned.
-func (c *ServerMock) ExpectationsWereMet() error {
-	c.mu.Lock()
-	defer c.mu.Unlock()
+func (sm *ServerMock) ExpectationsWereMet() error {
+	sm.mu.Lock()
+	defer sm.mu.Unlock()
 
-	if len(c.expectations) != 0 {
-		if len(c.expectations) == 1 && c.expectations[0].Unlimited {
+	if len(sm.expectations) != 0 {
+		if len(sm.expectations) == 1 && sm.expectations[0].Unlimited {
 			return nil
 		}
 
 		unmet := ""
 
-		for _, e := range c.expectations {
+		for _, e := range sm.expectations {
 			if e.Method != "" || e.RequestURI != "" {
 				unmet += ", " + e.Method + " " + e.RequestURI
 			} else {
diff --git a/resttest/server_test.go b/resttest/server_test.go
index 3446224..7df0231 100644
--- a/resttest/server_test.go
+++ b/resttest/server_test.go
@@ -5,9 +5,11 @@ import (
 	"io"
 	"io/ioutil"
 	"net/http"
+	"strings"
 	"sync"
 	"testing"
 
+	"github.com/bool64/shared"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"github.com/swaggest/rest/resttest"
@@ -55,7 +57,7 @@ func assertRoundTrip(t *testing.T, baseURL string, expectation resttest.Expectat
 	}
 }
 
-func TestMock_ServeHTTP(t *testing.T) {
+func TestServerMock_ServeHTTP(t *testing.T) {
 	// Creating REST service mock.
 	mock, baseURL := resttest.NewServerMock()
 	defer mock.Close()
@@ -117,7 +119,7 @@ func TestMock_ServeHTTP(t *testing.T) {
 	})
 }
 
-func TestMock_ServeHTTP_error(t *testing.T) {
+func TestServerMock_ServeHTTP_error(t *testing.T) {
 	// Creating REST service mock.
 	mock, baseURL := resttest.NewServerMock()
 	defer mock.Close()
@@ -175,7 +177,7 @@ func TestMock_ServeHTTP_error(t *testing.T) {
 	assert.Equal(t, `header "X-Foo" with value "bar" expected, "space" received`, string(respBody))
 }
 
-func TestMock_ServeHTTP_concurrency(t *testing.T) {
+func TestServerMock_ServeHTTP_concurrency(t *testing.T) {
 	// Creating REST service mock.
 	mock, url := resttest.NewServerMock()
 	defer mock.Close()
@@ -219,7 +221,7 @@ func TestMock_ServeHTTP_concurrency(t *testing.T) {
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
 
-func TestMock_ResetExpectations(t *testing.T) {
+func TestServerMock_ResetExpectations(t *testing.T) {
 	// Creating REST service mock.
 	mock, _ := resttest.NewServerMock()
 	defer mock.Close()
@@ -234,3 +236,27 @@ func TestMock_ResetExpectations(t *testing.T) {
 	mock.ResetExpectations()
 	assert.NoError(t, mock.ExpectationsWereMet())
 }
+
+func TestServerMock_vars(t *testing.T) {
+	sm, url := resttest.NewServerMock()
+	sm.JSONComparer.Vars = &shared.Vars{}
+	sm.Expect(resttest.Expectation{
+		Method:       http.MethodGet,
+		RequestURI:   "/",
+		RequestBody:  []byte(`{"foo":"bar","dyn":"$var1"}`),
+		ResponseBody: []byte(`{"bar":"foo","dynEcho":"$var1"}`),
+	})
+
+	req, err := http.NewRequest(http.MethodGet, url+"/", strings.NewReader(`{"foo":"bar","dyn":"abc"}`))
+	require.NoError(t, err)
+
+	resp, err := http.DefaultTransport.RoundTrip(req)
+	require.NoError(t, err)
+
+	body, err := ioutil.ReadAll(resp.Body)
+	require.NoError(t, err)
+
+	require.NoError(t, resp.Body.Close())
+
+	assert.Equal(t, `{"bar":"foo","dynEcho":"abc"}`, string(body))
+}