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 3 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
107 changes: 107 additions & 0 deletions kadai2/wataboru/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# 課題 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のパッケージを使わないで実現するのに相当苦労しました。)
- 画像のエンコードを、ストラテジーパターンを利用してリファクタリングしました。
- テスタブルなコードを目指すあまり、実装として読みにくくなっていないか気になっています。

69 changes: 69 additions & 0 deletions kadai2/wataboru/commands/imageconverter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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 = func(name string) (os.FileInfo, error) {
return os.Stat(name)
}
osIsNotExist = func(err error) bool {
return os.IsNotExist(err)
}
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
)
Loading