{"id":346639,"date":"2023-03-13T09:00:21","date_gmt":"2023-03-13T09:00:21","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=346639"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=346639","title":{"rendered":"<span>\u0427\u0442\u043e \u043d\u0435 \u0442\u0430\u043a \u0441 \u0442\u0432\u043e\u0438\u043c \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435\u043c \u0442\u0435\u0441\u0442\u043e\u0432 \u0432 Go?<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/122\/06d\/223\/12206d2238d0b0445cb29274c1734aa7.jpeg\" width=\"1200\" height=\"838\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/122\/06d\/223\/12206d2238d0b0445cb29274c1734aa7.jpeg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0411\u044e\u0440\u043e\u043a\u0440\u0430\u0442\u0438\u044f \u0441\u0435\u043c\u0438\u043c\u0438\u043b\u044c\u043d\u044b\u043c\u0438 \u0448\u0430\u0433\u0430\u043c\u0438 \u0432\u043d\u0435\u0434\u0440\u044f\u0435\u0442\u0441\u044f \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u041b\u044e\u0434\u0435\u0439 \u0432 \u043f\u0438\u0434\u0436\u0430\u043a\u0430\u0445 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 \u043b\u0438\u0448\u044c \u0446\u0438\u0444\u0440\u044b, \u0438 \u044d\u0442\u043e \u0436\u0435 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a test coverage \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. \u041e\u0434\u043d\u0430\u043a\u043e, \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0437\u0430\u0447\u0430\u0441\u0442\u0443\u044e (\u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044f\u043c \u044f\u0437\u044b\u043a\u0430) \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u044b \u043c\u0438\u0440\u0430. \u0422\u0430\u043a \u043b\u0438 \u0432\u0441\u0435 \u043f\u043b\u043e\u0445\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435?    <\/p>\n<h2>\u042d\u043a\u0441\u043f\u043e\u0437\u0438\u0446\u0438\u044f<\/h2>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<pre><code class=\"go\">type Service1 interface { DoSmth(ctx context.Context, ids []int) ([]Model, error) }  type service1 struct { repo Repository }<\/code><\/pre>\n<p>\u0421 \u0442\u0430\u043a\u0438\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/p>\n<pre><code class=\"go\">package service1  import ( \"context\" \"github.com\/google\/uuid\" \"github.com\/pkg\/errors\" \"habr-test-coverage\/internal\/helper\" )  var ( ErrLogic      = errors.New(\"logical error\") ErrValidation = errors.New(\"validation error\") )  func (s *service1) DoSmth(ctx context.Context, ids []int) ([]Model, error) { if len(ids) == 0 { \/\/ 1. invalid params return []Model{}, nil }  if helper.HasDuplicates(ids) { \/\/ 2. bll validation error return nil, errors.Wrapf(ErrValidation, \"duplicates detected in ids: %v\", ids) }  res, err := s.repo.GetSmth(ctx, ids) if err != nil { \/\/ 3. mockable dependency error return nil, err }  if len(res) != len(ids) { \/\/ 4. logical error return nil, ErrLogic }  for i := range res { randData, err := uuid.NewRandom() if err != nil { \/\/ 5. external package error return nil, err }  if randData == uuid.Nil { \/\/ 6. unreachable code panic(\"should not happen ever, we lost faith in humanity\") }  res[i].Data = randData.String() }  \/\/ 7. result return res, nil }<\/code><\/pre>\n<h3>\u0417\u0430\u0432\u044f\u0437\u043a\u0430<\/h3>\n<p>\u0412 \u044d\u0442\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0432\u0438\u0434\u043e\u0432 \u043e\u0448\u0438\u0431\u043e\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0432 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442. \u0412\u043e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c?<\/p>\n<p>\u041e\u0448\u0438\u0431\u043a\u0438 \u0432\u0438\u0434\u0430 <strong>3<\/strong> \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b\u0430, \u0442.\u043a. \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u0430\u043c\u0438 \u0432 \u0445\u043e\u0434\u0435 \u0442\u0435\u0441\u0442\u0430 \u0437\u0430 \u0441\u0447\u0435\u0442 \u043c\u043e\u043a\u043e\u0432. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u043b\u044c\u0437\u0430 \u043e\u0442 \u043d\u0438\u0445, \u0443\u0431\u0435\u0440\u0435\u0447\u044c \u0441\u0435\u0431\u044f \u043e\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0433\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043e\u0442 \u0447\u0435\u0433\u043e \u043e\u0442\u044b\u0433\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u043d\u043e \u0438 \u0437\u0430\u0442\u0440\u0430\u0442\u043d\u043e \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. <\/p>\n<p>\u0422\u043e\u0436\u0435 \u0438 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0432\u0438\u0434\u0430 <strong>5<\/strong>, \u043e\u043d\u0438 \u043b\u0438\u0431\u043e \u0436\u0435 \u043d\u0435 \u043f\u043e\u0434 \u043d\u0430\u0448\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u043c, \u043b\u0438\u0431\u043e \u0436\u0435 \u0438\u043c\u0435\u044e\u0442 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0435 \u043a\u0435\u0439\u0441\u044b \u0434\u043b\u044f \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043e\u043a: \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0441\u043b\u0430\u0442\u044c \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u0447\u0442\u043e \u0432 \u0442\u0435\u043e\u0440\u0438\u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c\u043e \u0432 \u0442\u0435\u0441\u0442 \u043a\u0435\u0439\u0441\u0435, \u043e\u0434\u043d\u0430\u043a\u043e, \u043f\u043e\u0447\u0442\u0438 \u043d\u0435 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u044b \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438. <\/p>\n<p>\u041e\u0448\u0438\u0431\u043a\u0438 \u0432\u0438\u0434\u0430 <strong>6<\/strong> \u043f\u043e-\u0445\u043e\u0440\u043e\u0448\u0435\u043c\u0443 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c\u0441\u044f, \u043d\u043e \u043e\u043d\u0438 \u0447\u0430\u0441\u0442\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0438 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. \u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u043a\u0435\u0439\u0441 \u0437\u0430\u0447\u0430\u0441\u0442\u0443\u044e \u043d\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c.<\/p>\n<h2>\u0420\u0430\u0437\u0432\u0438\u0442\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f<\/h2>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u0435\u0439\u0441\u044b \u0432\u0438\u0434\u0430 <strong>1, 2, 4, 7<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0442\u0435\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">package service1  import ( \"context\" \"testing\"  \"github.com\/gojuno\/minimock\/v3\" \"github.com\/google\/uuid\" \"github.com\/stretchr\/testify\/require\" )  type mocksService struct { repo *RepositoryMock }  func newMocksService(t *testing.T) *mocksService { ctrl := minimock.NewController(t) return &amp;mocksService{ repo: NewRepositoryMock(ctrl), } }  func TestDoSmth(t *testing.T) { t.Parallel() ctx := context.TODO() require := require.New(t)  type in struct { ids []int } type out struct { res []Model err error }  tests := []struct { name   string in     in setup  func(*mocksService, *in) assert func(*in, *out) }{ { name:  \"check invalid params\", in:    in{ids: nil}, setup: func(service *mocksService, in *in) {}, assert: func(in *in, out *out) { require.NoError(out.err) require.Empty(out.res) }, }, { name:  \"check validation error\", in:    in{ids: []int{1, 2, 2}}, setup: func(service *mocksService, in *in) {}, assert: func(in *in, out *out) { require.ErrorIs(out.err, ErrValidation) require.Nil(out.res) }, }, { name: \"check logical error\", in:   in{ids: []int{1, 2, 3}}, setup: func(m *mocksService, in *in) { m.repo.GetSmthMock. Expect(ctx, in.ids). Return([]Model{{}, {}}, nil) }, assert: func(in *in, out *out) { require.ErrorIs(out.err, ErrLogic) require.Nil(out.res) }, }, { name: \"success\", in:   in{ids: []int{1, 2, 3}}, setup: func(m *mocksService, in *in) { m.repo.GetSmthMock. Expect(ctx, in.ids). Return([]Model{{ID: 1}, {ID: 2}, {ID: 3}}, nil) }, assert: func(in *in, out *out) { for i, r := range out.res { require.Equal(r.ID, in.ids[i]) _, err := uuid.Parse(r.Data) require.NoError(err) }  require.NoError(out.err) }, }, }  for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel()  m := newMocksService(t) s := NewService1(m.repo)  tt.setup(m, &amp;tt.in)  res, err := s.DoSmth(ctx, tt.in.ids) tt.assert(&amp;tt.in, &amp;out{ res: res, err: err, }) }) } } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u041a\u0443\u043b\u044c\u043c\u0438\u043d\u0430\u0446\u0438\u044f<\/h2>\n<details class=\"spoiler\">\n<summary>\u041d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c 21.1% \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\">go test .\/... -coverprofile cover.out &amp;&amp; go tool cover -func cover.out<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dad\/06d\/d61\/dad06dd61cbbf36e601f58f03c1d85d3.png\" alt=\"cover report\" title=\"cover report\" width=\"883\" height=\"27\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dad\/06d\/d61\/dad06dd61cbbf36e601f58f03c1d85d3.png\"\/><\/p>\n<div><figcaption>cover report<\/figcaption><\/div>\n<\/figure>\n<\/div>\n<\/details>\n<p>\u041c\u0430\u043b\u043e\u0432\u0430\u0442\u043e \u043e\u0434\u043d\u0430\u043a\u043e, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0432 <abbr class=\"habraabbr\" title=\"go tool cover -html cover.out\" data-title=\"&lt;p>go tool cover -html cover.out&lt;\/p>&#187; data-abbr=&#187;html&#187;>html<\/abbr><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a18\/957\/016\/a18957016a917bf40b2c455de2c6be01.png\" width=\"916\" height=\"780\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a18\/957\/016\/a18957016a917bf40b2c455de2c6be01.png\"\/><\/figure>\n<p><strong>82.4% <\/strong>\u043f\u043e\u043a\u0440\u044b\u0442\u043e, \u043d\u0430\u0441 \u044d\u0442\u043e \u0432\u043f\u043e\u043b\u043d\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442, \u043d\u043e \u0432 \u0447\u0435\u043c \u0442\u043e\u0433\u0434\u0430 \u0437\u0430\u0433\u0432\u043e\u0437\u0434\u043a\u0430? \u0421\u043c\u043e\u0442\u0440\u0438\u043c \u0437\u0434\u0435\u0441\u044c \u0436\u0435 \u043d\u0430 \u0432\u0435\u0441\u044c coverage report<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b8f\/fd8\/ee7\/b8ffd8ee73408af2a13e3be4a878aac8.png\" width=\"500\" height=\"142\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b8f\/fd8\/ee7\/b8ffd8ee73408af2a13e3be4a878aac8.png\"\/><\/figure>\n<h2>\u0420\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0412\u0438\u0434\u0438\u043c \u043c\u044b \u0442\u043e, \u0447\u0442\u043e \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u043b\u043e\u0441\u044c \u0442\u0430\u043c, \u0433\u0434\u0435 \u043d\u0430\u043c \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u043e. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/ff6\/3ea\/d4c\/ff63ead4c8261a7c8b69233bd6fe8c72.png\" width=\"236\" height=\"247\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ff6\/3ea\/d4c\/ff63ead4c8261a7c8b69233bd6fe8c72.png\"\/><\/figure>\n<blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u043f\u043e\u043b\u043d\u0435 \u0437\u0430\u043a\u043e\u043d\u043e\u043c\u0435\u0440\u0435\u043d, \u0443\u0442\u0438\u043b\u0438\u0442\u0430 <em>cover<\/em> \u043f\u043e \u0434\u0435\u0444\u043e\u043b\u0442\u0443 \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0434\u043b\u044f <strong>\u0432\u0441\u0435\u0445<\/strong> <abbr class=\"habraabbr\" title=\"\u0415\u0441\u043b\u0438 \u043e\u043d\u0438 \u043d\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u043d\u0430 . \u0438\u043b\u0438 _ https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns\" data-title=\"&lt;p>\u0415\u0441\u043b\u0438 \u043e\u043d\u0438 \u043d\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u043d\u0430 . \u0438\u043b\u0438 _&amp;nbsp;&lt;\/p>&lt;p>&lt;a href=&quot;https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns&quot;>https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns&lt;\/a>&lt;\/p>&lt;p>&lt;\/p>&#187; data-abbr=&#187;\u0433\u043e\u0448\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432&#187;>\u0433\u043e\u0448\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432*<\/abbr> \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u0435\u0441\u043b\u0438 \u0432 <abbr class=\"habraabbr\" title=\"\u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u043d\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f testdata https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns\" data-title=\"&lt;p>\u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u043d\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f testdata&lt;br>&lt;a href=&quot;https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns&quot;>https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns&lt;\/a>&lt;\/p>&lt;p>&lt;\/p>&#187; data-abbr=&#187;\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438&#187;>\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438*<\/abbr> \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f <strong>\u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d<\/strong> \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b<\/p>\n<\/blockquote>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430, \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u043c\u043e\u043a \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u0440\u0430\u0441\u0447\u0435\u0442\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f, \u0442.\u043a. \u043e\u043d\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438 \u043c\u043e\u0433\u0443\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c 1000+ \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0432 \u044d\u0442\u043e\u043c \u043f\u043b\u0430\u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u0436\u0435\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u0430\u043a\u043e\u0439 \u0445\u0438\u0442\u0440\u043e\u0441\u0442\u044c\u044e<\/p>\n<pre><code class=\"bash\">go test .\/... -coverprofile cover.out.tmp &amp;&amp; cat cover.out.tmp | grep -v \"_mock.go\" > cover.out &amp;&amp; rm cover.out.tmp &amp;&amp; go tool cover -func cover.out<\/code><\/pre>\n<ol>\n<li>\n<p>\u0417\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u043f\u043e\u0440\u0442 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u0432 tmp \u0444\u0430\u0439\u043b<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u0432 \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b, \u043a\u0440\u043e\u043c\u0435 \u0441\u0442\u0440\u043e\u043a, \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u044e\u0449\u0438\u0445\u0441\u044f \u043d\u0430 \u0442\u043e\u0442 \u043f\u0430\u0442\u0442\u0435\u0440\u043d, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043c\u043e\u043a \u0444\u0430\u0439\u043b\u043e\u0432. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e <code>_mock.go<\/code><\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u0430\u043b\u044f\u0435\u043c tmp \u0444\u0430\u0439\u043b<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c cover<\/p>\n<\/li>\n<\/ol>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 <strong>65.2%:<\/strong><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/070\/c7a\/44b\/070c7a44b6c023dd0bd6a3f564377050.png\" width=\"698\" height=\"198\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/070\/c7a\/44b\/070c7a44b6c023dd0bd6a3f564377050.png\"\/><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0447\u0442\u043e \u0432 \u043e\u0442\u0434\u0435\u043b\u0435 \u043c\u044b \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043b\u0438\u0441\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0438\u0437\u043d\u0435\u0441 \u043b\u043e\u0433\u0438\u043a\u0443, \u0442\u043e \u0431\u0438\u0448\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 <abbr class=\"habraabbr\" title=\"data access layer\" data-title=\"&lt;p>data access layer&lt;\/p>&#187; data-abbr=&#187;dal&#187;>dal<\/abbr> \u0438 <abbr class=\"habraabbr\" title=\"application layer \u0438\u043b\u0438 \u0436\u0435 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0445\u044d\u043d\u0434\u043b\u0435\u0440\u043e\u0432(\u0440\u0443\u0447\u0435\u043a)\" data-title=\"&lt;p>application layer \u0438\u043b\u0438 \u0436\u0435 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0445\u044d\u043d\u0434\u043b\u0435\u0440\u043e\u0432(\u0440\u0443\u0447\u0435\u043a)&lt;\/p>&#187; data-abbr=&#187;app&#187;>app<\/abbr> \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c <abbr class=\"habraabbr\" title=\"end-to-end \u0442\u0435\u0441\u0442\u044b \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u043b\u0438 \u0436\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439\" data-title=\"&lt;p>end-to-end \u0442\u0435\u0441\u0442\u044b \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u043b\u0438 \u0436\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439&lt;\/p>&#187; data-abbr=&#187;e2e&#187;>e2e<\/abbr> \u0442\u0435\u0441\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0438. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0442\u0443 \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0441\u043c\u044b\u0441\u043b\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043c\u0435\u0442\u043e\u0434\u044b \u043c\u043e\u0434\u0435\u043b\u0435\u043a. \u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043b\u0435\u0433\u043a\u0430 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c6e\/05f\/86b\/c6e05f86b9ea1b8e55c22e6f1c259e4d.png\" width=\"244\" height=\"247\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c6e\/05f\/86b\/c6e05f86b9ea1b8e55c22e6f1c259e4d.png\"\/><\/figure>\n<p>\u041e\u0442\u0434\u0435\u043b\u0438\u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438, \u043c\u044b \u043d\u0435 \u0431\u0435\u0440\u0435\u043c \u0432 \u0440\u0430\u0441\u0447\u0435\u0442 \u043d\u0435\u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c <strong>83.3% <\/strong>\u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/300\/76e\/d76\/30076ed768b5ed7e8241afa8e193c58c.png\" width=\"697\" height=\"74\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/300\/76e\/d76\/30076ed768b5ed7e8241afa8e193c58c.png\"\/><\/figure>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043b\u0438 coverage <strong>\u0441 20 \u0434\u043e >80%<\/strong> \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u043d\u0438 \u0435\u0434\u0438\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u0447\u043a\u0438 \u043a\u043e\u0434\u0430. <\/p>\n<h2>\u0418\u0441\u043f\u044b\u0442\u0430\u043d\u0438\u0435<\/h2>\n<p>\u041d\u043e \u0432\u0441\u0435 \u043b\u0438 \u0443 \u043d\u0430\u0441 \u0445\u043e\u0440\u043e\u0448\u043e \u0442\u0435\u043f\u0435\u0440\u044c? \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f \u0435\u0449\u0435 \u0440\u0430\u0437\u043e\u043a<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b0f\/ce8\/731\/b0fce873176a76b33055d87440a403ef.png\" width=\"252\" height=\"347\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b0f\/ce8\/731\/b0fce873176a76b33055d87440a403ef.png\"\/><\/figure>\n<p>\u0410 \u0443 \u043d\u0430\u0441 \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0435\u0441\u0442\u044c \u0435\u0449\u0435 \u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0431\u0435\u0437 \u0435\u0434\u0438\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u043b\u0441\u044f \u0432 \u0440\u0435\u043f\u043e\u0440\u0442\u0435, \u0442.\u043a. \u0432 \u043d\u0435\u043c \u043d\u0435\u0442 \u0442\u0435\u0441\u0442\u043e\u0432. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0447\u0442\u043e-\u0442\u043e \u0441 \u044d\u0442\u0438\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c: \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043f\u043e\u0434 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0435\u0439 <code>bll<\/code>  \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u044d\u0442\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0432 \u0440\u0435\u043f\u043e\u0440\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0444\u043b\u0430\u0433 <a href=\"https:\/\/pkg.go.dev\/cmd\/go#hdr-Testing_flags\" rel=\"noopener noreferrer nofollow\">-coverpkg<\/a><\/p>\n<pre><code class=\"bash\">go test .\/... -coverpkg='.\/internal\/service\/...\/bll\/...' -coverprofile cover.out.tmp  &amp;&amp; cat cover.out.tmp | grep -v \"_mock.go\" > cover.out &amp;&amp; rm cover.out.tmp &amp;&amp; go tool cover -func cover.out<\/code><\/pre>\n<p>\u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u0436\u0435 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u0443 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0432\u0438\u0434\u044f \u043d\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043b\u043e\u0433\u0438\u043a\u0438<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/69c\/75f\/ba7\/69c75fba77d7e52485b225479e34ce4d.png\" width=\"697\" height=\"176\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/69c\/75f\/ba7\/69c75fba77d7e52485b225479e34ce4d.png\"\/><\/figure>\n<p>\u0414\u043e\u043f\u0438\u0441\u0430\u0432 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u044e\u0449\u0438\u0435 \u0442\u0435\u0441\u0442\u044b, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c <strong>84.2%<\/strong><\/p>\n<h2>\u0420\u0435\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u0430<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043f\u0435\u0440\u0432\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u0443, \u0442\u0443\u0442 \u0435\u0449\u0435 \u043e\u0441\u0442\u0430\u043b\u0438\u0441\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u0438<\/p>\n<details class=\"spoiler\">\n<summary>\u043f\u0435\u0440\u0432\u044b\u0439 \u0442\u0435\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c97\/974\/320\/c97974320a12bae7d5a73ab29d132e6f.png\" width=\"918\" height=\"768\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c97\/974\/320\/c97974320a12bae7d5a73ab29d132e6f.png\"\/><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e \u043f\u043e\u0432\u043e\u0434\u0443\u00a0\u043a\u0435\u0439\u0441\u0430 <strong>6<\/strong>. \u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043c\u044b \u043d\u0435\u00a0\u043c\u043e\u0436\u0435\u043c, \u0438 \u0441\u0442\u0440\u0430\u043d\u043d\u043e, \u0447\u0442\u043e\u00a0\u0442\u0430\u043a\u043e\u0439 \u0431\u043b\u043e\u043a \u0432\u043e\u043e\u0431\u0449\u0435 \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432\u00a0\u0440\u0435\u043f\u043e\u0440\u0442. \u0423\u043c\u0435\u0441\u0442\u0435\u043d\u00a0\u0431\u044b\u043b\u00a0\u0431\u044b \u0432\u043e\u043f\u0440\u043e\u0441: \u0430\u00a0\u0435\u0441\u0442\u044c\u00a0\u043b\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u043f\u043e\u043c\u0435\u0442\u0438\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043a\u0443\u0441\u043a\u0438 \u043a\u043e\u0434\u0430 \u043a\u0430\u043a\u00a0\u043d\u0435\u0434\u043e\u0441\u044f\u0433\u0430\u0435\u043c\u044b\u0435 \u0434\u043b\u044f\u00a0\u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f, \u0447\u0442\u043e\u0431\u044b\u00a0\u043b\u0438\u0448\u043d\u0438\u0439 \u0440\u0430\u0437 \u043d\u0435\u00a0\u043f\u043e\u0440\u0442\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443, \u0438\u043b\u0438 \u0436\u0435 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043f\u043e \u0442\u0438\u043f\u0443 \u043f\u0430\u043d\u0438\u043a \u043f\u0440\u0438 \u043f\u043e\u0434\u0441\u0447\u0435\u0442\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f?<\/p>\n<p>\u042d\u0442\u043e\u0442 \u0432\u043e\u043f\u0440\u043e\u0441 \u043e\u0431\u0441\u0443\u0436\u0434\u0430\u043b\u0441\u044f \u043d\u0435\u00a0\u0440\u0430\u0437 \u043d\u0430\u00a0\u0444\u043e\u0440\u0443\u043c\u0430\u0445 \u0438 issue: <a href=\"https:\/\/github.com\/golang\/go\/issues\/53271\" rel=\"noopener noreferrer nofollow\">1<\/a>, <a href=\"https:\/\/github.com\/golang\/go\/issues\/31280\" rel=\"noopener noreferrer nofollow\">2<\/a>, \u043d\u043e\u00a0\u0435\u0441\u043b\u0438 \u0432\u043a\u0440\u0430\u0442\u0446\u0435, \u0442\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430\u00a0\u0432\u0441\u0435 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0442\u00a0\u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438 \u043c\u043e\u0436\u043d\u043e \u0441\u0432\u0435\u0441\u0442\u0438 \u043a\u00a0<a href=\"https:\/\/github.com\/golang\/go\/issues\/34639#issuecomment-537327615\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u043c\u0443<\/a> \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0443 \u0420\u043e\u0431\u0430 \u041f\u0430\u0439\u043a\u0430 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 issue. \u0425\u043e\u0442\u044f, \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u043f\u0430\u0440\u0443 \u043d\u0435\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 <a href=\"https:\/\/github.com\/dave\/courtney\" rel=\"noopener noreferrer nofollow\">\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0435\u0448\u0430\u044e\u0442 \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.<\/p>\n<h2>\u0424\u0438\u043d\u0430\u043b<\/h2>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u043d\u0435\u0434\u043e\u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u043b\u0443\u0447\u0448\u0438\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438 \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0435 \u0434\u0438\u0441\u043a\u0443\u0441\u0441\u0438\u0438 \u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438.<\/p>\n<p>\u0421\u043e\u0446 \u0441\u0435\u0442\u0438:<\/p>\n<p><a href=\"https:\/\/www.linkedin.com\/in\/dishanov\/\" rel=\"noopener noreferrer nofollow\">https:\/\/www.linkedin.com\/in\/dishanov\/<\/a><\/p>\n<h2>\u042d\u043f\u0438\u043b\u043e\u0433<\/h2>\n<details class=\"spoiler\">\n<summary>\u0414\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a -coverpkg<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0415\u0449\u0435 \u043e\u0434\u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0444\u043b\u0430\u0433\u0430 <code>coverpkg<\/code>, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f <strong>A<\/strong> \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0444\u043b\u0430\u0433\u0430, \u0442\u043e \u0432\u0441\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u043d\u0435\u0439 \u043f\u0440\u0438 \u0438\u0445 \u0432\u044b\u0437\u043e\u0432\u0435 \u0441 \u0442\u0435\u0441\u0442\u0430 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <strong>B<\/strong> \u0431\u0443\u0434\u0443\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043a\u0440\u044b\u0442\u044b\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0432 \u0440\u0435\u043f\u043e\u0440\u0442\u0435, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043a \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044e \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0442\u043e\u0447\u0435\u0447\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b. <\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u0435 \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>HasDuplicates<\/code> \u0438\u0437 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>helper<\/code>, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 <u>\u043d\u0435\u0442<\/u> \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<details class=\"spoiler\">\n<summary>HasDuplicates<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func HasDuplicates(slice []int) bool { set := make(map[int]bool) for _, v := range slice { if set[v] { return true } set[v] = true } return false }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0432\u043a\u043b\u044e\u0447\u0430\u044f <code>internal\/helper<\/code> \u0432 coverpkg<\/p>\n<pre><code class=\"bash\">go test .\/... -coverpkg='.\/internal\/helper\/...','.\/internal\/service\/...\/bll\/...' -coverprofile cover.out.tmp  &amp;&amp; cat cover.out.tmp | grep -v \"_mock.go\" > cover.out &amp;&amp; rm cover.out.tmp &amp;&amp; go tool cover -html cover.out <\/code><\/pre>\n<p>\u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/cdc\/517\/427\/cdc517427c9f4da583f4991e0f9e9158.png\" width=\"440\" height=\"284\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cdc\/517\/427\/cdc517427c9f4da583f4991e0f9e9158.png\"\/><\/figure>\n<p>\u0418\u043d\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0431\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c, \u0430 \u0437\u0430\u0447\u0430\u0441\u0442\u0443\u044e \u043c\u043e\u0436\u0435\u0442 \u0432\u0440\u0435\u0434\u0438\u0442\u044c \u043a\u0430\u043a \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0432\u0435\u0434\u044c \u043c\u044b \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u044d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f\u0445.<\/p>\n<p>*\u0415\u0441\u0442\u044c \u043e\u0434\u0438\u043d \u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442. \u0411\u0430\u0433 \u044d\u0442\u043e \u0438\u043b\u0438 \u0444\u0438\u0447\u0430, \u043d\u043e \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e <code>'.\/internal\/helper\/...'<\/code>\u0432<code>coverpkg<\/code>, \u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u0436\u0435 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043a\u0440\u044b\u0442\u043e\u0439 \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u043f\u043e \u0444\u0430\u043a\u0442\u0443 \u0435\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0432 \u0442\u0435\u0441\u0442\u0435 <code>DoSmth<\/code> \u0438 \u0432  \u0441\u043a\u0440\u0438\u043f\u0442\u0435 \u043c\u044b \u0432\u0441\u0435 \u0435\u0449\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435: <code>go test .\/...<\/code><\/p>\n<p>\u0418\u043d\u043e\u0439 \u0440\u0430\u0437 \u044f \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u043b \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0443 (\u043a\u0440\u0430\u0439\u043d\u0435 \u043d\u0435 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0435\u0435), \u043a\u043e\u0433\u0434\u0430 \u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u044b \u043f\u0438\u0441\u0430\u043b\u0438 \u0432 \u0441\u043b\u043e\u0435 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 (app), \u0430 \u043d\u0435 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u043c \u0441\u043b\u043e\u0435. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u043f\u043e \u0444\u0430\u043a\u0442\u0443 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0431\u0438\u0437\u043d\u0435\u0441 \u043b\u043e\u0433\u0438\u043a\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043d\u043e \u0435\u0441\u043b\u0438 <strong>\u043d\u0435<\/strong> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>coverpkg<\/code>, \u0442\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f \u0432 \u0440\u0435\u043f\u043e\u0440\u0442 \u043d\u0435 \u043f\u043e\u043f\u0430\u0434\u0435\u0442, \u0447\u0442\u043e \u043d\u0435 \u0435\u0441\u0442\u044c \u0445\u043e\u0440\u043e\u0448\u043e.<\/p>\n<\/p>\n<\/p>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/721958\/\"> https:\/\/habr.com\/ru\/post\/721958\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><\/figure>\n<p>\u0411\u044e\u0440\u043e\u043a\u0440\u0430\u0442\u0438\u044f \u0441\u0435\u043c\u0438\u043c\u0438\u043b\u044c\u043d\u044b\u043c\u0438 \u0448\u0430\u0433\u0430\u043c\u0438 \u0432\u043d\u0435\u0434\u0440\u044f\u0435\u0442\u0441\u044f \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u041b\u044e\u0434\u0435\u0439 \u0432 \u043f\u0438\u0434\u0436\u0430\u043a\u0430\u0445 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0442 \u043b\u0438\u0448\u044c \u0446\u0438\u0444\u0440\u044b, \u0438 \u044d\u0442\u043e \u0436\u0435 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0441\u044f \u043a test coverage \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432. \u041e\u0434\u043d\u0430\u043a\u043e, \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0437\u0430\u0447\u0430\u0441\u0442\u0443\u044e (\u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044f\u043c \u044f\u0437\u044b\u043a\u0430) \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u043f\u043e\u043b\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u044b \u043c\u0438\u0440\u0430. \u0422\u0430\u043a \u043b\u0438 \u0432\u0441\u0435 \u043f\u043b\u043e\u0445\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435?    <\/p>\n<h2>\u042d\u043a\u0441\u043f\u043e\u0437\u0438\u0446\u0438\u044f<\/h2>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<pre><code class=\"go\">type Service1 interface { DoSmth(ctx context.Context, ids []int) ([]Model, error) }  type service1 struct { repo Repository }<\/code><\/pre>\n<p>\u0421 \u0442\u0430\u043a\u0438\u043c \u043c\u0435\u0442\u043e\u0434\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/p>\n<pre><code class=\"go\">package service1  import ( \"context\" \"github.com\/google\/uuid\" \"github.com\/pkg\/errors\" \"habr-test-coverage\/internal\/helper\" )  var ( ErrLogic      = errors.New(\"logical error\") ErrValidation = errors.New(\"validation error\") )  func (s *service1) DoSmth(ctx context.Context, ids []int) ([]Model, error) { if len(ids) == 0 { \/\/ 1. invalid params return []Model{}, nil }  if helper.HasDuplicates(ids) { \/\/ 2. bll validation error return nil, errors.Wrapf(ErrValidation, \"duplicates detected in ids: %v\", ids) }  res, err := s.repo.GetSmth(ctx, ids) if err != nil { \/\/ 3. mockable dependency error return nil, err }  if len(res) != len(ids) { \/\/ 4. logical error return nil, ErrLogic }  for i := range res { randData, err := uuid.NewRandom() if err != nil { \/\/ 5. external package error return nil, err }  if randData == uuid.Nil { \/\/ 6. unreachable code panic(\"should not happen ever, we lost faith in humanity\") }  res[i].Data = randData.String() }  \/\/ 7. result return res, nil }<\/code><\/pre>\n<h3>\u0417\u0430\u0432\u044f\u0437\u043a\u0430<\/h3>\n<p>\u0412 \u044d\u0442\u043e\u043c \u043c\u0435\u0442\u043e\u0434\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0432\u0438\u0434\u043e\u0432 \u043e\u0448\u0438\u0431\u043e\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0432 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0442\u0435\u0441\u0442. \u0412\u043e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u0430\u0436\u043d\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c?<\/p>\n<p>\u041e\u0448\u0438\u0431\u043a\u0438 \u0432\u0438\u0434\u0430 <strong>3<\/strong> \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b\u0430, \u0442.\u043a. \u0432\u0441\u0435 \u0440\u0430\u0432\u043d\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u0430\u043c\u0438 \u0432 \u0445\u043e\u0434\u0435 \u0442\u0435\u0441\u0442\u0430 \u0437\u0430 \u0441\u0447\u0435\u0442 \u043c\u043e\u043a\u043e\u0432. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u043f\u043e\u043b\u044c\u0437\u0430 \u043e\u0442 \u043d\u0438\u0445, \u0443\u0431\u0435\u0440\u0435\u0447\u044c \u0441\u0435\u0431\u044f \u043e\u0442 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u0433\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043e\u0442 \u0447\u0435\u0433\u043e \u043e\u0442\u044b\u0433\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u043d\u043e \u0438 \u0437\u0430\u0442\u0440\u0430\u0442\u043d\u043e \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438. <\/p>\n<p>\u0422\u043e\u0436\u0435 \u0438 \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0432\u0438\u0434\u0430 <strong>5<\/strong>, \u043e\u043d\u0438 \u043b\u0438\u0431\u043e \u0436\u0435 \u043d\u0435 \u043f\u043e\u0434 \u043d\u0430\u0448\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u043c, \u043b\u0438\u0431\u043e \u0436\u0435 \u0438\u043c\u0435\u044e\u0442 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0435 \u043a\u0435\u0439\u0441\u044b \u0434\u043b\u044f \u0432\u043e\u0437\u043d\u0438\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043e\u043a: \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0441\u043b\u0430\u0442\u044c \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u0447\u0442\u043e \u0432 \u0442\u0435\u043e\u0440\u0438\u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c\u043e \u0432 \u0442\u0435\u0441\u0442 \u043a\u0435\u0439\u0441\u0435, \u043e\u0434\u043d\u0430\u043a\u043e, \u043f\u043e\u0447\u0442\u0438 \u043d\u0435 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u044b \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438. <\/p>\n<p>\u041e\u0448\u0438\u0431\u043a\u0438 \u0432\u0438\u0434\u0430 <strong>6<\/strong> \u043f\u043e-\u0445\u043e\u0440\u043e\u0448\u0435\u043c\u0443 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c\u0441\u044f, \u043d\u043e \u043e\u043d\u0438 \u0447\u0430\u0441\u0442\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u0438 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a. \u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u043a\u0435\u0439\u0441 \u0437\u0430\u0447\u0430\u0441\u0442\u0443\u044e \u043d\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u043c.<\/p>\n<h2>\u0420\u0430\u0437\u0432\u0438\u0442\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f<\/h2>\n<p>\u0422\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u043a\u0435\u0439\u0441\u044b \u0432\u0438\u0434\u0430 <strong>1, 2, 4, 7<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0442\u0435\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">package service1  import ( \"context\" \"testing\"  \"github.com\/gojuno\/minimock\/v3\" \"github.com\/google\/uuid\" \"github.com\/stretchr\/testify\/require\" )  type mocksService struct { repo *RepositoryMock }  func newMocksService(t *testing.T) *mocksService { ctrl := minimock.NewController(t) return &amp;mocksService{ repo: NewRepositoryMock(ctrl), } }  func TestDoSmth(t *testing.T) { t.Parallel() ctx := context.TODO() require := require.New(t)  type in struct { ids []int } type out struct { res []Model err error }  tests := []struct { name   string in     in setup  func(*mocksService, *in) assert func(*in, *out) }{ { name:  \"check invalid params\", in:    in{ids: nil}, setup: func(service *mocksService, in *in) {}, assert: func(in *in, out *out) { require.NoError(out.err) require.Empty(out.res) }, }, { name:  \"check validation error\", in:    in{ids: []int{1, 2, 2}}, setup: func(service *mocksService, in *in) {}, assert: func(in *in, out *out) { require.ErrorIs(out.err, ErrValidation) require.Nil(out.res) }, }, { name: \"check logical error\", in:   in{ids: []int{1, 2, 3}}, setup: func(m *mocksService, in *in) { m.repo.GetSmthMock. Expect(ctx, in.ids). Return([]Model{{}, {}}, nil) }, assert: func(in *in, out *out) { require.ErrorIs(out.err, ErrLogic) require.Nil(out.res) }, }, { name: \"success\", in:   in{ids: []int{1, 2, 3}}, setup: func(m *mocksService, in *in) { m.repo.GetSmthMock. Expect(ctx, in.ids). Return([]Model{{ID: 1}, {ID: 2}, {ID: 3}}, nil) }, assert: func(in *in, out *out) { for i, r := range out.res { require.Equal(r.ID, in.ids[i]) _, err := uuid.Parse(r.Data) require.NoError(err) }  require.NoError(out.err) }, }, }  for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel()  m := newMocksService(t) s := NewService1(m.repo)  tt.setup(m, &amp;tt.in)  res, err := s.DoSmth(ctx, tt.in.ids) tt.assert(&amp;tt.in, &amp;out{ res: res, err: err, }) }) } } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u041a\u0443\u043b\u044c\u043c\u0438\u043d\u0430\u0446\u0438\u044f<\/h2>\n<details class=\"spoiler\">\n<summary>\u041d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c 21.1% \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"bash\">go test .\/... -coverprofile cover.out &amp;&amp; go tool cover -func cover.out<\/code><\/pre>\n<figure class=\"full-width\">\n<div><figcaption>cover report<\/figcaption><\/div>\n<\/figure>\n<\/div>\n<\/details>\n<p>\u041c\u0430\u043b\u043e\u0432\u0430\u0442\u043e \u043e\u0434\u043d\u0430\u043a\u043e, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0432 <abbr class=\"habraabbr\" title=\"go tool cover -html cover.out\" data-title=\"&lt;p>go tool cover -html cover.out&lt;\/p>&#187; data-abbr=&#187;html&#187;>html<\/abbr><\/p>\n<figure class=\"full-width\"><\/figure>\n<p><strong>82.4% <\/strong>\u043f\u043e\u043a\u0440\u044b\u0442\u043e, \u043d\u0430\u0441 \u044d\u0442\u043e \u0432\u043f\u043e\u043b\u043d\u0435 \u0443\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442, \u043d\u043e \u0432 \u0447\u0435\u043c \u0442\u043e\u0433\u0434\u0430 \u0437\u0430\u0433\u0432\u043e\u0437\u0434\u043a\u0430? \u0421\u043c\u043e\u0442\u0440\u0438\u043c \u0437\u0434\u0435\u0441\u044c \u0436\u0435 \u043d\u0430 \u0432\u0435\u0441\u044c coverage report<\/p>\n<figure class=\"\"><\/figure>\n<h2>\u0420\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>\u0412\u0438\u0434\u0438\u043c \u043c\u044b \u0442\u043e, \u0447\u0442\u043e \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u043b\u043e\u0441\u044c \u0442\u0430\u043c, \u0433\u0434\u0435 \u043d\u0430\u043c \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u043e. \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<figure class=\"\"><\/figure>\n<blockquote>\n<p>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u043f\u043e\u043b\u043d\u0435 \u0437\u0430\u043a\u043e\u043d\u043e\u043c\u0435\u0440\u0435\u043d, \u0443\u0442\u0438\u043b\u0438\u0442\u0430 <em>cover<\/em> \u043f\u043e \u0434\u0435\u0444\u043e\u043b\u0442\u0443 \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 \u0434\u043b\u044f <strong>\u0432\u0441\u0435\u0445<\/strong> <abbr class=\"habraabbr\" title=\"\u0415\u0441\u043b\u0438 \u043e\u043d\u0438 \u043d\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u043d\u0430 . \u0438\u043b\u0438 _ https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns\" data-title=\"&lt;p>erns&#187; data-title=&#187;&lt;p>\u0415\u0441\u043b\u0438 \u043e\u043d\u0438 \u043d\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u043d\u0430 . \u0438\u043b\u0438 _&amp;nbsp;&lt;\/p>&lt;p>&lt;a href=&quot;https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns&quot;>https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns&lt;\/a>&lt;\/p>&lt;p>&lt;\/p>&#187; data-abbr=&#187;\u0433\u043e\u0448\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432&#187;>\u0433\u043e\u0448\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u043e\u0432*<\/abbr> \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438, \u0435\u0441\u043b\u0438 \u0432 <abbr class=\"habraabbr\" title=\"\u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u043d\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f testdata https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns\" data-title=\"&lt;p> data-title=&#187;&lt;p>\u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u043d\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f testdata&lt;br>&lt;a href=&quot;https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns&quot;>https:\/\/pkg.go.dev\/cmd\/go#hdr-Package_lists_and_patterns&lt;\/a>&lt;\/p>&lt;p>&lt;\/p>&#187; data-abbr=&#187;\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438&#187;>\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438*<\/abbr> \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f <strong>\u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d<\/strong> \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b<\/p>\n<\/blockquote>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430, \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u043c\u043e\u043a \u0444\u0430\u0439\u043b\u043e\u0432 \u0432 \u0440\u0430\u0441\u0447\u0435\u0442\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f, \u0442.\u043a. \u043e\u043d\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438 \u043c\u043e\u0433\u0443\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c 1000+ \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u0434\u0430. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0443\u0442\u0438\u043b\u0438\u0442\u0430 \u0432 \u044d\u0442\u043e\u043c \u043f\u043b\u0430\u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u0436\u0435\u043c \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u0430\u043a\u043e\u0439 \u0445\u0438\u0442\u0440\u043e\u0441\u0442\u044c\u044e<\/p>\n<pre><code class=\"bash\">go test .\/... -coverprofile cover.out.tmp &amp;&amp; cat cover.out.tmp | grep -v \"_mock.go\" > cover.out &amp;&amp; rm cover.out.tmp &amp;&amp; go tool cover -func cover.out<\/code><\/pre>\n<ol>\n<li>\n<p>\u0417\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u043f\u043e\u0440\u0442 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u0432 tmp \u0444\u0430\u0439\u043b<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u0432 \u043d\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b, \u043a\u0440\u043e\u043c\u0435 \u0441\u0442\u0440\u043e\u043a, \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u044e\u0449\u0438\u0445\u0441\u044f \u043d\u0430 \u0442\u043e\u0442 \u043f\u0430\u0442\u0442\u0435\u0440\u043d, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043c\u043e\u043a \u0444\u0430\u0439\u043b\u043e\u0432. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e <code>_mock.go<\/code><\/p>\n<\/li>\n<li>\n<p>\u0423\u0434\u0430\u043b\u044f\u0435\u043c tmp \u0444\u0430\u0439\u043b<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c cover<\/p>\n<\/li>\n<\/ol>\n<p>\u0418 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0435 <strong>65.2%:<\/strong><\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0447\u0442\u043e \u0432 \u043e\u0442\u0434\u0435\u043b\u0435 \u043c\u044b \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043b\u0438\u0441\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0438\u0437\u043d\u0435\u0441 \u043b\u043e\u0433\u0438\u043a\u0443, \u0442\u043e \u0431\u0438\u0448\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0430 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 <abbr class=\"habraabbr\" title=\"data access layer\" data-title=\"&lt;p>data access layer&lt;\/p>&#187; data-abbr=&#187;dal&#187;>dal<\/abbr> \u0438 <abbr class=\"habraabbr\" title=\"application layer \u0438\u043b\u0438 \u0436\u0435 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0445\u044d\u043d\u0434\u043b\u0435\u0440\u043e\u0432(\u0440\u0443\u0447\u0435\u043a)\" data-title=\"&lt;p>(\u0440\u0443\u0447\u0435\u043a)&#187; data-title=&#187;&lt;p>application layer \u0438\u043b\u0438 \u0436\u0435 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0445\u044d\u043d\u0434\u043b\u0435\u0440\u043e\u0432(\u0440\u0443\u0447\u0435\u043a)&lt;\/p>&#187; data-abbr=&#187;app&#187;>app<\/abbr> \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c <abbr class=\"habraabbr\" title=\"end-to-end \u0442\u0435\u0441\u0442\u044b \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u043b\u0438 \u0436\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439\" data-title=\"&lt;p>\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439&#187; data-title=&lt;p>end-to-end \u0442\u0435\u0441\u0442\u044b \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u043b\u0438 \u0436\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439&lt;\/p>&#187; data-abbr=&#187;e2e&#187;>e2e<\/abbr> \u0442\u0435\u0441\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0432 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0438. \u041d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0442\u0443 \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0441\u043c\u044b\u0441\u043b\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043c\u0435\u0442\u043e\u0434\u044b \u043c\u043e\u0434\u0435\u043b\u0435\u043a. \u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043b\u0435\u0433\u043a\u0430 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u0435\u043a\u043e\u043c\u043f\u043e\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430<\/p>\n<figure class=\"\"><\/figure>\n<p>\u041e\u0442\u0434\u0435\u043b\u0438\u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438, \u043c\u044b \u043d\u0435 \u0431\u0435\u0440\u0435\u043c \u0432 \u0440\u0430\u0441\u0447\u0435\u0442 \u043d\u0435\u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c <strong>83.3% <\/strong>\u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u0430<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u044b \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043b\u0438 coverage <strong>\u0441 20 \u0434\u043e >80%<\/strong> \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u043d\u0438 \u0435\u0434\u0438\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u0447\u043a\u0438 \u043a\u043e\u0434\u0430. <\/p>\n<h2>\u0418\u0441\u043f\u044b\u0442\u0430\u043d\u0438\u0435<\/h2>\n<p>\u041d\u043e \u0432\u0441\u0435 \u043b\u0438 \u0443 \u043d\u0430\u0441 \u0445\u043e\u0440\u043e\u0448\u043e \u0442\u0435\u043f\u0435\u0440\u044c? \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f \u0435\u0449\u0435 \u0440\u0430\u0437\u043e\u043a<\/p>\n<figure class=\"\"><\/figure>\n<p>\u0410 \u0443 \u043d\u0430\u0441 \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0435\u0441\u0442\u044c \u0435\u0449\u0435 \u0438 \u0432\u0442\u043e\u0440\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u0431\u0435\u0437 \u0435\u0434\u0438\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u043b\u0441\u044f \u0432 \u0440\u0435\u043f\u043e\u0440\u0442\u0435, \u0442.\u043a. \u0432 \u043d\u0435\u043c \u043d\u0435\u0442 \u0442\u0435\u0441\u0442\u043e\u0432. \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0447\u0442\u043e-\u0442\u043e \u0441 \u044d\u0442\u0438\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c: \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u043f\u043e\u0434 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0435\u0439 <code>bll<\/code>  \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u044d\u0442\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0432 \u0440\u0435\u043f\u043e\u0440\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0444\u043b\u0430\u0433 <a href=\"https:\/\/pkg.go.dev\/cmd\/go#hdr-Testing_flags\" rel=\"noopener noreferrer nofollow\">-coverpkg<\/a><\/p>\n<pre><code class=\"bash\">go test .\/... -coverpkg='.\/internal\/service\/...\/bll\/...' -coverprofile cover.out.tmp  &amp;&amp; cat cover.out.tmp | grep -v \"_mock.go\" > cover.out &amp;&amp; rm cover.out.tmp &amp;&amp; go tool cover -func cover.out<\/code><\/pre>\n<p>\u0421\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u0436\u0435 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u0443 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0432\u0438\u0434\u044f \u043d\u0435 \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u043b\u043e\u0433\u0438\u043a\u0438<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0414\u043e\u043f\u0438\u0441\u0430\u0432 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u044e\u0449\u0438\u0435 \u0442\u0435\u0441\u0442\u044b, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c <strong>84.2%<\/strong><\/p>\n<h2>\u0420\u0435\u0442\u0440\u043e\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u0430<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u043d\u0430\u0448\u0435\u043c\u0443 \u043f\u0435\u0440\u0432\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u0443, \u0442\u0443\u0442 \u0435\u0449\u0435 \u043e\u0441\u0442\u0430\u043b\u0438\u0441\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u043e\u043d\u043a\u043e\u0441\u0442\u0438<\/p>\n<details class=\"spoiler\">\n<summary>\u043f\u0435\u0440\u0432\u044b\u0439 \u0442\u0435\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e \u043f\u043e\u0432\u043e\u0434\u0443\u00a0\u043a\u0435\u0439\u0441\u0430 <strong>6<\/strong>. \u041f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043c\u044b \u043d\u0435\u00a0\u043c\u043e\u0436\u0435\u043c, \u0438 \u0441\u0442\u0440\u0430\u043d\u043d\u043e, \u0447\u0442\u043e\u00a0\u0442\u0430\u043a\u043e\u0439 \u0431\u043b\u043e\u043a \u0432\u043e\u043e\u0431\u0449\u0435 \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432\u00a0\u0440\u0435\u043f\u043e\u0440\u0442. \u0423\u043c\u0435\u0441\u0442\u0435\u043d\u00a0\u0431\u044b\u043b\u00a0\u0431\u044b \u0432\u043e\u043f\u0440\u043e\u0441: \u0430\u00a0\u0435\u0441\u0442\u044c\u00a0\u043b\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u043f\u043e\u043c\u0435\u0442\u0438\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043a\u0443\u0441\u043a\u0438 \u043a\u043e\u0434\u0430 \u043a\u0430\u043a\u00a0\u043d\u0435\u0434\u043e\u0441\u044f\u0433\u0430\u0435\u043c\u044b\u0435 \u0434\u043b\u044f\u00a0\u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f, \u0447\u0442\u043e\u0431\u044b\u00a0\u043b\u0438\u0448\u043d\u0438\u0439 \u0440\u0430\u0437 \u043d\u0435\u00a0\u043f\u043e\u0440\u0442\u0438\u0442\u044c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443, \u0438\u043b\u0438 \u0436\u0435 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u043f\u043e \u0442\u0438\u043f\u0443 \u043f\u0430\u043d\u0438\u043a \u043f\u0440\u0438 \u043f\u043e\u0434\u0441\u0447\u0435\u0442\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u044f?<\/p>\n<p>\u042d\u0442\u043e\u0442 \u0432\u043e\u043f\u0440\u043e\u0441 \u043e\u0431\u0441\u0443\u0436\u0434\u0430\u043b\u0441\u044f \u043d\u0435\u00a0\u0440\u0430\u0437 \u043d\u0430\u00a0\u0444\u043e\u0440\u0443\u043c\u0430\u0445 \u0438 issue: <a href=\"https:\/\/github.com\/golang\/go\/issues\/53271\" rel=\"noopener noreferrer nofollow\">1<\/a>, <a href=\"https:\/\/github.com\/golang\/go\/issues\/31280\" rel=\"noopener noreferrer nofollow\">2<\/a>, \u043d\u043e\u00a0\u0435\u0441\u043b\u0438 \u0432\u043a\u0440\u0430\u0442\u0446\u0435, \u0442\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430\u00a0\u0432\u0441\u0435 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0442\u00a0\u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438 \u043c\u043e\u0436\u043d\u043e \u0441\u0432\u0435\u0441\u0442\u0438 \u043a\u00a0<a href=\"https:\/\/github.com\/golang\/go\/issues\/34639#issuecomment-537327615\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u043c\u0443<\/a> \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0443 \u0420\u043e\u0431\u0430 \u041f\u0430\u0439\u043a\u0430 \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 issue. \u0425\u043e\u0442\u044f, \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u043f\u0430\u0440\u0443 \u043d\u0435\u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 <a href=\"https:\/\/github.com\/dave\/courtney\" rel=\"noopener noreferrer nofollow\">\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0435\u0448\u0430\u044e\u0442 \u044d\u0442\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.<\/p>\n<h2>\u0424\u0438\u043d\u0430\u043b<\/h2>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u043e\u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u043d\u0435\u0434\u043e\u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043f\u0440\u0438 \u043f\u043e\u043a\u0440\u044b\u0442\u0438\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0442\u0435\u0441\u0442\u0430\u043c\u0438, \u043b\u0443\u0447\u0448\u0438\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438 \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0435 \u0434\u0438\u0441\u043a\u0443\u0441\u0441\u0438\u0438 \u043a\u043e\u043c\u044c\u044e\u043d\u0438\u0442\u0438.<\/p>\n<p>\u0421\u043e\u0446 \u0441\u0435\u0442\u0438:<\/p>\n<p><a href=\"https:\/\/www.linkedin.com\/in\/dishanov\/\" rel=\"noopener noreferrer nofollow\">https:\/\/www.linkedin.com\/in\/dishanov\/<\/a><\/p>\n<h2>\u042d\u043f\u0438\u043b\u043e\u0433<\/h2>\n<details class=\"spoiler\">\n<summary>\u0414\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a -coverpkg<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0415\u0449\u0435 \u043e\u0434\u043d\u0430 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0444\u043b\u0430\u0433\u0430 <code>coverpkg<\/code>, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f <strong>A<\/strong> \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0444\u043b\u0430\u0433\u0430, \u0442\u043e \u0432\u0441\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u043d\u0435\u0439 \u043f\u0440\u0438 \u0438\u0445 \u0432\u044b\u0437\u043e\u0432\u0435 \u0441 \u0442\u0435\u0441\u0442\u0430 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <strong>B<\/strong> \u0431\u0443\u0434\u0443\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043a\u0440\u044b\u0442\u044b\u043c\u0438 \u0442\u0435\u0441\u0442\u0430\u043c\u0438 \u0432 \u0440\u0435\u043f\u043e\u0440\u0442\u0435, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043a \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044e \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u0442\u043e\u0447\u0435\u0447\u043d\u044b\u0435 \u0442\u0435\u0441\u0442\u044b. <\/p>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0442\u0435\u0441\u0442\u0435 \u043c\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>HasDuplicates<\/code> \u0438\u0437 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 <code>helper<\/code>, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 <u>\u043d\u0435\u0442<\/u> \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<details class=\"spoiler\">\n<summary>HasDuplicates<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func HasDuplicates(slice []int) bool { set := make(map[int]bool) for _, v := range slice { if set[v] { return true } set[v] = true } return false }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0432\u043a\u043b\u044e\u0447\u0430\u044f <code>internal\/helper<\/code> \u0432 coverpkg<\/p>\n<pre><code class=\"bash\">go test .\/... -coverpkg='.\/internal\/helper\/...','.\/internal\/service\/...\/bll\/...' -coverprofile cover.out.tmp  &amp;&amp; cat cover.out.tmp | grep -v \"_mock.go\" > cover.out &amp;&amp; rm cover.out.tmp &amp;&amp; go tool cover -html cover.out <\/code><\/pre>\n<p>\u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c<\/p>\n<figure class=\"\"><\/figure>\n<p>\u0418\u043d\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0431\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c, \u0430 \u0437\u0430\u0447\u0430\u0441\u0442\u0443\u044e \u043c\u043e\u0436\u0435\u0442 \u0432\u0440\u0435\u0434\u0438\u0442\u044c \u043a\u0430\u043a \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0432\u0435\u0434\u044c \u043c\u044b \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u043d\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u044d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0432 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f\u0445.<\/p>\n<p>*\u0415\u0441\u0442\u044c \u043e\u0434\u0438\u043d \u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442. \u0411\u0430\u0433 \u044d\u0442\u043e \u0438\u043b\u0438 \u0444\u0438\u0447\u0430, \u043d\u043e \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e <code>'.\/internal\/helper\/...'<\/code>\u0432<code>coverpkg<\/code>, \u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0442\u043e\u0436\u0435 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043a\u0440\u044b\u0442\u043e\u0439 \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u043f\u043e \u0444\u0430\u043a\u0442\u0443 \u0435\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0432 \u0442\u0435\u0441\u0442\u0435 <code>DoSmth<\/code> \u0438 \u0432  \u0441\u043a\u0440\u0438\u043f\u0442\u0435 \u043c\u044b \u0432\u0441\u0435 \u0435\u0449\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435: <code>go test .\/...<\/code><\/p>\n<p>\u0418\u043d\u043e\u0439 \u0440\u0430\u0437 \u044f \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u043b \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0443 (\u043a\u0440\u0430\u0439\u043d\u0435 \u043d\u0435 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0435\u0435), \u043a\u043e\u0433\u0434\u0430 \u0432\u0441\u0435 \u0442\u0435\u0441\u0442\u044b \u043f\u0438\u0441\u0430\u043b\u0438 \u0432 \u0441\u043b\u043e\u0435 \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 (app), \u0430 \u043d\u0435 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u043e\u043c \u0441\u043b\u043e\u0435. \u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043c\u044b \u043f\u043e \u0444\u0430\u043a\u0442\u0443 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0431\u0438\u0437\u043d\u0435\u0441 \u043b\u043e\u0433\u0438\u043a\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043d\u043e \u0435\u0441\u043b\u0438 <strong>\u043d\u0435<\/strong> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>coverpkg<\/code>, \u0442\u043e<\/p>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-346639","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/346639","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=346639"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/346639\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=346639"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=346639"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=346639"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}