Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wataru / boru 課題2 #11

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions kadai2/wataboru/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
.DS_Store
110 changes: 110 additions & 0 deletions kadai2/wataboru/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# 課題 2

## 【TRY】io.Readerとio.Writer

### io.Readerとio.Writerについて調べてみよう

- 標準パッケージでどのように使われているか
- io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる

> - 標準パッケージでどのように使われているか

- 実装している標準パッケージ
- bufio
- bytes
- zip
- strings

- どの様に使われているか
- strings

```
func (w *appendSliceWriter) Write(p []byte) (int, error) {
*w = append(*w, p...)
return len(p), nil
}
```

- bufio

```
func (b *Writer) Write(p []byte) (nn int, err error) {
for len(p) > b.Available() && b.err == nil {
var n int
if b.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, b.err = b.wr.Write(p)
} else {
n = copy(b.buf[b.n:], p)
b.n += n
b.Flush()
}
nn += n
p = p[n:]
}
if b.err != nil {
return nn, b.err
}
n := copy(b.buf[b.n:], p)
b.n += n
nn += n
return nn, nil
}
```

- zip

```
func (w *fileWriter) Write(p []byte) (int, error) {
if w.closed {
return 0, errors.New("zip: write to closed file")
}
w.crc32.Write(p)
return w.rawCount.Write(p)
}
```

> - io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる

1. 各パッケージで扱っている書き込み先は全て違うが、同じインターフェースにて抽象化されていること中の実装は隠蔽され、利用者としてはそれぞれ全て等しく `Write` や `Read` で利用することができる。
2. 上記の通り隠蔽されることで、書き込み先や読み込み先という仕様が変更になったとしても、利用するパッケージを変更するだけで実現が可能になる。(変更容易性が上がる)

## 【TRY】テストを書いてみよう

### 1回目の課題のテストを作ってみて下さい
- テストのしやすさを考えてリファクタリングしてみる
- テストのカバレッジを取ってみる
- テーブル駆動テストを行う
- テストヘルパーを作ってみる

## 動作手順

```
$ go test ./imageconverter
$ go test ./commands/imageconverter
```

## カバレッジ

- imageconverter
`./imageconverter_coveage.html`

- main
`./imageconverter_coveage.html`

## 感想等

### io.Readerとio.Writerについて調べてみよう

- Interfaceを利用した

### 【TRY】テストを書いてみよう

- 画像のエンコード処理を、ストラテジーパターンを利用してリファクタリングしました。
- 可能な限り、テスト対象の関数内で利用しているパッケージをMockして、依存を減らす様にしました。(Mockのパッケージを使わないで実現するのに相当苦労しました。)
- 標準パッケージや自身の実装した関数のmockにパッケージ変数とinitを利用していますが、これで正しいか悩んでいます。
- os.FileInfoはInterfaceのため、test側でモックを実装しています。が、不要なメソッドも実装する必要があり苦労しました。埋め込みを用いた上で該当するメソッドだけ修正することは可能なのでしょうか。
- mainのテストにて、flag.Parse() をinitの中で実行していたため、testでflag引数を渡すことができず苦戦しました。こちら、run()側にflag.Parse() を渡すのか、 testing.Init() を呼び出すか、どちらが良いか悩んでいます。あまりテストコードがメインの実装に漏れるのが嫌なので、他の方法があれば知りたいです。
- テスタブルなコードを目指すあまり、実装として読みにくくなっていないか気になっています。

65 changes: 65 additions & 0 deletions kadai2/wataboru/commands/imageconverter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"flag"
"fmt"
"os"
"testing"

"github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter"
)

const (
// ExitCodeSuccess is the exit code on success
ExitCodeSuccess = 0
// ExitCodeError is the exit code when failed
ExitCodeError = 1
// ExitCodeError is the exit code when failed
ExitCodeInvalidDirectoryError = 2
)

var (
args imageconverter.Args
osStat func(name string) (os.FileInfo, error)
osIsNotExist func(err error) bool
imgconv func(args imageconverter.Args) error
)

func init() {
testing.Init()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mainのテストにて、flag.Parse() をinitの中で実行していたため、testでflag引数を渡すことができず苦戦しました。こちら、run()側にflag.Parse() を渡すのか、 testing.Init() を呼び出すか、どちらが良いか悩んでいます。あまりテストコードが実装に漏れるのが嫌なのですが。。。他の方法があれば知りたいです。

flag.StringVar(&args.Directory, "dir", "", "Input target Directory.\n ex) `--dir=./convert_image`")
flag.StringVar(&args.Directory, "d", "", "Input target Directory. (short)")
flag.StringVar(&args.BeforeExtension, "before", "jpg", "Input extension before conversion.\n ex) `--before=png`")
flag.StringVar(&args.BeforeExtension, "b", "jpg", "Input extension before conversion. (short)")
flag.StringVar(&args.AfterExtension, "after", "png", "Input extension after conversion.\n ex) `--after=jpg`")
flag.StringVar(&args.AfterExtension, "a", "png", "Input extension after conversion. (short)")
flag.Parse()

osStat = os.Stat
osIsNotExist = os.IsNotExist
imgconv = imageconverter.Convert

}

func run() int {
if args.Directory == "" {
fmt.Fprintln(os.Stderr, "Input target Directory.\n ex) `--dir=./convert_image`")
return ExitCodeInvalidDirectoryError
}

if _, err := osStat(args.Directory); osIsNotExist(err) {
fmt.Fprintln(os.Stderr, "Target directory is not found.")
return ExitCodeInvalidDirectoryError
}

if err := imgconv(args); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
return ExitCodeError
}

