如何模拟调用 io.Copy 函数。

huangapple go评论81阅读模式
英文:

How to mock a function calling io.Copy

问题

尝试模拟以下函数。它基本上从S3中获取一个对象(io.ReadCloser),并将其写入之前使用os.Open()打开的文件(io.WriteCloser)。

package main

import (
	"io";

	log "github.com/sirupsen/logrus"
)

func writeFile(destination io.WriteCloser, source io.ReadCloser) error {
	defer destination.Close()
	defer source.Close()
	_, err := io.Copy(destination, source)
	if err != nil {
		log.WithFields(log.Fields{"desc": "无法将内容从S3复制到blahblah"}).Error(err)
		return err
	}
	return nil
}

我认为我离成功很近,但目前我的测试卡住了,既没有出错也没有成功...我还意识到我可以将os.Stdout传递给目标,但仍然遇到了同样的问题。在io.Copy中发生了一些问题。我想象中这是因为我试图将空数据复制到空位置?

package main

import (
	"errors";
	"io";
	"reflect";
	"testing";
)

type mockReadCloser struct {}

func (m mockReadCloser) Read(p []byte) (int, error) { return 0, nil }
func (m mockReadCloser) Close() error               { return nil }

type mockWriteCloser struct{}

func (m mockWriteCloser) Close() error                      { return nil }
func (m mockWriteCloser) Write(b []byte) (n int, err error) { return 0, nil }

func Test_writeFile(t *testing.T) {
	type args struct {
		destination io.WriteCloser
		source      io.ReadCloser
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "",
			args: args{
				destination: &mockWriteCloser{},
				source:      &mockReadCloser{},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := writeFile(tt.args.destination, tt.args.source); (err != nil) != tt.wantErr {
				t.Errorf("writeFile() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}
英文:

Trying to mock the following function. It basically takes an object from S3 (io.ReadCloser) and writes it to a file that execution os.Open()d earlier (io.WriteCloser).

package main

import (
	"io"

	log "github.com/sirupsen/logrus"
)

func writeFile(destination io.WriteCloser, source io.ReadCloser) error {
	defer destination.Close()
	defer source.Close()
	_, err := io.Copy(destination, source)
	if err != nil {
		log.WithFields(log.Fields{"desc": "unable to copy contents from s3 to blahblah"}).Error(err)
		return err
	}
	return nil
}

I think I'm pretty close, but at the moment my test hangs and never errors / succeeds... I also realized that I can pass os.Stdout to my destination, but still ran into the same issue. Something is going on within io.Copy. I imagine it's because I'm trying to copy empty data to nothing?

package main

import (
	"errors"
	"io"
	"reflect"
	"testing"
)

type mockReadCloser struct {}

func (m mockReadCloser) Read(p []byte) (int, error) { return 0, nil }
func (m mockReadCloser) Close() error               { return nil }

type mockWriteCloser struct{}

func (m mockWriteCloser) Close() error                      { return nil }
func (m mockWriteCloser) Write(b []byte) (n int, err error) { return 0, nil }

func Test_writeFile(t *testing.T) {
	type args struct {
		destination io.WriteCloser
		source      io.ReadCloser
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "",
			args: args{
				destination: &mockWriteCloser{},
				source:      &mockReadCloser{},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := writeFile(tt.args.destination, tt.args.source); (err != nil) != tt.wantErr {
				t.Errorf("writeFile() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

答案1

得分: 2

这是因为io.Copy在遇到EOF或错误之前不会返回。

Copy函数从源(src)复制数据到目标(dst),直到在源上达到EOF或发生错误为止。它返回复制的字节数以及在复制过程中遇到的第一个错误(如果有的话)。

成功的Copy函数返回err == nil,而不是err == EOF。因为Copy函数被定义为从源读取数据直到EOF,它不会将Read返回的EOF视为需要报告的错误。

所以,如果你在mockReadCloser.Read中返回EOF,它就不会再阻塞了。

func (m mockReadCloser) Read(p []byte) (int, error) { return 0, io.EOF }

这是因为Read函数会重复调用,直到没有更多可读取的内容(EOF)。

for {
    nr, er := src.Read(buf)
    ...
}
英文:

It's because io.Copy doesn't return until EOF or error.

> Copy copies from src to dst until either EOF is reached on src or an error occurs. It returns the number of bytes copied and the first error encountered while copying, if any.
>
> A successful Copy returns err == nil, not err == EOF. Because Copy is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported.

So, if you return EOF from your mockReadCloser.Read, it shouldn't hang any more.

func (m mockReadCloser) Read(p []byte) (int, error) { return 0, io.EOF }

This is because Read is called repeatedly until there is nothing more to read (EOF).

for {
    nr, er := src.Read(buf)
    ...
}

huangapple
  • 本文由 发表于 2022年6月18日 20:57:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/72669429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定