├── .gitignore
├── Go Test 从入门到躺平.pdf
├── LICENSE
├── README.md
├── fail_test.go
├── go.mod
├── go.sum
├── s0
├── bfe.html
├── bfe.svg
├── grpc.html
└── grpc.svg
├── s1
├── arith.go
├── arith_test.go
├── cover.html
├── cover2.html
├── hello.go
├── hello_test.go
└── out.svg
├── s10
├── reverse.go
├── reverse_test.go
└── testdata
│ └── fuzz
│ └── FuzzReverse
│ └── 6bda972cf8cbff20524e6ebca2a8be89e2d88b246a7ae067174a068f9ea04380
├── s2
├── cleanup_test.go
├── fail_test.go
├── helper_test.go
├── log_test.go
├── main_test.go
├── parallel_test.go
└── skip_test.go
├── s4
├── cover.html
├── cover.sh
├── cover2.html
├── cover3.html
├── covermode.go
├── covermode.html
├── covermode_test.go
└── out.svg
├── s5
├── assert_require_test.go
└── gocovery_test.go
├── s6
├── container
│ ├── kafka_test.go
│ ├── redis_test.go
│ └── testdata
│ │ └── docker-compose.yml
├── gnomock
│ ├── kafka_test.go
│ └── redis_test.go
├── httptest
│ ├── handler_test.go
│ └── server_test.go
├── mock
│ ├── io_test.go
│ └── rw_test.go
├── mockey
│ └── mock_test.go
└── monkey
│ └── hello_test.go
├── s7
└── book
│ ├── api
│ ├── book.go
│ ├── book_monkey_test.go
│ ├── book_test.go
│ └── test-bin
│ ├── dao
│ ├── book.go
│ └── book_test.go
│ ├── model
│ └── book.go
│ ├── service
│ ├── book.go
│ └── book_test.go
│ ├── testdata
│ └── test.db
│ └── util
│ ├── db.go
│ └── db_test.go
├── s8
├── benchmark_hash_test.go
├── benchmark_parallel_test.go
├── benchstat.txt
├── benchstat_test.go
├── new.txt
└── old.txt
└── s9
└── example_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 |
--------------------------------------------------------------------------------
/Go Test 从入门到躺平.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smallnest/go_test_workshop/27f12b63da3545c6ea5ae94b9287c3f5b0da5137/Go Test 从入门到躺平.pdf
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 smallnest
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go_test_workshop
2 |
3 | 如何编写健壮的代码?测试或者单元测试是一个非常有效的手段。自1997年JUint的出世引领单元测试以来,单元测试曾经风靡一时。然后在最近的十年,或者说在最近的几年,互联网企业面临巨大的竞争压力,或者说代码规范不收重视的情况下,糙快猛的编程方式也收到吹捧,以至于单元测试没有收到足够的重视,因此留下来很多的技术债务和线上风险,也就是俗称的"雷"。
4 |
5 | 这个实战课程,全面介绍Go语言的单元测试的知识,通过大量的实战的代码,带你领略go test的便捷和强大,解答你在实际开发中的众多的疑惑,你在以后的Go应用程序的开发中,可以应用本文介绍的知识,把单元测试威力实际发挥出来。
6 |
7 | 如果你会写`func TestXXX(t *testing.T) {`这一行代码,恭喜你,你已经知道了Go的一半的单元测试的知识,你可以重点关注其它的一半的Go单元测试的知识。如果你还未写过Go的单元测试,没关系,本门课程手把手教你怎么轻轻松松编写Go的单元测试。
8 |
9 |
10 | 本课程的代码放在了 [Go Test Workshop](https://github.com/smallnest/go_test_workshop)
11 |
12 | 
13 |
14 | - s1: go test basic
15 | - [table driven tests ](https://github.com/golang/go/wiki/TableDrivenTests)
16 | - [color output](https://github.com/rakyll/gotest)
17 | - gotest -v ./...
18 |
19 | - s2: testing.T
20 | - testing.TB interface
21 | - testing.T
22 | - Fail/FailNow/Failed/Fatal/Fatalf
23 | - Log/Logf/Error/Errorf
24 | - Skip/SkipNow/Skipf/Skipped
25 | - Helper
26 | - Parallel
27 |
28 |
29 | - s3: go test command
30 | - go help test
31 | - go test [build/test flags] [packages] [build/test flags & test binary flags]
32 | - local directory mode: cache disabled
33 | - package list mode: cache enabled. `-count=1` to disable cache
34 | - test binary flags
35 | - -c: Compile the test binary to pkg.test but do not run it
36 | - -exec xprog
37 | - -json: Convert test output to JSON suitable for automated processing.
38 | - -o file:Compile the test binary to the named file.
39 |
40 | - go help testflag
41 | - -count n: Run each test, benchmark, and fuzz seed n times (default 1).
42 | - -cpu 1,2,4: Specify a list of GOMAXPROCS values for which the tests, benchmarks or fuzz tests should be executed.
43 | - -failfast: Do not start new tests after the first test failure.
44 | - -json
45 | - -list regexp
46 | - -parallel n
47 | - -run regexp
48 | - -short
49 | - -shuffle off,on,N
50 | - -timeout d
51 | - -v
52 | - -vet list
53 |
54 |
55 | - go help testfunc
56 | - func TestXxx(t *testing.T) { ... }
57 | - func BenchmarkXxx(b *testing.B) { ... }
58 | - func FuzzXxx(f *testing.F) { ... }
59 | - func ExamplePrintln() { ... }
60 |
61 | - https://pkg.go.dev/testing
62 |
63 | - s4: coverage
64 | - go test -coverprofile cover.out ./...
65 | - go-cover-treemap -coverprofile cover.out > out.svg
66 | - https://github.com/microsoft/vscode-go/issues/816
67 | - https://golang.org/doc/go1.10#test
68 | - go tool cover -html=cover.out -o cover.html
69 | - go tool cover -o cover2.html -html=cover.out; sed -i 's/black/whitesmoke/g' cover2.html; open cover2.html
70 | - mac: go tool cover -o cover2.html -html=cover.out; sed -i'*.bak' 's/black/whitesmoke/g' cover2.html; rm -fr cover2.html*.bak;open cover2.html
71 |
72 |
73 | - s5: tools
74 | - assert/require
75 | - gocovery: Behavior-driven Development
76 |
77 | - s6: mock
78 | - https://github.com/golang/mock
79 | - https://github.com/vektra/mockery
80 | - https://github.com/DATA-DOG/go-sqlmock
81 | - https://github.com/h2non/gock
82 | - https://github.com/gavv/httpexpect
83 | - https://github.com/steinfletcher/apitest
84 | - https://github.com/carlmjohnson/be
85 | - https://github.com/arikama/go-mysql-test-container
86 | - https://github.com/jfilipczyk/gomatch
87 |
88 | - monkey
89 | - https://github.com/bouk/monkey
90 | - https://github.com/agiledragon/gomonkey
91 | - printf '\x07' | dd of=api.test bs=1 seek=160 count=1 conv=notrunc
92 | - https://github.com/eisenxp/macos-golink-wrapper
93 | - container
94 | - https://github.com/testcontainers/testcontainers-go
95 |
96 | - s7: web case
97 | - dao
98 | - service
99 | - http
100 |
101 | - s8: benchmark
102 |
103 | - s9: example
104 |
105 | - s10: fuzzing test
106 |
107 |
108 |
109 | ## install tools
110 |
111 | ```
112 | go install github.com/rakyll/gotest@master
113 | go install github.com/axw/gocov/gocov@latest
114 | go install github.com/matm/gocov-html@latest
115 | go install github.com/nikolaydubina/go-cover-treemap@latest
116 | // https://go-cover-treemap.io/
117 | ```
--------------------------------------------------------------------------------
/fail_test.go:
--------------------------------------------------------------------------------
1 | package s2
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func TestFail(t *testing.T) {
9 | t.Fail()
10 | t.Log("after Fail")
11 | t.FailNow()
12 | t.Log("after FailNow")
13 | }
14 |
15 | func TestFatal(t *testing.T) {
16 | t.Fatal("fataled")
17 | t.Log("after Fatal")
18 | }
19 |
20 | func TestFatalf(t *testing.T) {
21 | t.Fatalf("there is: %v", "Fatalf")
22 | t.Log("after Fatalf")
23 | }
24 |
25 | func TestFailNowInAnotherGoroutine(t *testing.T) {
26 | go func() {
27 | t.FailNow()
28 | t.Log("after FailNow in another goroutine")
29 | }()
30 |
31 | time.Sleep(time.Second)
32 | t.Log("after one second")
33 | }
34 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/smallnest/go_test_workshop
2 |
3 | go 1.22
4 |
5 | require (
6 | github.com/DATA-DOG/go-sqlmock v1.5.0
7 | github.com/agiledragon/gomonkey/v2 v2.11.0
8 | github.com/cespare/xxhash v1.1.0
9 | github.com/creachadair/cityhash v0.1.1
10 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13
11 | github.com/go-redis/redis v6.15.9+incompatible
12 | github.com/go-redis/redis/v7 v7.4.1
13 | github.com/golang/mock v1.6.0
14 | github.com/google/uuid v1.3.0
15 | github.com/leemcloughlin/gofarmhash v0.0.0-20160919192320-0a055c5b87a8
16 | github.com/mattn/go-sqlite3 v1.14.22
17 | github.com/minio/highwayhash v1.0.2
18 | github.com/orlangure/gnomock v0.21.1-0.20220731072318-def3e4eb83fa
19 | github.com/pierrec/xxHash v0.1.5
20 | github.com/segmentio/kafka-go v0.4.33
21 | github.com/smartystreets/goconvey v1.6.4
22 | github.com/spaolacci/murmur3 v1.1.0
23 | github.com/stretchr/testify v1.9.0
24 | github.com/testcontainers/testcontainers-go v0.13.0
25 | )
26 |
27 | require (
28 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
29 | github.com/Microsoft/go-winio v0.5.2 // indirect
30 | github.com/Microsoft/hcsshim v0.8.23 // indirect
31 | github.com/bytedance/mockey v1.2.10 // indirect
32 | github.com/cenkalti/backoff/v4 v4.1.2 // indirect
33 | github.com/containerd/cgroups v1.0.1 // indirect
34 | github.com/containerd/containerd v1.5.9 // indirect
35 | github.com/davecgh/go-spew v1.1.1 // indirect
36 | github.com/docker/distribution v2.8.1+incompatible // indirect
37 | github.com/docker/docker v20.10.17+incompatible // indirect
38 | github.com/docker/go-connections v0.4.0 // indirect
39 | github.com/docker/go-units v0.4.0 // indirect
40 | github.com/gogo/protobuf v1.3.2 // indirect
41 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
42 | github.com/golang/protobuf v1.5.2 // indirect
43 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
44 | github.com/jtolds/gls v4.20.0+incompatible // indirect
45 | github.com/klauspost/compress v1.15.7 // indirect
46 | github.com/magiconair/properties v1.8.5 // indirect
47 | github.com/moby/sys/mount v0.2.0 // indirect
48 | github.com/moby/sys/mountinfo v0.5.0 // indirect
49 | github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
50 | github.com/morikuni/aec v1.0.0 // indirect
51 | github.com/onsi/ginkgo v1.16.5 // indirect
52 | github.com/onsi/gomega v1.18.1 // indirect
53 | github.com/opencontainers/go-digest v1.0.0 // indirect
54 | github.com/opencontainers/image-spec v1.0.2 // indirect
55 | github.com/opencontainers/runc v1.0.2 // indirect
56 | github.com/pierrec/lz4/v4 v4.1.15 // indirect
57 | github.com/pkg/errors v0.9.1 // indirect
58 | github.com/pmezard/go-difflib v1.0.0 // indirect
59 | github.com/sirupsen/logrus v1.9.0 // indirect
60 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
61 | go.opencensus.io v0.22.3 // indirect
62 | go.uber.org/atomic v1.10.0 // indirect
63 | go.uber.org/multierr v1.8.0 // indirect
64 | go.uber.org/zap v1.22.0 // indirect
65 | golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff // indirect
66 | golang.org/x/net v0.0.0-20220811182439-13a9a731de15 // indirect
67 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
68 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
69 | google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect
70 | google.golang.org/grpc v1.33.2 // indirect
71 | google.golang.org/protobuf v1.28.0 // indirect
72 | gopkg.in/yaml.v3 v3.0.1 // indirect
73 | )
74 |
--------------------------------------------------------------------------------
/s0/grpc.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 | google.golang.org/grpc
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | authz
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | balancer
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | balancer_conn_wrappers.go
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | benchmark
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | channelz/service
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | clientconn.go
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | codes
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | credentials
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | dialoptions.go
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 | grpclog
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | health
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 | internal
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 | metadata/metadata.go
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | picker_wrapper.go
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 | pickfirst.go
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 | reflection/serverreflection.go
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 | resolver_conn_wrapper.go
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 | rpc_util.go
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 | server.go
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 | service_config.go
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 | stream.go
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 | test
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 | xds
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 | rbac_translator.go
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 | base
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 | grpclb
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 | rls
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 | weightedtarget
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 | latency/latency.go
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 | service.go
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 | alts/internal
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 | oauth/oauth.go
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 | tls.go
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 | tls/certprovider
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 | xds/xds.go
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 | balancer/gracefulswitch/gracefulswitch.go
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 | balancergroup/balancergroup.go
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 | binarylog
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 | credentials
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 | grpctest
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 | metadata/metadata.go
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 | profiling
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 | resolver
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 | serviceconfig/serviceconfig.go
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 | testutils
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 | transport
820 |
821 |
822 |
823 |
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
832 |
833 | xds
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 | bufconn/bufconn.go
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 | servertester.go
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 | internal
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 |
936 |
937 |
938 |
939 | grpclb.go
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 | grpclb_remote_balancer.go
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 | balancer.go
989 |
990 |
991 |
992 |
993 |
994 |
995 |
996 |
997 |
998 |
999 |
1000 |
1001 |
1002 | config.go
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 | internal
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 | picker.go
1040 |
1041 |
1042 |
1043 |
1044 |
1045 |
1046 |
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 | conn
1059 |
1060 |
1061 |
1062 |
1063 |
1064 |
1065 |
1066 | handshaker
1072 |
1073 |
1074 |
1075 |
1076 |
1077 |
1078 |
1079 |
1080 |
1081 |
1082 |
1083 |
1084 |
1085 |
1086 |
1087 |
1088 |
1089 |
1090 |
1091 |
1092 |
1093 |
1094 |
1095 |
1096 |
1097 | pemfile
1103 |
1104 |
1105 |
1106 |
1107 |
1108 |
1109 |
1110 |
1111 |
1112 |
1113 |
1114 |
1115 |
1116 |
1117 |
1118 |
1119 |
1120 |
1121 |
1122 | env_config.go
1128 |
1129 |
1130 |
1131 |
1132 |
1133 |
1134 |
1135 | method_logger.go
1141 |
1142 |
1143 |
1144 |
1145 |
1146 |
1147 |
1148 |
1149 |
1150 |
1151 |
1152 |
1153 |
1154 |
1155 |
1156 |
1157 |
1158 |
1159 |
1160 |
1161 |
1162 |
1163 |
1164 |
1165 |
1166 | xds/handshake_info.go
1172 |
1173 |
1174 |
1175 |
1176 |
1177 |
1178 |
1179 |
1180 |
1181 |
1182 |
1183 |
1184 |
1185 |
1186 |
1187 |
1188 |
1189 |
1190 |
1191 |
1192 |
1193 |
1194 |
1195 |
1196 |
1197 |
1198 |
1199 |
1200 |
1201 |
1202 |
1203 |
1204 |
1205 |
1206 |
1207 |
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 | dns/dns_resolver.go
1221 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 |
1228 | balancer.go
1234 |
1235 |
1236 |
1237 |
1238 |
1239 |
1240 |
1241 |
1242 |
1243 |
1244 |
1245 |
1246 |
1247 |
1248 |
1249 |
1250 |
1251 |
1252 |
1253 |
1254 |
1255 |
1256 |
1257 |
1258 |
1259 |
1260 |
1261 |
1262 |
1263 |
1264 |
1265 |
1266 |
1267 |
1268 |
1269 |
1270 |
1271 |
1272 |
1273 |
1274 |
1275 |
1276 |
1277 |
1278 |
1279 |
1280 |
1281 |
1282 |
1283 |
1284 |
1285 |
1286 |
1287 |
1288 |
1289 | controlbuf.go
1295 |
1296 |
1297 |
1298 |
1299 |
1300 |
1301 |
1302 | flowcontrol.go
1308 |
1309 |
1310 |
1311 |
1312 |
1313 |
1314 |
1315 | handler_server.go
1321 |
1322 |
1323 |
1324 |
1325 |
1326 |
1327 |
1328 | http2_client.go
1334 |
1335 |
1336 |
1337 |
1338 |
1339 |
1340 |
1341 | http2_server.go
1347 |
1348 |
1349 |
1350 |
1351 |
1352 |
1353 |
1354 | http_util.go
1360 |
1361 |
1362 |
1363 |
1364 |
1365 |
1366 |
1367 |
1368 |
1369 |
1370 |
1371 |
1372 |
1373 | transport.go
1379 |
1380 |
1381 |
1382 |
1383 |
1384 |
1385 |
1386 |
1387 |
1388 |
1389 |
1390 |
1391 |
1392 |
1393 |
1394 |
1395 |
1396 |
1397 |
1398 |
1399 |
1400 |
1401 |
1402 |
1403 |
1404 | rbac
1410 |
1411 |
1412 |
1413 |
1414 |
1415 |
1416 |
1417 |
1418 |
1419 |
1420 |
1421 |
1422 |
1423 |
1424 |
1425 |
1426 |
1427 |
1428 |
1429 | balancer
1435 |
1436 |
1437 |
1438 |
1439 |
1440 |
1441 |
1442 |
1443 |
1444 |
1445 |
1446 |
1447 |
1448 | httpfilter/fault/fault.go
1454 |
1455 |
1456 |
1457 |
1458 |
1459 |
1460 |
1461 |
1462 |
1463 |
1464 |
1465 |
1466 |
1467 | resolver
1473 |
1474 |
1475 |
1476 |
1477 |
1478 |
1479 |
1480 | server
1486 |
1487 |
1488 |
1489 |
1490 |
1491 |
1492 |
1493 | test/e2e
1499 |
1500 |
1501 |
1502 |
1503 |
1504 |
1505 |
1506 |
1507 |
1508 |
1509 |
1510 |
1511 |
1512 | xdsclient
1518 |
1519 |
1520 |
1521 |
1522 |
1523 |
1524 |
1525 |
1526 |
1527 |
1528 |
1529 |
1530 |
1531 |
1532 |
1533 |
1534 |
1535 |
1536 |
1537 |
1538 |
1539 |
1540 |
1541 |
1542 |
1543 |
1544 |
1545 |
1546 |
1547 |
1548 |
1549 |
1550 |
1551 |
1552 |
1553 |
1554 |
1555 |
1556 |
1557 |
1558 |
1559 |
1560 |
1561 |
1562 |
1563 |
1564 |
1565 |
1566 |
1567 |
1568 |
1569 |
1570 |
1571 |
1572 |
1573 |
1574 |
1575 |
1576 |
1577 |
1578 |
1579 |
1580 |
1581 |
1582 |
1583 |
1584 |
1585 |
1586 |
1587 |
1588 |
1589 |
1590 |
1591 | cdsbalancer
1597 |
1598 |
1599 |
1600 |
1601 |
1602 |
1603 |
1604 | clusterimpl
1610 |
1611 |
1612 |
1613 |
1614 |
1615 |
1616 |
1617 | clustermanager
1623 |
1624 |
1625 |
1626 |
1627 |
1628 |
1629 |
1630 | clusterresolver
1636 |
1637 |
1638 |
1639 |
1640 |
1641 |
1642 |
1643 |
1644 |
1645 |
1646 |
1647 |
1648 |
1649 | outlierdetection
1655 |
1656 |
1657 |
1658 |
1659 |
1660 |
1661 |
1662 | priority
1668 |
1669 |
1670 |
1671 |
1672 |
1673 |
1674 |
1675 | ringhash
1681 |
1682 |
1683 |
1684 |
1685 |
1686 |
1687 |
1688 |
1689 |
1690 |
1691 |
1692 |
1693 |
1694 | watch_service.go
1700 |
1701 |
1702 |
1703 |
1704 |
1705 |
1706 |
1707 | xds_resolver.go
1713 |
1714 |
1715 |
1716 |
1717 |
1718 |
1719 |
1720 |
1721 |
1722 |
1723 |
1724 |
1725 |
1726 |
1727 |
1728 |
1729 |
1730 |
1731 |
1732 |
1733 |
1734 |
1735 |
1736 |
1737 |
1738 |
1739 |
1740 |
1741 |
1742 |
1743 |
1744 |
1745 |
1746 |
1747 |
1748 |
1749 |
1750 |
1751 |
1752 |
1753 |
1754 |
1755 |
1756 | bootstrap
1762 |
1763 |
1764 |
1765 |
1766 |
1767 |
1768 |
1769 |
1770 |
1771 |
1772 |
1773 |
1774 |
1775 |
1776 |
1777 |
1778 |
1779 |
1780 |
1781 | clientimpl_authority.go
1787 |
1788 |
1789 |
1790 |
1791 |
1792 |
1793 |
1794 |
1795 |
1796 |
1797 |
1798 |
1799 |
1800 |
1801 |
1802 |
1803 |
1804 |
1805 |
1806 |
1807 |
1808 |
1809 |
1810 |
1811 |
1812 |
1813 |
1814 |
1815 |
1816 |
1817 |
1818 | controller
1824 |
1825 |
1826 |
1827 |
1828 |
1829 |
1830 |
1831 | load/store.go
1837 |
1838 |
1839 |
1840 |
1841 |
1842 |
1843 |
1844 |
1845 |
1846 |
1847 |
1848 |
1849 |
1850 |
1851 |
1852 |
1853 |
1854 |
1855 |
1856 | xdsresource
1862 |
1863 |
1864 |
1865 |
1866 |
1867 |
1868 |
1869 | cdsbalancer.go
1875 |
1876 |
1877 |
1878 |
1879 |
1880 |
1881 |
1882 |
1883 |
1884 |
1885 |
1886 |
1887 |
1888 |
1889 |
1890 |
1891 |
1892 |
1893 |
1894 |
1895 |
1896 |
1897 |
1898 |
1899 |
1900 |
1901 |
1902 |
1903 |
1904 |
1905 |
1906 |
1907 |
1908 |
1909 |
1910 |
1911 |
1912 |
1913 |
1914 |
1915 |
1916 |
1917 |
1918 |
1919 |
1920 |
1921 |
1922 |
1923 |
1924 | configbuilder.go
1930 |
1931 |
1932 |
1933 |
1934 |
1935 |
1936 |
1937 |
1938 |
1939 |
1940 |
1941 |
1942 |
1943 |
1944 |
1945 |
1946 |
1947 |
1948 |
1949 |
1950 |
1951 |
1952 |
1953 |
1954 |
1955 |
1956 |
1957 |
1958 |
1959 |
1960 |
1961 |
1962 |
1963 |
1964 |
1965 |
1966 |
1967 |
1968 |
1969 |
1970 |
1971 |
1972 |
1973 |
1974 |
1975 |
1976 |
1977 |
1978 |
1979 |
1980 |
1981 |
1982 |
1983 |
1984 |
1985 |
1986 |
1987 |
1988 |
1989 |
1990 |
1991 |
1992 |
1993 |
1994 |
1995 |
1996 |
1997 |
1998 |
1999 |
2000 |
2001 |
2002 |
2003 |
2004 |
2005 |
2006 |
2007 |
2008 |
2009 |
2010 |
2011 |
2012 |
2013 |
2014 |
2015 | bootstrap.go
2021 |
2022 |
2023 |
2024 |
2025 |
2026 |
2027 |
2028 |
2029 |
2030 |
2031 |
2032 |
2033 |
2034 |
2035 |
2036 |
2037 |
2038 |
2039 |
2040 |
2041 |
2042 |
2043 |
2044 |
2045 |
2046 | filter_chain.go
2052 |
2053 |
2054 |
2055 |
2056 |
2057 |
2058 |
2059 | matcher.go
2065 |
2066 |
2067 |
2068 |
2069 |
2070 |
2071 |
2072 |
2073 |
2074 |
2075 |
2076 |
2077 |
2078 |
2079 |
2080 |
2081 |
2082 |
2083 |
2084 |
2085 |
2086 |
2087 |
2088 |
2089 |
2090 |
2091 |
2092 |
2093 |
2094 |
2095 |
2096 | unmarshal.go
2102 |
2103 |
2104 |
2105 |
2106 |
2107 |
2108 |
2109 | unmarshal_cds.go
2115 |
2116 |
2117 |
2118 |
2119 |
2120 |
2121 |
2122 | unmarshal_eds.go
2128 |
2129 |
2130 |
2131 |
2132 |
2133 |
2134 |
2135 |
2136 |
2137 |
2138 |
2139 |
2140 |
2141 | unmarshal_rds.go
2147 |
2148 |
2149 |
2150 |
--------------------------------------------------------------------------------
/s1/arith.go:
--------------------------------------------------------------------------------
1 | package s1
2 |
3 | func Add(a, b int) int {
4 | return a + b
5 | }
6 |
7 | func Sub(a, b int) int {
8 | return a - b
9 | }
10 |
11 | func Mul(a, b int) int {
12 | return a * b
13 | }
14 |
15 | func Div(a, b int) int {
16 | return a / b
17 | }
18 |
--------------------------------------------------------------------------------
/s1/arith_test.go:
--------------------------------------------------------------------------------
1 | package s1
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestAdd(t *testing.T) {
8 | type args struct {
9 | a int
10 | b int
11 | }
12 | tests := []struct {
13 | name string
14 | args args
15 | want int
16 | }{
17 | {"普通的两个数相加", args{10, 20}, 30},
18 | {"一个参数是0", args{10, 0}, 10},
19 | }
20 | for _, tt := range tests {
21 | t.Run(tt.name, func(t *testing.T) {
22 | if got := Add(tt.args.a, tt.args.b); got != tt.want {
23 | t.Errorf("Add() = %v, want %v", got, tt.want)
24 | }
25 | })
26 | }
27 | }
28 |
29 | func TestAdd2(t *testing.T) {
30 | v := Add(1, 2)
31 | if v != 3 {
32 | t.Errorf("expect 3 but got %d", v)
33 | }
34 | }
35 |
36 | func TestSub(t *testing.T) {
37 | type args struct {
38 | a int
39 | b int
40 | }
41 | tests := []struct {
42 | name string
43 | args args // int
44 | want int // out
45 | }{
46 | {"普通的两个数相减", args{10, 20}, -10},
47 | {"一个参数是0", args{10, 0}, 10},
48 | }
49 | for _, tt := range tests {
50 | t.Run(tt.name, func(t *testing.T) {
51 | if got := Sub(tt.args.a, tt.args.b); got != tt.want {
52 | t.Errorf("Sub() = %v, want %v", got, tt.want)
53 | }
54 | })
55 | }
56 | }
57 |
58 | func TestMul(t *testing.T) {
59 | type args struct {
60 | a int
61 | b int
62 | }
63 | tests := []struct {
64 | name string
65 | args args
66 | want int
67 | }{
68 | {"普通的两个数相乘", args{10, 20}, 200},
69 | {"一个参数是0", args{10, 0}, 0},
70 | }
71 | for _, tt := range tests {
72 | t.Run(tt.name, func(t *testing.T) {
73 | if got := Mul(tt.args.a, tt.args.b); got != tt.want {
74 | t.Errorf("Mul() = %v, want %v", got, tt.want)
75 | }
76 | })
77 | }
78 | }
79 |
80 | func TestDiv(t *testing.T) {
81 | type args struct {
82 | a int
83 | b int
84 | }
85 | tests := []struct {
86 | name string
87 | args args
88 | want int
89 | }{
90 | {"普通的两个数相除", args{10, 20}, 0},
91 | // {"一个参数是0", args{10, 0}, int(math.NaN())},
92 | }
93 | for _, tt := range tests {
94 | t.Run(tt.name, func(t *testing.T) {
95 | if got := Div(tt.args.a, tt.args.b); got != tt.want {
96 | t.Errorf("Div() = %v, want %v", got, tt.want)
97 | }
98 | })
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/s1/cover.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | s1: Go Coverage Report
7 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | github.com/smallnest/go_test_workshop/s1/arith.go (100.0%)
59 |
60 | github.com/smallnest/go_test_workshop/s1/hello.go (100.0%)
61 |
62 |
63 |
64 |
65 | not tracked
66 |
67 | not covered
68 | covered
69 |
70 |
71 |
72 |
73 |
74 |
package s1
75 |
76 | func Add(a, b int) int {
77 | return a + b
78 | }
79 |
80 | func Sub(a, b int) int {
81 | return a - b
82 | }
83 |
84 | func Mul(a, b int) int {
85 | return a * b
86 | }
87 |
88 | func Div(a, b int) int {
89 | return a / b
90 | }
91 |
92 |
93 |
package s1
94 |
95 | func greet(s string) string {
96 | return "hello " + s
97 | }
98 |
99 |
100 |
101 |
102 |
129 |
130 |
--------------------------------------------------------------------------------
/s1/cover2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | s1: Go Coverage Report
7 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | github.com/smallnest/go_test_workshop/s1/arith.go (100.0%)
59 |
60 | github.com/smallnest/go_test_workshop/s1/hello.go (100.0%)
61 |
62 |
63 |
64 |
65 | not tracked
66 |
67 | not covered
68 | covered
69 |
70 |
71 |
72 |
73 |
74 |
package s1
75 |
76 | func Add(a, b int) int {
77 | return a + b
78 | }
79 |
80 | func Sub(a, b int) int {
81 | return a - b
82 | }
83 |
84 | func Mul(a, b int) int {
85 | return a * b
86 | }
87 |
88 | func Div(a, b int) int {
89 | return a / b
90 | }
91 |
92 |
93 |
package s1
94 |
95 | func greet(s string) string {
96 | return "hello " + s
97 | }
98 |
99 |
100 |
101 |
102 |
129 |
130 |
--------------------------------------------------------------------------------
/s1/hello.go:
--------------------------------------------------------------------------------
1 | package s1
2 |
3 | func greet(s string) string {
4 | return "hello " + s
5 | }
6 |
--------------------------------------------------------------------------------
/s1/hello_test.go:
--------------------------------------------------------------------------------
1 | // package s1_test
2 |
3 | package s1
4 |
5 | import "testing"
6 |
7 | func TestGreet(t *testing.T) {
8 | s := greet("test")
9 |
10 | if s != "hello test" {
11 | t.Errorf("want 'hello test' but got %s", s)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/s1/out.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 | github.com/smallnest/go_test_workshop/s1
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | arith.go
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | hello.go
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/s10/reverse.go:
--------------------------------------------------------------------------------
1 | package s10
2 |
3 | func Reverse(s string) string {
4 | b := []byte(s)
5 | for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 {
6 | b[i], b[j] = b[j], b[i]
7 | }
8 | return string(b)
9 | }
10 |
--------------------------------------------------------------------------------
/s10/reverse_test.go:
--------------------------------------------------------------------------------
1 | package s10
2 |
3 | import (
4 | "testing"
5 | "unicode/utf8"
6 | )
7 |
8 | func TestReverse(t *testing.T) {
9 | testcases := []struct {
10 | in, want string
11 | }{
12 | {"Hello, world", "dlrow ,olleH"},
13 | {" ", " "},
14 | {"!12345", "54321!"},
15 | }
16 | for _, tc := range testcases {
17 | rev := Reverse(tc.in)
18 | if rev != tc.want {
19 | t.Errorf("Reverse: %q, want %q", rev, tc.want)
20 | }
21 | }
22 | }
23 |
24 | func FuzzReverse(f *testing.F) {
25 | testcases := []string{"Hello, world", " ", "!12345"}
26 | for _, tc := range testcases {
27 | f.Add(tc) // Use f.Add to provide a seed corpus
28 | }
29 | f.Fuzz(func(t *testing.T, orig string) {
30 | rev := Reverse(orig)
31 | doubleRev := Reverse(rev)
32 | if orig != doubleRev {
33 | t.Errorf("Before: %q, after: %q", orig, doubleRev)
34 | }
35 | if utf8.ValidString(orig) && !utf8.ValidString(rev) {
36 | t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
37 | }
38 | })
39 | }
40 |
--------------------------------------------------------------------------------
/s10/testdata/fuzz/FuzzReverse/6bda972cf8cbff20524e6ebca2a8be89e2d88b246a7ae067174a068f9ea04380:
--------------------------------------------------------------------------------
1 | go test fuzz v1
2 | string("ۯ")
3 |
--------------------------------------------------------------------------------
/s2/cleanup_test.go:
--------------------------------------------------------------------------------
1 | package s2
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | "path/filepath"
7 | "testing"
8 | )
9 |
10 | func TestWrite(t *testing.T) {
11 | tempDir, err := ioutil.TempDir(".", "temp")
12 | if err != nil {
13 | t.Errorf("create tempDir: %v", err)
14 | }
15 | t.Cleanup(func() { os.RemoveAll(tempDir) })
16 |
17 | err = ioutil.WriteFile(filepath.Join(tempDir, "test.log"), []byte("hello test"), 0644)
18 | if err != nil {
19 | t.Errorf("want writing success but got %v", err)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/s2/fail_test.go:
--------------------------------------------------------------------------------
1 | package s2
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func TestFail(t *testing.T) {
9 | t.Fail()
10 | t.Log("after Fail")
11 | t.FailNow()
12 | t.Log("after FailNow")
13 | }
14 |
15 | func TestFatal(t *testing.T) {
16 | t.Fatal("fataled")
17 | t.Log("after Fatal")
18 | }
19 |
20 | func TestFatalf(t *testing.T) {
21 | t.Fatalf("there is: %v", "Fatalf")
22 | t.Log("after Fatalf")
23 | }
24 |
25 | func TestFailNowInAnotherGoroutine(t *testing.T) {
26 | go func() {
27 | t.FailNow()
28 | t.Log("after FailNow in another goroutine")
29 | }()
30 |
31 | time.Sleep(time.Second)
32 | t.Log("after one second")
33 | }
34 |
--------------------------------------------------------------------------------
/s2/helper_test.go:
--------------------------------------------------------------------------------
1 | package s2
2 |
3 | import "testing"
4 |
5 | func notHelper(t *testing.T, msg string) {
6 | t.Error(msg)
7 | }
8 |
9 | func helper(t *testing.T, msg string) {
10 | t.Helper()
11 | t.Error(msg)
12 | }
13 |
14 | func notHelperCallingHelper(t *testing.T, msg string) {
15 | helper(t, msg)
16 | }
17 |
18 | func helperCallingHelper(t *testing.T, msg string) {
19 | t.Helper()
20 | helper(t, msg)
21 | }
22 |
23 | func TestHelper(t *testing.T) {
24 | notHelper(t, "0")
25 | helper(t, "1")
26 | notHelperCallingHelper(t, "2")
27 | helperCallingHelper(t, "3")
28 |
29 | fn := func(msg string) {
30 | t.Helper()
31 | t.Error(msg)
32 | }
33 | fn("4")
34 |
35 | t.Helper()
36 | t.Error("5")
37 |
38 | t.Run("sub", func(t *testing.T) {
39 | helper(t, "6")
40 | notHelperCallingHelper(t, "7")
41 | t.Helper()
42 | t.Error("8")
43 | })
44 | }
45 |
--------------------------------------------------------------------------------
/s2/log_test.go:
--------------------------------------------------------------------------------
1 | package s2
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func TestLog(t *testing.T) {
9 | t.Log("it is a log")
10 | t.Logf("it is a log at %v", time.Now().Format(time.RFC1123))
11 |
12 | t.Error("it is an error")
13 | t.Errorf("it is an error at %v", time.Now().Format(time.RFC1123))
14 | }
15 |
--------------------------------------------------------------------------------
/s2/main_test.go:
--------------------------------------------------------------------------------
1 | package s2
2 |
3 | import (
4 | "log"
5 | "os"
6 | "testing"
7 | )
8 |
9 | func TestMain(m *testing.M) {
10 | log.Println("do stuff BEFORE the tests!")
11 | exitVal := m.Run()
12 | log.Println("do stuff AFTER the tests!")
13 |
14 | os.Exit(exitVal)
15 | }
16 |
--------------------------------------------------------------------------------
/s2/parallel_test.go:
--------------------------------------------------------------------------------
1 | package s2
2 |
3 | import (
4 | "net/http"
5 | "testing"
6 | )
7 |
8 | func TestParallel(t *testing.T) {
9 | var urls = map[string]string{"baidu": "http://baidu.com", "bing": "http://bing.com", "google": "http://google.com"}
10 | for k, v := range urls {
11 | v := v
12 | ok := t.Run(k, func(t *testing.T) {
13 | t.Parallel()
14 |
15 | t.Logf("start to get %s", v)
16 | resp, err := http.Get(v)
17 | t.Logf("end to get %s", v)
18 | if err != nil {
19 | t.Errorf("failed to get %s: %v", v, err)
20 | return
21 | }
22 |
23 | resp.Body.Close()
24 | })
25 |
26 | t.Logf("run: %t", ok)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/s2/skip_test.go:
--------------------------------------------------------------------------------
1 | package s2
2 |
3 | import (
4 | "runtime"
5 | "testing"
6 | )
7 |
8 | func TestSkip(t *testing.T) {
9 | if runtime.GOOS == "darwin" {
10 | t.Skip("skip MacOS")
11 | }
12 |
13 | if testing.Short() {
14 | t.Skip("skip because of short")
15 | }
16 |
17 | t.Log("there a non-skipped log")
18 | }
19 |
--------------------------------------------------------------------------------
/s4/cover.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | s1: Go Coverage Report
7 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | github.com/smallnest/go_test_workshop/s1/arith.go (100.0%)
59 |
60 | github.com/smallnest/go_test_workshop/s1/hello.go (100.0%)
61 |
62 | github.com/smallnest/go_test_workshop/s6/book/api/book.go (75.0%)
63 |
64 | github.com/smallnest/go_test_workshop/s6/book/dao/book.go (66.7%)
65 |
66 | github.com/smallnest/go_test_workshop/s6/book/service/book.go (100.0%)
67 |
68 |
69 |
70 |
71 | not tracked
72 |
73 | not covered
74 | covered
75 |
76 |
77 |
78 |
79 |
80 |
package s1
81 |
82 | func Add(a, b int) int {
83 | return a + b
84 | }
85 |
86 | func Sub(a, b int) int {
87 | return a - b
88 | }
89 |
90 | func Mul(a, b int) int {
91 | return a * b
92 | }
93 |
94 | func Div(a, b int) int {
95 | return a / b
96 | }
97 |
98 |
99 |
package s1
100 |
101 | func greet(s string) string {
102 | return "hello " + s
103 | }
104 |
105 |
106 |
package api
107 |
108 | import (
109 | "context"
110 | "encoding/json"
111 | "net/http"
112 | "strconv"
113 |
114 | "github.com/smallnest/go_test_workshop/s6/book/model"
115 | "github.com/smallnest/go_test_workshop/s6/book/service"
116 | )
117 |
118 | type BookController struct {
119 | bookService service.BookService
120 | }
121 |
122 | func (bc *BookController) CreateBook(w http.ResponseWriter, r *http.Request) {
123 | title := r.FormValue("title")
124 | authorID := r.FormValue("authorID")
125 | isbn := r.FormValue("isbn")
126 | computers := r.FormValue("subject")
127 |
128 | aid, err := strconv.Atoi(authorID)
129 | if err != nil {
130 | http.Error(w, "wrong author id", http.StatusBadRequest)
131 | return
132 | }
133 |
134 | book := &model.Book{
135 | Title: title,
136 | AuthorID: aid,
137 | ISBN: isbn,
138 | Subject: computers,
139 | }
140 |
141 | err = bc.bookService.CreateBook(context.TODO(), book)
142 | if err != nil {
143 | http.Error(w, "failed to create book", http.StatusInternalServerError)
144 | return
145 | }
146 |
147 | data, _ := json.Marshal(book)
148 |
149 | w.Header().Set("Contetx-Type", "application/json")
150 | w.Write(data)
151 | }
152 |
153 |
154 |
package dao
155 |
156 | import (
157 | "context"
158 | "database/sql"
159 | "fmt"
160 |
161 | "github.com/smallnest/go_test_workshop/s6/book/model"
162 | )
163 |
164 | type BookStore interface {
165 | InsertBook(context.Context, *model.Book) error
166 | }
167 |
168 | func NewBookStore(db *sql.DB) *bookStore {
169 | return &bookStore{
170 | db: db,
171 | }
172 | }
173 |
174 | type bookStore struct {
175 | db *sql.DB
176 | }
177 |
178 | func (s *bookStore) InsertBook(ctx context.Context, b *model.Book) error {
179 | const stmt = "INSERT INTO books (author_id, title, isbn) VALUES ($1, $2, $3)"
180 |
181 | result, err := s.db.ExecContext(ctx, stmt, b.AuthorID, b.Title, b.ISBN)
182 | if err != nil {
183 | return fmt.Errorf("could not insert row: %w", err)
184 | }
185 |
186 | if _, err := result.RowsAffected(); err != nil {
187 | return fmt.Errorf("could not get affected rows: %w", err)
188 | }
189 |
190 | b.ID, _ = result.LastInsertId()
191 |
192 | return nil
193 | }
194 |
195 |
196 |
package service
197 |
198 | import (
199 | "context"
200 | "database/sql"
201 |
202 | "github.com/smallnest/go_test_workshop/s6/book/dao"
203 | "github.com/smallnest/go_test_workshop/s6/book/model"
204 | )
205 |
206 | type BookService interface {
207 | CreateBook(ctx context.Context, b *model.Book) error
208 | }
209 |
210 | func NewBookService(db *sql.DB) *bookService {
211 | return &bookService{
212 | store: dao.NewBookStore(db),
213 | }
214 | }
215 |
216 | type bookService struct {
217 | store dao.BookStore
218 | }
219 |
220 | func (s *bookService) CreateBook(ctx context.Context, b *model.Book) error {
221 | return s.store.InsertBook(ctx, b)
222 | }
223 |
224 |
225 |
226 |
227 |
254 |
255 |
--------------------------------------------------------------------------------
/s4/cover.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd ..
4 | go test -coverprofile s4/cover.out ./...
5 |
6 | gocov test ./... | gocov-html > s4/cover3.html
7 |
8 | cd s4
9 |
10 | go tool cover -html=cover.out -o cover.html
11 |
12 | go tool cover -o cover2.html -html=cover.out; sed -i'*.bak' 's/black/whitesmoke/g' cover2.html; rm -fr cover2.html*.bak
13 |
--------------------------------------------------------------------------------
/s4/cover2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | s1: Go Coverage Report
7 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | github.com/smallnest/go_test_workshop/s1/arith.go (100.0%)
59 |
60 | github.com/smallnest/go_test_workshop/s1/hello.go (100.0%)
61 |
62 | github.com/smallnest/go_test_workshop/s6/book/api/book.go (75.0%)
63 |
64 | github.com/smallnest/go_test_workshop/s6/book/dao/book.go (66.7%)
65 |
66 | github.com/smallnest/go_test_workshop/s6/book/service/book.go (100.0%)
67 |
68 |
69 |
70 |
71 | not tracked
72 |
73 | not covered
74 | covered
75 |
76 |
77 |
78 |
79 |
80 |
package s1
81 |
82 | func Add(a, b int) int {
83 | return a + b
84 | }
85 |
86 | func Sub(a, b int) int {
87 | return a - b
88 | }
89 |
90 | func Mul(a, b int) int {
91 | return a * b
92 | }
93 |
94 | func Div(a, b int) int {
95 | return a / b
96 | }
97 |
98 |
99 |
package s1
100 |
101 | func greet(s string) string {
102 | return "hello " + s
103 | }
104 |
105 |
106 |
package api
107 |
108 | import (
109 | "context"
110 | "encoding/json"
111 | "net/http"
112 | "strconv"
113 |
114 | "github.com/smallnest/go_test_workshop/s6/book/model"
115 | "github.com/smallnest/go_test_workshop/s6/book/service"
116 | )
117 |
118 | type BookController struct {
119 | bookService service.BookService
120 | }
121 |
122 | func (bc *BookController) CreateBook(w http.ResponseWriter, r *http.Request) {
123 | title := r.FormValue("title")
124 | authorID := r.FormValue("authorID")
125 | isbn := r.FormValue("isbn")
126 | computers := r.FormValue("subject")
127 |
128 | aid, err := strconv.Atoi(authorID)
129 | if err != nil {
130 | http.Error(w, "wrong author id", http.StatusBadRequest)
131 | return
132 | }
133 |
134 | book := &model.Book{
135 | Title: title,
136 | AuthorID: aid,
137 | ISBN: isbn,
138 | Subject: computers,
139 | }
140 |
141 | err = bc.bookService.CreateBook(context.TODO(), book)
142 | if err != nil {
143 | http.Error(w, "failed to create book", http.StatusInternalServerError)
144 | return
145 | }
146 |
147 | data, _ := json.Marshal(book)
148 |
149 | w.Header().Set("Contetx-Type", "application/json")
150 | w.Write(data)
151 | }
152 |
153 |
154 |
package dao
155 |
156 | import (
157 | "context"
158 | "database/sql"
159 | "fmt"
160 |
161 | "github.com/smallnest/go_test_workshop/s6/book/model"
162 | )
163 |
164 | type BookStore interface {
165 | InsertBook(context.Context, *model.Book) error
166 | }
167 |
168 | func NewBookStore(db *sql.DB) *bookStore {
169 | return &bookStore{
170 | db: db,
171 | }
172 | }
173 |
174 | type bookStore struct {
175 | db *sql.DB
176 | }
177 |
178 | func (s *bookStore) InsertBook(ctx context.Context, b *model.Book) error {
179 | const stmt = "INSERT INTO books (author_id, title, isbn) VALUES ($1, $2, $3)"
180 |
181 | result, err := s.db.ExecContext(ctx, stmt, b.AuthorID, b.Title, b.ISBN)
182 | if err != nil {
183 | return fmt.Errorf("could not insert row: %w", err)
184 | }
185 |
186 | if _, err := result.RowsAffected(); err != nil {
187 | return fmt.Errorf("could not get affected rows: %w", err)
188 | }
189 |
190 | b.ID, _ = result.LastInsertId()
191 |
192 | return nil
193 | }
194 |
195 |
196 |
package service
197 |
198 | import (
199 | "context"
200 | "database/sql"
201 |
202 | "github.com/smallnest/go_test_workshop/s6/book/dao"
203 | "github.com/smallnest/go_test_workshop/s6/book/model"
204 | )
205 |
206 | type BookService interface {
207 | CreateBook(ctx context.Context, b *model.Book) error
208 | }
209 |
210 | func NewBookService(db *sql.DB) *bookService {
211 | return &bookService{
212 | store: dao.NewBookStore(db),
213 | }
214 | }
215 |
216 | type bookService struct {
217 | store dao.BookStore
218 | }
219 |
220 | func (s *bookService) CreateBook(ctx context.Context, b *model.Book) error {
221 | return s.store.InsertBook(ctx, b)
222 | }
223 |
224 |
225 |
226 |
227 |
254 |
255 |
--------------------------------------------------------------------------------
/s4/cover3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Coverage Report
5 |
6 |
7 |
131 |
132 |
133 |
134 | Coverage Report
135 | Generated on 24 Jul 22 19:38 +0800 with
gocov-html Report Overview
142 | Package Overview: github.com/smallnest/go_test_workshop/s1 100.00%
This is a coverage report created after analysis of the github.com/smallnest/go_test_workshop/s1
package. It
143 | has been generated with the following command:
gocov test github.com/smallnest/go_test_workshop/s1 | gocov-html Here are the stats. Please select a function name to view its implementation and see what's left for testing.
144 | Add(...)
github.com/smallnest/go_test_workshop/s1/arith.go
100.00%
1/1
145 | Sub(...)
github.com/smallnest/go_test_workshop/s1/arith.go
100.00%
1/1
146 | Mul(...)
github.com/smallnest/go_test_workshop/s1/arith.go
100.00%
1/1
147 | Div(...)
github.com/smallnest/go_test_workshop/s1/arith.go
100.00%
1/1
148 | greet(...)
github.com/smallnest/go_test_workshop/s1/hello.go
100.00%
1/1
149 | github.com/smallnest/go_test_workshop/s1
100.00%
5/5
150 |
151 |
152 | func Add
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/arith.go
:
153 | 3 func Add(a, b int) int {
4 return a + b
5 }
154 |
155 | func Sub
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/arith.go
:
156 | 7 func Sub(a, b int) int {
8 return a - b
9 }
157 |
158 | func Mul
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/arith.go
:
159 | 11 func Mul(a, b int) int {
12 return a * b
13 }
160 |
161 | func Div
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/arith.go
:
162 | 15 func Div(a, b int) int {
16 return a / b
17 }
163 |
164 | func greet
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/hello.go
:
165 | 3 func greet(s string) string {
4 return "hello " + s
5 }
166 |
167 |
170 |
171 | Package Overview: github.com/smallnest/go_test_workshop/s6/book/api 75.00%
This is a coverage report created after analysis of the github.com/smallnest/go_test_workshop/s6/book/api
package. It
172 | has been generated with the following command:
gocov test github.com/smallnest/go_test_workshop/s6/book/api | gocov-html Here are the stats. Please select a function name to view its implementation and see what's left for testing.
173 | BookController.CreateBook(...)
github.com/smallnest/go_test_workshop/s6/book/api/book.go
75.00%
12/16
174 | github.com/smallnest/go_test_workshop/s6/book/api
75.00%
12/16
175 |
176 |
177 | func BookController.CreateBook
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/api/book.go
:
178 | 17 func (bc *BookController) CreateBook(w http.ResponseWriter, r *http.Request) {
18 title := r.FormValue("title")
19 authorID := r.FormValue("authorID")
20 isbn := r.FormValue("isbn")
21 computers := r.FormValue("subject")
22
23 aid, err := strconv.Atoi(authorID)
24 if err != nil {
25 http.Error(w, "wrong author id", http.StatusBadRequest)
26 return
27 }
28
29 book := &model.Book{
30 Title: title,
31 AuthorID: aid,
32 ISBN: isbn,
33 Subject: computers,
34 }
35
36 err = bc.bookService.CreateBook(context.TODO(), book)
37 if err != nil {
38 http.Error(w, "failed to create book", http.StatusInternalServerError)
39 return
40 }
41
42 data, _ := json.Marshal(book)
43
44 w.Header().Set("Contetx-Type", "application/json")
45 w.Write(data)
46 }
179 |
180 |
183 |
184 | Package Overview: github.com/smallnest/go_test_workshop/s6/book/dao 66.67%
This is a coverage report created after analysis of the github.com/smallnest/go_test_workshop/s6/book/dao
package. It
185 | has been generated with the following command:
gocov test github.com/smallnest/go_test_workshop/s6/book/dao | gocov-html Here are the stats. Please select a function name to view its implementation and see what's left for testing.
186 | bookStore.InsertBook(...)
github.com/smallnest/go_test_workshop/s6/book/dao/book.go
75.00%
6/8
187 | NewBookStore(...)
github.com/smallnest/go_test_workshop/s6/book/dao/book.go
0.00%
0/1
188 | github.com/smallnest/go_test_workshop/s6/book/dao
66.67%
6/9
189 |
190 |
191 | func bookStore.InsertBook
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/dao/book.go
:
192 | 25 func (s *bookStore) InsertBook(ctx context.Context, b *model.Book) error {
26 const stmt = "INSERT INTO books (author_id, title, isbn) VALUES ($1, $2, $3)"
27
28 result, err := s.db.ExecContext(ctx, stmt, b.AuthorID, b.Title, b.ISBN)
29 if err != nil {
30 return fmt.Errorf("could not insert row: %w", err)
31 }
32
33 if _, err := result.RowsAffected(); err != nil {
34 return fmt.Errorf("could not get affected rows: %w", err)
35 }
36
37 b.ID, _ = result.LastInsertId()
38
39 return nil
40 }
193 |
194 | func NewBookStore
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/dao/book.go
:
195 | 15 func NewBookStore(db *sql.DB) *bookStore {
16 return &bookStore{
17 db: db,
18 }
19 }
196 |
197 |
200 |
201 | Package Overview: github.com/smallnest/go_test_workshop/s6/book/service 100.00%
This is a coverage report created after analysis of the github.com/smallnest/go_test_workshop/s6/book/service
package. It
202 | has been generated with the following command:
gocov test github.com/smallnest/go_test_workshop/s6/book/service | gocov-html Here are the stats. Please select a function name to view its implementation and see what's left for testing.
203 | NewBookService(...)
github.com/smallnest/go_test_workshop/s6/book/service/book.go
100.00%
1/1
204 | bookService.CreateBook(...)
github.com/smallnest/go_test_workshop/s6/book/service/book.go
100.00%
1/1
205 | github.com/smallnest/go_test_workshop/s6/book/service
100.00%
2/2
206 |
207 |
208 | func NewBookService
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/service/book.go
:
209 | 15 func NewBookService(db *sql.DB) *bookService {
16 return &bookService{
17 store: dao.NewBookStore(db),
18 }
19 }
210 |
211 | func bookService.CreateBook
Back In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/service/book.go
:
212 | 25 func (s *bookService) CreateBook(ctx context.Context, b *model.Book) error {
26 return s.store.InsertBook(ctx, b)
27 }
213 |
214 |
217 |
218 | Report Total
219 |
78.12%
220 |
221 |
222 |
--------------------------------------------------------------------------------
/s4/covermode.go:
--------------------------------------------------------------------------------
1 | package s4
2 |
3 | func example(letter string) string {
4 | switch letter {
5 | case "a":
6 | return "apple"
7 | case "b":
8 | return "banana"
9 | case "c":
10 | return "carrot"
11 | case "d":
12 | return "date"
13 | case "e":
14 | return "eggplant"
15 | case "f":
16 | return "fig"
17 | default:
18 | return "nevermind"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/s4/covermode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | s4: Go Coverage Report
7 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | github.com/smallnest/go_test_workshop/s4/covermode.go (100.0%)
59 |
60 |
61 |
62 |
63 | not tracked
64 |
65 | no coverage
66 | low coverage
67 | *
68 | *
69 | *
70 | *
71 | *
72 | *
73 | *
74 | *
75 | high coverage
76 |
77 |
78 |
79 |
80 |
81 |
package s4
82 |
83 | func example(letter string) string {
84 | switch letter {
85 | case "a":
86 | return "apple"
87 | case "b":
88 | return "banana"
89 | case "c":
90 | return "carrot"
91 | case "d":
92 | return "date"
93 | case "e":
94 | return "eggplant"
95 | case "f":
96 | return "fig"
97 | default:
98 | return "nevermind"
99 | }
100 | }
101 |
102 |
103 |
104 |
105 |
132 |
133 |
--------------------------------------------------------------------------------
/s4/covermode_test.go:
--------------------------------------------------------------------------------
1 | package s4
2 |
3 | import "testing"
4 |
5 | func TestExample(t *testing.T) {
6 | for in, out := range map[string]string{
7 | "a": "apple",
8 | "b": "banana",
9 | "c": "carrot",
10 | "d": "date",
11 | "e": "eggplant",
12 | "f": "fig",
13 | "": "nevermind",
14 | } {
15 | t.Run("input-"+in, func(t *testing.T) {
16 | // t.Parallel()
17 | result := example(in)
18 | if result != out {
19 | t.Errorf("sent %s and got %s expected %s", in, result, out)
20 | }
21 | })
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/s4/out.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 | github.com/smallnest/go_test_workshop
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | s1
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | s6/book
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | arith.go
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | hello.go
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | api/book.go
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | dao/book.go
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | service/book.go
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/s5/assert_require_test.go:
--------------------------------------------------------------------------------
1 | package s5
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | "github.com/stretchr/testify/require"
8 | )
9 |
10 | func TestAssert(t *testing.T) {
11 | var err error
12 | assert.NoError(t, err)
13 |
14 | assert.Equal(t, 1, 1)
15 |
16 | assert.Empty(t, len([]string{"abc"}))
17 |
18 | assert.Greater(t, 7, 6)
19 |
20 | t.Log("exit assert")
21 | }
22 |
23 | func TestRequire(t *testing.T) {
24 | var err error
25 | require.NoError(t, err)
26 |
27 | require.Equal(t, 1, 1)
28 |
29 | require.Empty(t, len([]string{"abc"}))
30 |
31 | require.Greater(t, 7, 6)
32 |
33 | t.Log("exit require")
34 | }
35 |
--------------------------------------------------------------------------------
/s5/gocovery_test.go:
--------------------------------------------------------------------------------
1 | package s5
2 |
3 | import (
4 | "testing"
5 |
6 | . "github.com/smartystreets/goconvey/convey"
7 | )
8 |
9 | func TestGocovery(t *testing.T) {
10 | Convey("Given a starting integer value", t, func() {
11 | x := 42
12 |
13 | Convey("When incremented", func() {
14 | x++
15 |
16 | Convey("The value should be greater by one", func() {
17 | So(x, ShouldEqual, 43)
18 | })
19 | Convey("The value should NOT be what it used to be", func() {
20 | So(x, ShouldNotEqual, 42)
21 | })
22 | })
23 | Convey("When decremented", func() {
24 | x--
25 |
26 | Convey("The value should be lesser by one", func() {
27 | So(x, ShouldEqual, 41)
28 | })
29 | Convey("The value should NOT be what it used to be", func() {
30 | So(x, ShouldNotEqual, 42)
31 | })
32 | })
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/s6/container/kafka_test.go:
--------------------------------------------------------------------------------
1 | package container
2 |
3 | import (
4 | "context"
5 | "strings"
6 | "testing"
7 | "time"
8 |
9 | "github.com/google/uuid"
10 | "github.com/segmentio/kafka-go"
11 | "github.com/stretchr/testify/assert"
12 | "github.com/stretchr/testify/require"
13 | "github.com/testcontainers/testcontainers-go"
14 | )
15 |
16 | func TestWithKafka(t *testing.T) {
17 | kafkaContainer := testcontainers.NewLocalDockerCompose(
18 | []string{"testdata/docker-compose.yml"},
19 | strings.ToLower(uuid.New().String()),
20 | )
21 | execError := kafkaContainer.WithCommand([]string{"up", "-d"}).Invoke()
22 | require.NoError(t, execError.Error)
23 |
24 | // kafka starts ver slow
25 | time.Sleep(time.Minute)
26 | defer destroyKafka(kafkaContainer)
27 |
28 | // test write
29 | w := &kafka.Writer{
30 | Addr: kafka.TCP("localhost:9092"),
31 | Topic: "test-topic",
32 | Balancer: &kafka.LeastBytes{},
33 | }
34 |
35 | err := w.WriteMessages(context.Background(),
36 | kafka.Message{
37 | Key: []byte("Key-A"),
38 | Value: []byte("Hello World!"),
39 | },
40 | kafka.Message{
41 | Key: []byte("Key-B"),
42 | Value: []byte("One!"),
43 | },
44 | kafka.Message{
45 | Key: []byte("Key-C"),
46 | Value: []byte("Two!"),
47 | },
48 | )
49 | assert.NoError(t, err)
50 | err = w.Close()
51 | assert.NoError(t, err)
52 |
53 | // test read
54 | r := kafka.NewReader(kafka.ReaderConfig{
55 | Brokers: []string{"localhost:9092"},
56 | Topic: "test-topic",
57 | Partition: 0,
58 | MinBytes: 10e3, // 10KB
59 | MaxBytes: 10e6, // 10MB
60 | })
61 |
62 | m, err := r.ReadMessage(context.Background())
63 | assert.NoError(t, err)
64 | assert.NotEmpty(t, m)
65 |
66 | }
67 |
68 | func destroyKafka(compose *testcontainers.LocalDockerCompose) {
69 | compose.Down()
70 | time.Sleep(1 * time.Second)
71 | }
72 |
--------------------------------------------------------------------------------
/s6/container/redis_test.go:
--------------------------------------------------------------------------------
1 | package container
2 |
3 | import (
4 | "context"
5 | "testing"
6 |
7 | "github.com/go-redis/redis"
8 | "github.com/stretchr/testify/assert"
9 | "github.com/testcontainers/testcontainers-go"
10 | "github.com/testcontainers/testcontainers-go/wait"
11 | )
12 |
13 | func TestWithRedis(t *testing.T) {
14 | ctx := context.Background()
15 | req := testcontainers.ContainerRequest{
16 | Image: "redis:latest",
17 | ExposedPorts: []string{"6379/tcp"},
18 | WaitingFor: wait.ForLog("Ready to accept connections"),
19 | }
20 | redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
21 | ContainerRequest: req,
22 | Started: true,
23 | })
24 | if err != nil {
25 | t.Error(err)
26 | }
27 | defer redisC.Terminate(ctx)
28 |
29 | rdb := redis.NewClient(&redis.Options{
30 | Addr: "localhost:6379",
31 | Password: "", // no password set
32 | DB: 0, // use default DB
33 | })
34 |
35 | err = rdb.Set("key", "value", 0).Err()
36 | assert.NoError(t, err)
37 |
38 | val, err := rdb.Get("key").Result()
39 | assert.NoError(t, err)
40 | assert.Equal(t, "value", val)
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/s6/container/testdata/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | zookeeper:
4 | image: wurstmeister/zookeeper:latest
5 | container_name: zookeeper
6 | expose:
7 | - 2181
8 | kafka:
9 | image: wurstmeister/kafka:latest
10 | container_name: kafka
11 | depends_on:
12 | - zookeeper
13 | ports:
14 | - "9092:9092"
15 | environment:
16 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
17 | KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
18 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
19 | KAFKA_CREATE_TOPICS: "test-topic:1:1"
--------------------------------------------------------------------------------
/s6/gnomock/kafka_test.go:
--------------------------------------------------------------------------------
1 | package gnomock_test
2 |
3 | import (
4 | "context"
5 | "os"
6 | "testing"
7 | "time"
8 |
9 | "github.com/orlangure/gnomock"
10 | "github.com/orlangure/gnomock/preset/kafka"
11 | kafkaclient "github.com/segmentio/kafka-go"
12 | "github.com/stretchr/testify/require"
13 | )
14 |
15 | func TestKafka(t *testing.T) {
16 | // init some messages
17 | messages := []kafka.Message{
18 | {
19 | Topic: "events",
20 | Key: "order",
21 | Value: "1",
22 | Time: time.Now().UnixNano(),
23 | },
24 | {
25 | Topic: "alerts",
26 | Key: "CPU",
27 | Value: "92",
28 | Time: time.Now().UnixNano(),
29 | },
30 | }
31 |
32 | p := kafka.Preset(
33 | kafka.WithTopics("topic-1", "topic-2"),
34 | kafka.WithMessages(messages...),
35 | )
36 |
37 | // start the kafka container
38 | container, err := gnomock.Start(
39 | p,
40 | gnomock.WithDebugMode(), gnomock.WithLogWriter(os.Stdout),
41 | gnomock.WithContainerName("kafka"),
42 | gnomock.WithUseLocalImagesFirst(),
43 | )
44 | require.NoError(t, err)
45 | // stop the kafka container on exit
46 | defer func() { require.NoError(t, gnomock.Stop(container)) }()
47 |
48 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
49 | defer cancel()
50 |
51 | alertsReader := kafkaclient.NewReader(kafkaclient.ReaderConfig{
52 | Brokers: []string{container.Address(kafka.BrokerPort)},
53 | Topic: "alerts",
54 | })
55 |
56 | m, err := alertsReader.ReadMessage(ctx)
57 | require.NoError(t, err)
58 | require.NoError(t, alertsReader.Close())
59 |
60 | require.Equal(t, "CPU", string(m.Key))
61 | require.Equal(t, "92", string(m.Value))
62 |
63 | eventsReader := kafkaclient.NewReader(kafkaclient.ReaderConfig{
64 | Brokers: []string{container.Address(kafka.BrokerPort)},
65 | Topic: "events",
66 | })
67 |
68 | m, err = eventsReader.ReadMessage(ctx)
69 | require.NoError(t, err)
70 | require.NoError(t, eventsReader.Close())
71 |
72 | require.Equal(t, "order", string(m.Key))
73 | require.Equal(t, "1", string(m.Value))
74 |
75 | c, err := kafkaclient.Dial("tcp", container.Address(kafka.BrokerPort))
76 | require.NoError(t, err)
77 |
78 | require.NoError(t, c.DeleteTopics("topic-1", "topic-2"))
79 | require.Error(t, c.DeleteTopics("unknown-topic"))
80 |
81 | require.NoError(t, c.Close())
82 | }
83 |
--------------------------------------------------------------------------------
/s6/gnomock/redis_test.go:
--------------------------------------------------------------------------------
1 | package gnomock_test
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | redisclient "github.com/go-redis/redis/v7"
8 | "github.com/orlangure/gnomock"
9 | "github.com/orlangure/gnomock/preset/redis"
10 | )
11 |
12 | func TestRedis(t *testing.T) {
13 | vs := make(map[string]interface{})
14 |
15 | vs["a"] = "foo"
16 | vs["b"] = 42
17 | vs["c"] = true
18 |
19 | p := redis.Preset(redis.WithValues(vs))
20 | container, _ := gnomock.Start(p,
21 | gnomock.WithDebugMode(),
22 | gnomock.WithUseLocalImagesFirst(),
23 | )
24 |
25 | defer func() { _ = gnomock.Stop(container) }()
26 |
27 | addr := container.DefaultAddress()
28 | client := redisclient.NewClient(&redisclient.Options{Addr: addr})
29 |
30 | fmt.Println(client.Get("a").Result())
31 |
32 | var number int
33 |
34 | err := client.Get("b").Scan(&number)
35 | fmt.Println(number, err)
36 |
37 | var flag bool
38 |
39 | err = client.Get("c").Scan(&flag)
40 | fmt.Println(flag, err)
41 | }
42 |
--------------------------------------------------------------------------------
/s6/httptest/handler_test.go:
--------------------------------------------------------------------------------
1 | package httptest
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "net/http"
7 | "net/http/httptest"
8 | "testing"
9 | )
10 |
11 | func TestHandler(t *testing.T) {
12 | handler := func(w http.ResponseWriter, r *http.Request) {
13 | io.WriteString(w, "Hello World!")
14 | }
15 |
16 | req := httptest.NewRequest("GET", "http://example.com/foo", nil)
17 | w := httptest.NewRecorder()
18 | handler(w, req)
19 |
20 | resp := w.Result()
21 | body, _ := io.ReadAll(resp.Body)
22 |
23 | fmt.Println(resp.StatusCode)
24 | fmt.Println(resp.Header.Get("Content-Type"))
25 | fmt.Println(string(body))
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/s6/httptest/server_test.go:
--------------------------------------------------------------------------------
1 | package httptest
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "log"
7 | "net/http"
8 | "net/http/httptest"
9 | "testing"
10 | )
11 |
12 | func TestHTTPServer(t *testing.T) {
13 | ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
14 | fmt.Fprintf(w, "Hello, %s", r.Proto)
15 | }))
16 | ts.EnableHTTP2 = true
17 | ts.StartTLS()
18 | defer ts.Close()
19 |
20 | res, err := ts.Client().Get(ts.URL)
21 | if err != nil {
22 | log.Fatal(err)
23 | }
24 | greeting, err := io.ReadAll(res.Body)
25 | res.Body.Close()
26 | if err != nil {
27 | log.Fatal(err)
28 | }
29 | fmt.Printf("%s", greeting)
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/s6/mock/io_test.go:
--------------------------------------------------------------------------------
1 | // Code generated by MockGen. DO NOT EDIT.
2 | // Source: io (interfaces: Reader,Writer)
3 |
4 | // Package mock_io is a generated GoMock package.
5 | package mock_io
6 |
7 | import (
8 | reflect "reflect"
9 |
10 | gomock "github.com/golang/mock/gomock"
11 | )
12 |
13 | // MockReader is a mock of Reader interface.
14 | type MockReader struct {
15 | ctrl *gomock.Controller
16 | recorder *MockReaderMockRecorder
17 | }
18 |
19 | // MockReaderMockRecorder is the mock recorder for MockReader.
20 | type MockReaderMockRecorder struct {
21 | mock *MockReader
22 | }
23 |
24 | // NewMockReader creates a new mock instance.
25 | func NewMockReader(ctrl *gomock.Controller) *MockReader {
26 | mock := &MockReader{ctrl: ctrl}
27 | mock.recorder = &MockReaderMockRecorder{mock}
28 | return mock
29 | }
30 |
31 | // EXPECT returns an object that allows the caller to indicate expected use.
32 | func (m *MockReader) EXPECT() *MockReaderMockRecorder {
33 | return m.recorder
34 | }
35 |
36 | // Read mocks base method.
37 | func (m *MockReader) Read(arg0 []byte) (int, error) {
38 | m.ctrl.T.Helper()
39 | ret := m.ctrl.Call(m, "Read", arg0)
40 | ret0, _ := ret[0].(int)
41 | ret1, _ := ret[1].(error)
42 | return ret0, ret1
43 | }
44 |
45 | // Read indicates an expected call of Read.
46 | func (mr *MockReaderMockRecorder) Read(arg0 interface{}) *gomock.Call {
47 | mr.mock.ctrl.T.Helper()
48 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockReader)(nil).Read), arg0)
49 | }
50 |
51 | // MockWriter is a mock of Writer interface.
52 | type MockWriter struct {
53 | ctrl *gomock.Controller
54 | recorder *MockWriterMockRecorder
55 | }
56 |
57 | // MockWriterMockRecorder is the mock recorder for MockWriter.
58 | type MockWriterMockRecorder struct {
59 | mock *MockWriter
60 | }
61 |
62 | // NewMockWriter creates a new mock instance.
63 | func NewMockWriter(ctrl *gomock.Controller) *MockWriter {
64 | mock := &MockWriter{ctrl: ctrl}
65 | mock.recorder = &MockWriterMockRecorder{mock}
66 | return mock
67 | }
68 |
69 | // EXPECT returns an object that allows the caller to indicate expected use.
70 | func (m *MockWriter) EXPECT() *MockWriterMockRecorder {
71 | return m.recorder
72 | }
73 |
74 | // Write mocks base method.
75 | func (m *MockWriter) Write(arg0 []byte) (int, error) {
76 | m.ctrl.T.Helper()
77 | ret := m.ctrl.Call(m, "Write", arg0)
78 | ret0, _ := ret[0].(int)
79 | ret1, _ := ret[1].(error)
80 | return ret0, ret1
81 | }
82 |
83 | // Write indicates an expected call of Write.
84 | func (mr *MockWriterMockRecorder) Write(arg0 interface{}) *gomock.Call {
85 | mr.mock.ctrl.T.Helper()
86 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockWriter)(nil).Write), arg0)
87 | }
88 |
--------------------------------------------------------------------------------
/s6/mock/rw_test.go:
--------------------------------------------------------------------------------
1 | package mock_io
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/golang/mock/gomock"
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestReader(t *testing.T) {
11 | ctrl := gomock.NewController(t)
12 | defer ctrl.Finish()
13 |
14 | r := NewMockReader(ctrl)
15 |
16 | r.EXPECT().Read(gomock.All()).Do(func(arg0 []byte) {
17 | for i := 0; i < 10; i++ {
18 | arg0[i] = 'a' + byte(i)
19 | }
20 | }).Return(10, nil).AnyTimes()
21 |
22 | data := make([]byte, 1024)
23 | n, err := r.Read(data)
24 | assert.NoError(t, err)
25 | assert.Equal(t, 10, n)
26 | assert.Equal(t, "abcdefghij", string(data[:n]))
27 |
28 | }
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/s6/mockey/mock_test.go:
--------------------------------------------------------------------------------
1 | package mockey
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "testing"
7 |
8 | "github.com/bytedance/mockey"
9 | )
10 |
11 | // go test -v . -ldflags=-checklinkname=0 -gcflags="all=-N -l"
12 |
13 | func TestCallerFunc(t *testing.T) {
14 | origin := fmt.Println
15 | mock := func(a ...any) (n int, err error) {
16 | fmt.Fprintln(os.Stdout, "I have changed the arguments")
17 | return origin(a...)
18 | }
19 | mock2 := mockey.Mock(origin).To(mock).Origin(&origin).Build()
20 | defer mock2.UnPatch()
21 |
22 | mock2.When(func(a ...any) bool {
23 | if len(a) == 1 && a[0] == "hello world" {
24 | return true
25 | }
26 |
27 | return false
28 | }).To(func(a ...any) (n int, err error) {
29 | return fmt.Fprintln(os.Stdout, "matched when")
30 | })
31 |
32 | fmt.Println("hello world")
33 | fmt.Println("hello mockey")
34 | }
35 |
--------------------------------------------------------------------------------
/s6/monkey/hello_test.go:
--------------------------------------------------------------------------------
1 | package monkey
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "testing"
7 |
8 | // "bou.ke/monkey"
9 | "github.com/agiledragon/gomonkey/v2"
10 | )
11 |
12 | func TestPrintlnByGomonkey(t *testing.T) {
13 | patches := gomonkey.ApplyFunc(fmt.Println, func(a ...any) (n int, err error) {
14 |
15 | return fmt.Fprintln(os.Stdout, "I have changed the arguments")
16 | })
17 | defer patches.Reset()
18 |
19 | fmt.Println("hello world")
20 | }
21 |
22 | // func TestPrintlnByMonkey(t *testing.T) {
23 | // monkey.Patch(fmt.Println, func(a ...interface{}) (n int, err error) {
24 | // s := make([]interface{}, len(a))
25 | // for i, v := range a {
26 | // s[i] = strings.Replace(fmt.Sprint(v), "hell", "*bleep*", -1)
27 | // }
28 | // return fmt.Fprintln(os.Stdout, s...)
29 | // })
30 | // fmt.Println("what the hell?") // what the *bleep*?
31 | // }
32 |
--------------------------------------------------------------------------------
/s7/book/api/book.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "net/http"
7 | "strconv"
8 |
9 | "github.com/smallnest/go_test_workshop/s7/book/model"
10 | "github.com/smallnest/go_test_workshop/s7/book/service"
11 | )
12 |
13 | type BookController struct {
14 | bookService service.BookService
15 | }
16 |
17 | func (bc *BookController) CreateBook(w http.ResponseWriter, r *http.Request) {
18 | title := r.FormValue("title")
19 | authorID := r.FormValue("authorID")
20 | isbn := r.FormValue("isbn")
21 | computers := r.FormValue("subject")
22 |
23 | aid, err := strconv.Atoi(authorID)
24 | if err != nil {
25 | http.Error(w, "wrong author id", http.StatusBadRequest)
26 | return
27 | }
28 |
29 | book := &model.Book{
30 | Title: title,
31 | AuthorID: aid,
32 | ISBN: isbn,
33 | Subject: computers,
34 | }
35 |
36 | err = bc.bookService.CreateBook(context.TODO(), book)
37 | if err != nil {
38 | http.Error(w, "failed to create book", http.StatusInternalServerError)
39 | return
40 | }
41 |
42 | data, _ := json.Marshal(book)
43 |
44 | w.Header().Set("Contetx-Type", "application/json")
45 | w.Write(data)
46 | }
47 |
--------------------------------------------------------------------------------
/s7/book/api/book_monkey_test.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "io"
7 | "net/http"
8 | "net/http/httptest"
9 | "net/url"
10 | "strings"
11 | "testing"
12 |
13 | "github.com/agiledragon/gomonkey/v2"
14 | "github.com/smallnest/go_test_workshop/s7/book/model"
15 | "github.com/smallnest/go_test_workshop/s7/book/service"
16 | "github.com/stretchr/testify/assert"
17 | "github.com/stretchr/testify/require"
18 | )
19 |
20 | func TestBookController_GetBook_By_Monkey(t *testing.T) {
21 | bookService := service.NewBookService(nil)
22 | bc := &BookController{bookService: bookService}
23 |
24 | data := url.Values{}
25 | data.Set("title", "The Go Programming Language")
26 | data.Set("authorID", "1")
27 | data.Set("isbn", "978-0134190440")
28 | data.Set("subject", "computers")
29 |
30 | r := httptest.NewRequest("POST", "http://example.com/foo", strings.NewReader(data.Encode()))
31 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
32 |
33 | book := &model.Book{
34 | ID: 6,
35 | Title: "The Go Programming Language",
36 | AuthorID: 1,
37 | ISBN: "978-0134190440",
38 | Subject: "computers",
39 | }
40 | patches := gomonkey.ApplyMethodFunc(bookService, "CreateBook", func(ctx context.Context, b *model.Book) error {
41 | b.ID = 6
42 |
43 | return nil
44 | })
45 | defer patches.Reset()
46 |
47 | w := httptest.NewRecorder()
48 | bc.CreateBook(w, r)
49 |
50 | resp := w.Result()
51 | require.Equal(t, http.StatusOK, resp.StatusCode)
52 | body, _ := io.ReadAll(resp.Body)
53 |
54 | err := json.Unmarshal(body, &book)
55 | require.NoError(t, err)
56 | assert.NotZero(t, book.ID)
57 | }
58 |
--------------------------------------------------------------------------------
/s7/book/api/book_test.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "encoding/json"
5 | "io"
6 | "net/http"
7 | "net/http/httptest"
8 | "net/url"
9 | "strings"
10 | "testing"
11 |
12 | "github.com/smallnest/go_test_workshop/s7/book/model"
13 | "github.com/smallnest/go_test_workshop/s7/book/service"
14 | "github.com/smallnest/go_test_workshop/s7/book/util"
15 | "github.com/stretchr/testify/assert"
16 | "github.com/stretchr/testify/require"
17 | )
18 |
19 | func TestBookController_GetBook(t *testing.T) {
20 | db := util.CreateTestDB(t)
21 | defer db.Close()
22 |
23 | bookService := service.NewBookService(db)
24 | bc := &BookController{bookService: bookService}
25 |
26 | data := url.Values{}
27 | data.Set("title", "The Go Programming Language")
28 | data.Set("authorID", "1")
29 | data.Set("isbn", "978-0134190440")
30 | data.Set("subject", "computers")
31 |
32 | r := httptest.NewRequest("POST", "http://example.com/foo", strings.NewReader(data.Encode()))
33 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
34 |
35 | w := httptest.NewRecorder()
36 | bc.CreateBook(w, r)
37 |
38 | resp := w.Result()
39 | require.Equal(t, http.StatusOK, resp.StatusCode)
40 | body, _ := io.ReadAll(resp.Body)
41 |
42 | var book model.Book
43 | err := json.Unmarshal(body, &book)
44 | require.NoError(t, err)
45 | assert.NotZero(t, book.ID)
46 | }
47 |
--------------------------------------------------------------------------------
/s7/book/api/test-bin:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/s7/book/dao/book.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 | "fmt"
7 |
8 | "github.com/smallnest/go_test_workshop/s7/book/model"
9 | )
10 |
11 | type BookStore interface {
12 | InsertBook(context.Context, *model.Book) error
13 | }
14 |
15 | func NewBookStore(db *sql.DB) *bookStore {
16 | return &bookStore{
17 | db: db,
18 | }
19 | }
20 |
21 | type bookStore struct {
22 | db *sql.DB
23 | }
24 |
25 | func (s *bookStore) InsertBook(ctx context.Context, b *model.Book) error {
26 | const stmt = "INSERT INTO books (author_id, title, isbn) VALUES ($1, $2, $3)"
27 |
28 | result, err := s.db.ExecContext(ctx, stmt, b.AuthorID, b.Title, b.ISBN)
29 | if err != nil {
30 | return fmt.Errorf("could not insert row: %w", err)
31 | }
32 |
33 | if _, err := result.RowsAffected(); err != nil {
34 | return fmt.Errorf("could not get affected rows: %w", err)
35 | }
36 |
37 | b.ID, _ = result.LastInsertId()
38 |
39 | return nil
40 | }
41 |
--------------------------------------------------------------------------------
/s7/book/dao/book_test.go:
--------------------------------------------------------------------------------
1 | package dao
2 |
3 | import (
4 | "context"
5 | "testing"
6 |
7 | "github.com/DATA-DOG/go-sqlmock"
8 | "github.com/smallnest/go_test_workshop/s7/book/model"
9 | "github.com/stretchr/testify/assert"
10 | "github.com/stretchr/testify/require"
11 | )
12 |
13 | func TestBookStore_InsertBook(t *testing.T) {
14 | db, mock, err := sqlmock.New()
15 | assert.NoError(t, err, "an error '%s' was not expected when opening a stub database connection", err)
16 | defer db.Close()
17 |
18 | store := &bookStore{
19 | db: db,
20 | }
21 |
22 | book := &model.Book{
23 | Title: "The Go Programming Language",
24 | AuthorID: 1,
25 | ISBN: "978-0134190440",
26 | Subject: "computers",
27 | }
28 |
29 | mock.ExpectExec("INSERT INTO books").WillReturnResult(sqlmock.NewResult(1, 1))
30 |
31 | err = store.InsertBook(context.TODO(), book)
32 |
33 | require.NoError(t, err)
34 | assert.NotZero(t, book.ID)
35 | }
36 |
--------------------------------------------------------------------------------
/s7/book/model/book.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type Book struct {
4 | ID int64
5 | AuthorID int
6 | Title string
7 | ISBN string
8 | Subject string
9 | }
10 |
11 | type Author struct {
12 | ID int
13 | FirstName string
14 | LastName string
15 | }
16 |
--------------------------------------------------------------------------------
/s7/book/service/book.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 |
7 | "github.com/smallnest/go_test_workshop/s7/book/dao"
8 | "github.com/smallnest/go_test_workshop/s7/book/model"
9 | )
10 |
11 | type BookService interface {
12 | CreateBook(ctx context.Context, b *model.Book) error
13 | }
14 |
15 | func NewBookService(db *sql.DB) *bookService {
16 | return &bookService{
17 | store: dao.NewBookStore(db),
18 | }
19 | }
20 |
21 | type bookService struct {
22 | store dao.BookStore
23 | }
24 |
25 | func (s *bookService) CreateBook(ctx context.Context, b *model.Book) error {
26 | return s.store.InsertBook(ctx, b)
27 | }
28 |
--------------------------------------------------------------------------------
/s7/book/service/book_test.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "context"
5 | "testing"
6 |
7 | "github.com/smallnest/go_test_workshop/s7/book/model"
8 | "github.com/smallnest/go_test_workshop/s7/book/util"
9 | "github.com/stretchr/testify/assert"
10 | "github.com/stretchr/testify/require"
11 | )
12 |
13 | func TestBookService_CreateBook(t *testing.T) {
14 | db := util.CreateTestDB(t)
15 | defer db.Close()
16 |
17 | bookService := NewBookService(db)
18 |
19 | book := &model.Book{
20 | Title: "The Go Programming Language",
21 | AuthorID: 1,
22 | ISBN: "978-0134190440",
23 | Subject: "computers",
24 | }
25 | err := bookService.CreateBook(context.TODO(), book)
26 |
27 | require.NoError(t, err)
28 | assert.NotZero(t, book.ID)
29 | }
30 |
--------------------------------------------------------------------------------
/s7/book/testdata/test.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smallnest/go_test_workshop/27f12b63da3545c6ea5ae94b9287c3f5b0da5137/s7/book/testdata/test.db
--------------------------------------------------------------------------------
/s7/book/util/db.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "database/sql"
5 | "testing"
6 |
7 | _ "github.com/mattn/go-sqlite3"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func CreateTestDB(t *testing.T) *sql.DB {
12 | db, err := sql.Open("sqlite3", "file:../testdata/test.db?cache=shared")
13 | assert.NoError(t, err)
14 |
15 | db.Exec(`
16 | CREATE TABLE IF NOT EXISTS books (
17 | Id INTEGER PRIMARY KEY AUTOINCREMENT,
18 | title TEXT,
19 | isbn TEXT,
20 | subject TEXT,
21 | author_id INTEGER
22 | );
23 | DELETE FROM books;
24 | `)
25 |
26 | return db
27 | }
28 |
--------------------------------------------------------------------------------
/s7/book/util/db_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "testing"
5 |
6 | _ "github.com/mattn/go-sqlite3"
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestCreateTestDB(t *testing.T) {
11 | db := CreateTestDB(t)
12 | defer db.Close()
13 |
14 | _, err := db.Exec("DELETE FROM tor_list")
15 | assert.NoError(t, err)
16 | }
17 |
--------------------------------------------------------------------------------
/s8/benchmark_hash_test.go:
--------------------------------------------------------------------------------
1 | package s8
2 |
3 | import (
4 | "crypto/md5"
5 | "crypto/rand"
6 | "crypto/sha1"
7 | "crypto/sha256"
8 | "crypto/sha512"
9 | "encoding/hex"
10 | "fmt"
11 | "hash/crc32"
12 | "hash/fnv"
13 | _ "runtime"
14 | "testing"
15 | "unsafe"
16 |
17 | xxhashasm "github.com/cespare/xxhash"
18 | "github.com/creachadair/cityhash"
19 | afarmhash "github.com/dgryski/go-farm"
20 | farmhash "github.com/leemcloughlin/gofarmhash"
21 | "github.com/minio/highwayhash"
22 | "github.com/pierrec/xxHash/xxHash64"
23 | "github.com/spaolacci/murmur3"
24 | )
25 |
26 | var n int64
27 | var testBytes []byte
28 |
29 | func BenchmarkHash(b *testing.B) {
30 | sizes := []int64{32, 64, 128, 256, 512, 1024}
31 | for _, n = range sizes {
32 | testBytes = make([]byte, n)
33 | readN, err := rand.Read(testBytes)
34 | if readN != int(n) {
35 | panic(fmt.Sprintf("expect %d but got %d", n, readN))
36 | }
37 | if err != nil {
38 | panic(err)
39 | }
40 |
41 | b.Run(fmt.Sprintf("Sha1-%d", n), benchmarkSha1)
42 | b.Run(fmt.Sprintf("Sha256-%d", n), benchmarkSha256)
43 | b.Run(fmt.Sprintf("Sha512-%d", n), benchmarkSha512)
44 | b.Run(fmt.Sprintf("MD5-%d", n), benchmarkMD5)
45 | b.Run(fmt.Sprintf("Fnv-%d", n), benchmarkFnv)
46 | b.Run(fmt.Sprintf("Crc32-%d", n), benchmarkCrc32)
47 | b.Run(fmt.Sprintf("CityHash-%d", n), benchmarkCityhash)
48 | b.Run(fmt.Sprintf("FarmHash-%d", n), benchmarkFarmhash)
49 | b.Run(fmt.Sprintf("Farmhash_dgryski-%d", n), benchmarkFarmhash_dgryski)
50 | b.Run(fmt.Sprintf("Murmur3-%d", n), benchmarkMurmur3)
51 | b.Run(fmt.Sprintf("Highwayhash-%d", n), benchmarkHighwayhash)
52 | b.Run(fmt.Sprintf("XXHash64-%d", n), benchmarkXXHash64)
53 | b.Run(fmt.Sprintf("XXHash64_ASM-%d", n), benchmarkXXHash64_ASM)
54 | b.Run(fmt.Sprintf("MapHash64-%d", n), benchmarkMapHash64)
55 | fmt.Println()
56 | }
57 |
58 | }
59 |
60 | func benchmarkSha1(b *testing.B) {
61 | x := sha1.New()
62 |
63 | b.SetBytes(n)
64 | b.ResetTimer()
65 |
66 | for i := 0; i < b.N; i++ {
67 | x.Reset()
68 | x.Write(testBytes)
69 | _ = x.Sum(nil)
70 | }
71 | }
72 | func benchmarkSha256(b *testing.B) {
73 | x := sha256.New()
74 | b.SetBytes(n)
75 | b.ResetTimer()
76 |
77 | for i := 0; i < b.N; i++ {
78 | x.Reset()
79 | x.Write(testBytes)
80 | _ = x.Sum(nil)
81 | }
82 | }
83 |
84 | func benchmarkSha512(b *testing.B) {
85 | x := sha512.New()
86 | b.SetBytes(n)
87 | b.ResetTimer()
88 |
89 | for i := 0; i < b.N; i++ {
90 | x.Reset()
91 | x.Write(testBytes)
92 | _ = x.Sum(nil)
93 | }
94 | }
95 |
96 | func benchmarkMD5(b *testing.B) {
97 | x := md5.New()
98 | b.SetBytes(n)
99 | b.ResetTimer()
100 |
101 | for i := 0; i < b.N; i++ {
102 | x.Reset()
103 | x.Write(testBytes)
104 | _ = x.Sum(nil)
105 | }
106 | }
107 |
108 | func benchmarkCrc32(b *testing.B) {
109 | x := crc32.NewIEEE()
110 | b.SetBytes(n)
111 | b.ResetTimer()
112 |
113 | for i := 0; i < b.N; i++ {
114 | x.Reset()
115 | x.Write(testBytes)
116 | _ = x.Sum32()
117 | }
118 | }
119 |
120 | func benchmarkFnv(b *testing.B) {
121 | x := fnv.New64()
122 | b.SetBytes(n)
123 | b.ResetTimer()
124 |
125 | for i := 0; i < b.N; i++ {
126 | x.Reset()
127 | x.Write(testBytes)
128 | _ = x.Sum64()
129 | }
130 | }
131 |
132 | func benchmarkCityhash(b *testing.B) {
133 | b.SetBytes(n)
134 | b.ResetTimer()
135 |
136 | for i := 0; i < b.N; i++ {
137 | _ = cityhash.Hash64WithSeed(testBytes, 0xCAFE)
138 | }
139 | }
140 |
141 | func benchmarkFarmhash(b *testing.B) {
142 | b.SetBytes(n)
143 | b.ResetTimer()
144 |
145 | for i := 0; i < b.N; i++ {
146 | _ = farmhash.Hash64WithSeed(testBytes, 0xCAFE)
147 | }
148 | }
149 |
150 | func benchmarkFarmhash_dgryski(b *testing.B) {
151 | b.SetBytes(n)
152 | b.ResetTimer()
153 |
154 | for i := 0; i < b.N; i++ {
155 | _ = afarmhash.Hash64WithSeed(testBytes, 0xCAFE)
156 | }
157 | }
158 |
159 | func benchmarkMurmur3(b *testing.B) {
160 | x := murmur3.New64()
161 | b.SetBytes(n)
162 | b.ResetTimer()
163 |
164 | for i := 0; i < b.N; i++ {
165 | x.Reset()
166 | x.Write(testBytes)
167 | _ = x.Sum64()
168 | }
169 | }
170 | func benchmarkHighwayhash(b *testing.B) {
171 | key, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0FF0E0D0C0B0A090807060504030201000") // use your own key here
172 | x, _ := highwayhash.New64(key)
173 | b.SetBytes(n)
174 | b.ResetTimer()
175 |
176 | for i := 0; i < b.N; i++ {
177 | x.Reset()
178 | x.Write(testBytes)
179 | _ = x.Sum64()
180 | }
181 | }
182 |
183 | func benchmarkXXHash64(b *testing.B) {
184 | x := xxHash64.New(0xCAFE)
185 | b.SetBytes(n)
186 | b.ResetTimer()
187 |
188 | for i := 0; i < b.N; i++ {
189 | x.Reset()
190 | x.Write(testBytes)
191 | _ = x.Sum64()
192 | }
193 | }
194 |
195 | func benchmarkXXHash64_ASM(b *testing.B) {
196 | x := xxhashasm.New()
197 | b.SetBytes(n)
198 | b.ResetTimer()
199 |
200 | for i := 0; i < b.N; i++ {
201 | x.Reset()
202 | x.Write(testBytes)
203 | _ = x.Sum64()
204 | }
205 | }
206 |
207 | //go:noescape
208 | //go:linkname memhash runtime.memhash
209 | func memhash(p unsafe.Pointer, h, s uintptr) uintptr
210 |
211 | type stringStruct struct {
212 | str unsafe.Pointer
213 | len int
214 | }
215 |
216 | func MemHash(data []byte) uint64 {
217 | ss := (*stringStruct)(unsafe.Pointer(&data))
218 | return uint64(memhash(ss.str, 0, uintptr(ss.len)))
219 | }
220 | func benchmarkMapHash64(b *testing.B) {
221 | b.SetBytes(n)
222 | b.ResetTimer()
223 |
224 | for i := 0; i < b.N; i++ {
225 | _ = MemHash(testBytes)
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/s8/benchmark_parallel_test.go:
--------------------------------------------------------------------------------
1 | package s8_test
2 |
3 | import (
4 | "crypto/sha256"
5 | "fmt"
6 | "runtime"
7 | "testing"
8 | )
9 |
10 | func init() {
11 | fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(-1))
12 | }
13 |
14 | func BenchmarkParallelHash(b *testing.B) {
15 | testBytes := make([]byte, 1024)
16 |
17 | b.ResetTimer()
18 | b.SetParallelism(20)
19 | b.RunParallel(func(pb *testing.PB) {
20 | x := sha256.New()
21 | for pb.Next() {
22 | x.Reset()
23 | x.Write(testBytes)
24 | _ = x.Sum(nil)
25 | }
26 | })
27 | }
28 |
29 | func BenchmarkNonParallelHash(b *testing.B) {
30 | testBytes := make([]byte, 1024)
31 |
32 | b.ResetTimer()
33 | x := sha256.New()
34 | for i := 0; i < b.N; i++ {
35 | x.Reset()
36 | x.Write(testBytes)
37 | _ = x.Sum(nil)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/s8/benchstat.txt:
--------------------------------------------------------------------------------
1 | name old time/op new time/op delta
2 | Example-8 3.60µs ± 0% 0.17µs ± 1% -95.29% (p=0.008 n=5+5)
3 |
4 | name old alloc/op new alloc/op delta
5 | Example-8 32.0B ± 0% 8.0B ± 0% -75.00% (p=0.008 n=5+5)
6 |
7 | name old allocs/op new allocs/op delta
8 | Example-8 1.00 ± 0% 1.00 ± 0% ~ (all equal)
9 |
--------------------------------------------------------------------------------
/s8/benchstat_test.go:
--------------------------------------------------------------------------------
1 | package s8_test
2 |
3 | import (
4 | "crypto/sha256"
5 | "testing"
6 | )
7 |
8 | func BenchmarkExample(b *testing.B) {
9 | x := sha256.New()
10 | testBytes := make([]byte, 1024)
11 |
12 | b.ResetTimer()
13 | for i := 0; i < b.N; i++ {
14 | x.Reset()
15 | x.Write(testBytes)
16 | _ = x.Sum(nil)
17 | }
18 | }
19 |
20 | // func BenchmarkExample(b *testing.B) {
21 | // x := maphash.Hash{}
22 | // testBytes := make([]byte, 1024)
23 |
24 | // b.ResetTimer()
25 | // for i := 0; i < b.N; i++ {
26 | // x.Reset()
27 | // x.Write(testBytes)
28 | // _ = x.Sum(nil)
29 | // }
30 | // }
31 |
--------------------------------------------------------------------------------
/s8/new.txt:
--------------------------------------------------------------------------------
1 | goos: darwin
2 | goarch: amd64
3 | pkg: github.com/smallnest/go_test_workshop/s8
4 | cpu: VirtualApple @ 2.50GHz
5 | BenchmarkExample
6 | BenchmarkExample-8 7060740 169.0 ns/op 8 B/op 1 allocs/op
7 | BenchmarkExample-8 7102920 169.2 ns/op 8 B/op 1 allocs/op
8 | BenchmarkExample-8 7144312 168.8 ns/op 8 B/op 1 allocs/op
9 | BenchmarkExample-8 7112400 170.2 ns/op 8 B/op 1 allocs/op
10 | BenchmarkExample-8 7047000 172.0 ns/op 8 B/op 1 allocs/op
11 | PASS
12 | ok github.com/smallnest/go_test_workshop/s8 9.350s
13 |
--------------------------------------------------------------------------------
/s8/old.txt:
--------------------------------------------------------------------------------
1 | goos: darwin
2 | goarch: amd64
3 | pkg: github.com/smallnest/go_test_workshop/s8
4 | cpu: VirtualApple @ 2.50GHz
5 | BenchmarkExample
6 | BenchmarkExample-8 325957 3613 ns/op 32 B/op 1 allocs/op
7 | BenchmarkExample-8 331371 3607 ns/op 32 B/op 1 allocs/op
8 | BenchmarkExample-8 337330 3598 ns/op 32 B/op 1 allocs/op
9 | BenchmarkExample-8 332886 3605 ns/op 32 B/op 1 allocs/op
10 | BenchmarkExample-8 337428 3593 ns/op 32 B/op 1 allocs/op
11 | PASS
12 | ok github.com/smallnest/go_test_workshop/s8 6.571s
13 |
--------------------------------------------------------------------------------
/s9/example_test.go:
--------------------------------------------------------------------------------
1 | package io_test
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 | "log"
8 | "os"
9 | "strings"
10 | )
11 |
12 | func ExampleCopy() {
13 | r := strings.NewReader("some io.Reader stream to be read\n")
14 |
15 | if _, err := io.Copy(os.Stdout, r); err != nil {
16 | log.Fatal(err)
17 | }
18 |
19 | // Output:
20 | // some io.Reader stream to be read
21 | }
22 |
23 | func ExampleCopyBuffer() {
24 | r1 := strings.NewReader("first reader\n")
25 | r2 := strings.NewReader("second reader\n")
26 | buf := make([]byte, 8)
27 |
28 | // buf is used here...
29 | if _, err := io.CopyBuffer(os.Stdout, r1, buf); err != nil {
30 | log.Fatal(err)
31 | }
32 |
33 | // ... reused here also. No need to allocate an extra buffer.
34 | if _, err := io.CopyBuffer(os.Stdout, r2, buf); err != nil {
35 | log.Fatal(err)
36 | }
37 |
38 | // Output:
39 | // first reader
40 | // second reader
41 | }
42 |
43 | func ExampleCopyN() {
44 | r := strings.NewReader("some io.Reader stream to be read")
45 |
46 | if _, err := io.CopyN(os.Stdout, r, 4); err != nil {
47 | log.Fatal(err)
48 | }
49 |
50 | // Output:
51 | // some
52 | }
53 |
54 | func ExampleReadAtLeast() {
55 | r := strings.NewReader("some io.Reader stream to be read\n")
56 |
57 | buf := make([]byte, 14)
58 | if _, err := io.ReadAtLeast(r, buf, 4); err != nil {
59 | log.Fatal(err)
60 | }
61 | fmt.Printf("%s\n", buf)
62 |
63 | // buffer smaller than minimal read size.
64 | shortBuf := make([]byte, 3)
65 | if _, err := io.ReadAtLeast(r, shortBuf, 4); err != nil {
66 | fmt.Println("error:", err)
67 | }
68 |
69 | // minimal read size bigger than io.Reader stream
70 | longBuf := make([]byte, 64)
71 | if _, err := io.ReadAtLeast(r, longBuf, 64); err != nil {
72 | fmt.Println("error:", err)
73 | }
74 |
75 | // Output:
76 | // some io.Reader
77 | // error: short buffer
78 | // error: unexpected EOF
79 | }
80 |
81 | func ExampleReadFull() {
82 | r := strings.NewReader("some io.Reader stream to be read\n")
83 |
84 | buf := make([]byte, 4)
85 | if _, err := io.ReadFull(r, buf); err != nil {
86 | log.Fatal(err)
87 | }
88 | fmt.Printf("%s\n", buf)
89 |
90 | // minimal read size bigger than io.Reader stream
91 | longBuf := make([]byte, 64)
92 | if _, err := io.ReadFull(r, longBuf); err != nil {
93 | fmt.Println("error:", err)
94 | }
95 |
96 | // Output:
97 | // some
98 | // error: unexpected EOF
99 | }
100 |
101 | func ExampleWriteString() {
102 | if _, err := io.WriteString(os.Stdout, "Hello World"); err != nil {
103 | log.Fatal(err)
104 | }
105 |
106 | // Output: Hello World
107 | }
108 |
109 | func ExampleLimitReader() {
110 | r := strings.NewReader("some io.Reader stream to be read\n")
111 | lr := io.LimitReader(r, 4)
112 |
113 | if _, err := io.Copy(os.Stdout, lr); err != nil {
114 | log.Fatal(err)
115 | }
116 |
117 | // Output:
118 | // some
119 | }
120 |
121 | func ExampleMultiReader() {
122 | r1 := strings.NewReader("first reader ")
123 | r2 := strings.NewReader("second reader ")
124 | r3 := strings.NewReader("third reader\n")
125 | r := io.MultiReader(r1, r2, r3)
126 |
127 | if _, err := io.Copy(os.Stdout, r); err != nil {
128 | log.Fatal(err)
129 | }
130 |
131 | // Output:
132 | // first reader second reader third reader
133 | }
134 |
135 | func ExampleTeeReader() {
136 | var r io.Reader = strings.NewReader("some io.Reader stream to be read\n")
137 |
138 | r = io.TeeReader(r, os.Stdout)
139 |
140 | // Everything read from r will be copied to stdout.
141 | if _, err := io.ReadAll(r); err != nil {
142 | log.Fatal(err)
143 | }
144 |
145 | // Output:
146 | // some io.Reader stream to be read
147 | }
148 |
149 | func ExampleSectionReader() {
150 | r := strings.NewReader("some io.Reader stream to be read\n")
151 | s := io.NewSectionReader(r, 5, 17)
152 |
153 | if _, err := io.Copy(os.Stdout, s); err != nil {
154 | log.Fatal(err)
155 | }
156 |
157 | // Output:
158 | // io.Reader stream
159 | }
160 |
161 | func ExampleSectionReader_Read() {
162 | r := strings.NewReader("some io.Reader stream to be read\n")
163 | s := io.NewSectionReader(r, 5, 17)
164 |
165 | buf := make([]byte, 9)
166 | if _, err := s.Read(buf); err != nil {
167 | log.Fatal(err)
168 | }
169 |
170 | fmt.Printf("%s\n", buf)
171 |
172 | // Output:
173 | // io.Reader
174 | }
175 |
176 | func ExampleSectionReader_ReadAt() {
177 | r := strings.NewReader("some io.Reader stream to be read\n")
178 | s := io.NewSectionReader(r, 5, 17)
179 |
180 | buf := make([]byte, 6)
181 | if _, err := s.ReadAt(buf, 10); err != nil {
182 | log.Fatal(err)
183 | }
184 |
185 | fmt.Printf("%s\n", buf)
186 |
187 | // Output:
188 | // stream
189 | }
190 |
191 | func ExampleSectionReader_Seek() {
192 | r := strings.NewReader("some io.Reader stream to be read\n")
193 | s := io.NewSectionReader(r, 5, 17)
194 |
195 | if _, err := s.Seek(10, io.SeekStart); err != nil {
196 | log.Fatal(err)
197 | }
198 |
199 | if _, err := io.Copy(os.Stdout, s); err != nil {
200 | log.Fatal(err)
201 | }
202 |
203 | // Output:
204 | // stream
205 | }
206 |
207 | func ExampleSectionReader_Size() {
208 | r := strings.NewReader("some io.Reader stream to be read\n")
209 | s := io.NewSectionReader(r, 5, 17)
210 |
211 | fmt.Println(s.Size())
212 |
213 | // Output:
214 | // 17
215 | }
216 |
217 | func ExampleSeeker_Seek() {
218 | r := strings.NewReader("some io.Reader stream to be read\n")
219 |
220 | r.Seek(5, io.SeekStart) // move to the 5th char from the start
221 | if _, err := io.Copy(os.Stdout, r); err != nil {
222 | log.Fatal(err)
223 | }
224 |
225 | r.Seek(-5, io.SeekEnd)
226 | if _, err := io.Copy(os.Stdout, r); err != nil {
227 | log.Fatal(err)
228 | }
229 |
230 | // Output:
231 | // io.Reader stream to be read
232 | // read
233 | }
234 |
235 | func ExampleMultiWriter() {
236 | r := strings.NewReader("some io.Reader stream to be read\n")
237 |
238 | var buf1, buf2 bytes.Buffer
239 | w := io.MultiWriter(&buf1, &buf2)
240 |
241 | if _, err := io.Copy(w, r); err != nil {
242 | log.Fatal(err)
243 | }
244 |
245 | fmt.Print(buf1.String())
246 | fmt.Print(buf2.String())
247 |
248 | // Output:
249 | // some io.Reader stream to be read
250 | // some io.Reader stream to be read
251 | }
252 |
253 | func ExamplePipe() {
254 | r, w := io.Pipe()
255 |
256 | go func() {
257 | fmt.Fprint(w, "some io.Reader stream to be read\n")
258 | w.Close()
259 | }()
260 |
261 | if _, err := io.Copy(os.Stdout, r); err != nil {
262 | log.Fatal(err)
263 | }
264 |
265 | // Output:
266 | // some io.Reader stream to be read
267 | }
268 |
269 | func ExampleReadAll() {
270 | r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")
271 |
272 | b, err := io.ReadAll(r)
273 | if err != nil {
274 | log.Fatal(err)
275 | }
276 |
277 | fmt.Printf("%s", b)
278 |
279 | // Output:
280 | // Go is a general-purpose language designed with systems programming in mind.
281 | }
282 |
--------------------------------------------------------------------------------