return ExitCodeSuccess
}

func main() {
os.Exit(run())
}
138 changes: 138 additions & 0 deletions kadai2/wataboru/commands/imageconverter/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package main

import (
"flag"
"fmt"
"os"
"testing"

"github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter"
)

type mockFns struct {
osStat func(name string) (os.FileInfo, error)
osIsNotExist func(err error) bool
imgconv func(args imageconverter.Args) error
}

func Test_run(t *testing.T) {
tests := []struct {
name string
args imageconverter.Args
mocks mockFns
want int
}{
{
name: "Normal",
args: imageconverter.Args{
Directory: "./hoge",
BeforeExtension: "jpg",
AfterExtension: "png",
},
mocks: mockFns{
osStat: func(name string) (os.FileInfo, error) {
return nil, nil
},
osIsNotExist: func(err error) bool {
return false
},
imgconv: func(args imageconverter.Args) error {
return nil
},
},
want: ExitCodeSuccess,
},
{
name: "ErrorBecauseWithoutArgsDirectory",
args: imageconverter.Args{
Directory: "",
BeforeExtension: "jpg",
AfterExtension: "png",
},
mocks: mockFns{
osStat: func(name string) (os.FileInfo, error) {
return nil, nil
},
osIsNotExist: func(err error) bool {
return false
},
imgconv: func(args imageconverter.Args) error {
return nil
},
},
want: ExitCodeInvalidDirectoryError,
},
{
name: "ErrorBecauseOsIsNotExitReturnError",
args: imageconverter.Args{
Directory: "./hoge",
BeforeExtension: "jpg",
AfterExtension: "png",
},
mocks: mockFns{
osStat: func(name string) (os.FileInfo, error) {
return nil, nil
},
osIsNotExist: func(err error) bool {
return true
},
imgconv: func(args imageconverter.Args) error {
return nil
},
},
want: ExitCodeInvalidDirectoryError,
},
{
name: "ErrorBecauseImgconvReturnError",
args: imageconverter.Args{
Directory: "./hoge",
BeforeExtension: "jpg",
AfterExtension: "png",
},
mocks: mockFns{
osStat: func(name string) (os.FileInfo, error) {
return nil, nil
},
osIsNotExist: func(err error) bool {
return false
},
imgconv: func(args imageconverter.Args) error {
return fmt.Errorf("error")
},
},
want: ExitCodeError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
flagsSet(t, tt.args)
MockSet(t, tt.mocks)
if got := run(); got != tt.want {
t.Errorf("run() = %v, want %v", got, tt.want)
}
})
}
}

func flagsSet(t *testing.T, args imageconverter.Args) {
t.Helper()
if err := flag.CommandLine.Set("dir", args.Directory); err != nil {
t.Errorf("error occurred in flagSet helper: %v", err)

}
if err := flag.CommandLine.Set("before", args.BeforeExtension); err != nil {
t.Errorf("error occurred in flagSet helper: %v", err)

}
if err := flag.CommandLine.Set("after", args.AfterExtension); err != nil {
t.Errorf("error occurred in flagSet helper: %v", err)

}
}

func MockSet(t *testing.T, m mockFns) {
t.Helper()
osStat = m.osStat
osIsNotExist = m.osIsNotExist
imgconv = m.imgconv
}
40 changes: 40 additions & 0 deletions kadai2/wataboru/cover.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
mode: set
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:29.13,33.46 4 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:36.2,37.44 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:40.2,41.33 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:33.46,35.3 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:37.44,39.3 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:57.65,59.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:63.64,65.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:69.64,71.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:75.64,77.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:81.65,83.2 1 0
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:85.52,86.45 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:87.23,88.28 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:89.14,90.27 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:91.14,92.27 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:93.14,94.27 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:95.23,96.28 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:97.10,98.106 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:102.44,104.16 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:107.2,110.16 3 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:114.2,114.15 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:120.2,121.16 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:125.2,126.16 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:129.2,129.32 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:104.16,106.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:110.16,112.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:114.15,115.17 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:115.17,117.4 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:121.16,123.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:126.16,128.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:132.78,133.16 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:137.2,137.18 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:141.2,142.37 2 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:145.2,145.72 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:133.16,135.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:137.18,139.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:142.37,144.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:151.31,152.92 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:152.92,154.3 1 1
github.com/gopherdojo/dojo8/kadai1/wataboru/imageconverter/ImageConverter.go:157.48,159.2 1 1
8 changes: 8 additions & 0 deletions kadai2/wataboru/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/gopherdojo/dojo8/kadai1/wataboru

go 1.14

require (
golang.org/x/image v0.0.0-20200618115811-c13761719519
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed // indirect
)
30 changes: 30 additions & 0 deletions kadai2/wataboru/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
github.com/yuin/goldmark v1.1.32 h1:5tjfNdR2ki3yYQ842+eX2sQHeiwpKJ0RnHO4IYOc4V8=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/image v0.0.0-20200618115811-c13761719519 h1:1e2ufUJNM3lCHEY5jIgac/7UTjd6cgJNdatjPdFWf34=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed h1:+qzWo37K31KxduIYaBeMqJ8MUOyTayOQKpH9aDPLMSY=
golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Loading