简体   繁体   English

如何模拟 amqp.Dial 等库中的函数

[英]How to mock functions from libraries like amqp.Dial

I'm working on a small AMQP consumer and i want to test my consumer code, but im struggleing to mock the amqp.Dial .我正在研究一个小型 AMQP 消费者,我想测试我的消费者代码,但我很难模拟amqp.Dial I have added some interface so i can mock Connection and Channel and added a property so i can control the dial-function:我添加了一些接口以便我可以模拟ConnectionChannel并添加一个属性以便我可以控制拨号功能:

//consumer.go
type AmqpChannel interface {
    ExchangeDeclare(name, kind string, durable, autoDelete, internal, noWait bool, args amqp.Table) error
    QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args amqp.Table) (amqp.Queue, error)
    QueueBind(name, key, exchange string, noWait bool, args amqp.Table) error
    Consume(queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args amqp.Table) (<-chan amqp.Delivery, error)
    Publish(exchange, key string, mandatory, immediate bool, msg amqp.Publishing) error
}

type AmqpConnection interface {
    Channel() (AmqpChannel, error)
    Close() error
}

type AmqpDial func(url string) (AmqpConnection, error)

type MyConsumer struct {
    HostDsn    string
    channel    AmqpChannel
    queue      amqp.Queue
    connection AmqpConnection
    DialFunc   AmqpDial
}

func (c *MyConsumer) Connect() error {
    var err error
    c.connection, err = c.DialFunc(c.HostDsn)
...

This seems to be close to what i want to achive, i can specify my test like this:这似乎接近我想要达到的目标,我可以这样指定我的测试:

func TestConsumer(t *testing.T) {
    mockCtrl := gomock.NewController(t)
    defer mockCtrl.Finish()

    var myConsumer = consumer.MyConsumer{
        HostDsn: "test",
        DialFunc: func(url string) (consumer.AmqpConnection, error) {
            return mocks.NewMockAmqpConnection(mockCtrl), nil
        },
    }
    _ = myConsumer.Connect()
}

But i cannot pass the original amqp.Dial as dial-func on the main routine:但我不能通过原始amqp.Dial作为拨号功能在主程序上:

myConsumer := consumer.MyConsumer{
        HostDsn: fmt.Sprintf(
            "amqp://%s:%s@rabbitmq:5672/?heartbeat=5s",
            os.Getenv("RABBITMQ_USER"),
            url.QueryEscape(os.Getenv("RABBITMQ_PASSWORD")),
        ),
        DialFunc: amqp.Dial,
    }

gives

./main.go:28:9: cannot use amqp.Dial (type func(string) (*amqp.Connection, error)) as type consumer.AmqpDial in field value

I hoped/thought that, as amqp.Connection fulfills the AmqpConnection interface, this would work :/ What is the proper way of mocking methods like amqp.Dial ?我希望/认为,由于amqp.Connection实现了AmqpConnection接口,这将起作用:/ amqp.Dialamqp.Dial这样的方法的正确方法是amqp.Dial

PS: im aware of https://github.com/NeowayLabs/wabbit but i would prefer to solve this on a lower level :) PS:我知道https://github.com/NeowayLabs/wabbit但我更愿意在较低级别解决这个问题:)

Update: @mkopriva suggested to use another wrapper method, so i tried:更新:@mkopriva 建议使用另一种包装方法,所以我尝试:

func getDialer(url string) (consumer.AmqpConnection, error) {
    var connection, err = amqp.Dial(url)
    return connection, err
}
myConsumer := consumer.myConsumer{
        HostDsn: fmt.Sprintf(
            "amqp://%s:%s@rabbitmq:5672/?heartbeat=5s",
            os.Getenv("RABBITMQ_USER"),
            url.QueryEscape(os.Getenv("RABBITMQ_PASSWORD")),
        ),
        DialFunc: getDialer,
    }

But this results in:但这导致:

cannot use connection (type *amqp.Connection) as type consumer.AmqpConnection in return argument:
    *amqp.Connection does not implement consumer.AmqpConnection (wrong type for Channel method)
        have Channel() (*amqp.Channel, error)
        want Channel() (consumer.AmqpChannel, error)
make: *** [Makefile:4: build-consumer] Error 2

Given the following types:鉴于以下类型:

type AmqpChannel interface {
    ExchangeDeclare(name, kind string, durable, autoDelete, internal, noWait bool, args amqp.Table) error
    QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args amqp.Table) (amqp.Queue, error)
    QueueBind(name, key, exchange string, noWait bool, args amqp.Table) error
    Consume(queue, consumer string, autoAck, exclusive, noLocal, noWait bool, args amqp.Table) (<-chan amqp.Delivery, error)
    Publish(exchange, key string, mandatory, immediate bool, msg amqp.Publishing) error
}

type AmqpConnection interface {
    Channel() (AmqpChannel, error)
    Close() error
}

type AmqpDial func(url string) (AmqpConnection, error)

You can create simple wrappers that delegate to the actual code:您可以创建委托给实际代码的简单包装器:

func AmqpDialWrapper(url string) (AmqpConnection, error) {
    conn, err := amqp.Dial(url)
    if err != nil {
        return nil, err
    }
    return AmqpConnectionWrapper{conn}, nil
}

type AmqpConnectionWrapper struct {
    conn *amqp.Connection
}

// If *amqp.Channel does not satisfy the consumer.AmqpChannel interface
// then you'll need another wrapper, a AmqpChannelWrapper, that implements
// the consumer.AmqpChannel interface and delegates to *amqp.Channel.
func (w AmqpConnectionWrapper) Channel() (AmqpChannel, error) {
    return w.conn.Channel()
}

func (w AmqpConnectionWrapper) Close() error {
    return w.conn.Close()
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM