I'm working on a small AMQP consumer and i want to test my consumer code, but im struggleing to mock the 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:
//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:
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?
P.S.: im aware of https://github.com/NeowayLabs/wabbit but i would prefer to solve this on a lower level :)
Update: @mkopriva suggested to use another wrapper method, so i tried:
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