Перемешивание интерфейса, определенного в тестовом файле: интерфейс "Не определено"? - PullRequest
0 голосов
/ 21 января 2020

Это ошибка, которую я пытался воспроизвести на минимальном примере, но пока безуспешно. Модуль Go аналогичен следующему:

.
├── go.mod
└── handler
    ├── handler.go
    ├── handler_test.go
    └── mock_handler.go

, где handler.go пуст (содержит только package handler), handler_test.go содержит определение интерфейса Handler (то же самое, что и Go s http.Handler) и тест-заполнитель,

package handler

import (
    "net/http"
    "testing"
)

type Handler interface {
    ServeHTTP(http.ResponseWriter, *http.Request)
}

func TestMockHandler(t *testing.T) {
    mockHandler := MockHandler{}
    t.Log(mockHandler)
}

и mock_handler.go содержит структуру MockHandler, которая реализует интерфейс Handler и генерируется с использованием moq :

// Code generated by moq; DO NOT EDIT.
// github.com/matryer/moq

package handler

import (
    "net/http"
    "sync"
)

var (
    lockMockHandlerServeHTTP sync.RWMutex
)

// Ensure, that MockHandler does implement Handler.
// If this is not the case, regenerate this file with moq.
var _ Handler = &MockHandler{}

// MockHandler is a mock implementation of Handler.
//
//     func TestSomethingThatUsesHandler(t *testing.T) {
//
//         // make and configure a mocked Handler
//         mockedHandler := &MockHandler{
//             ServeHTTPFunc: func(in1 http.ResponseWriter, in2 *http.Request)  {
//                 panic("mock out the ServeHTTP method")
//             },
//         }
//
//         // use mockedHandler in code that requires Handler
//         // and then make assertions.
//
//     }
type MockHandler struct {
    // ServeHTTPFunc mocks the ServeHTTP method.
    ServeHTTPFunc func(in1 http.ResponseWriter, in2 *http.Request)

    // calls tracks calls to the methods.
    calls struct {
        // ServeHTTP holds details about calls to the ServeHTTP method.
        ServeHTTP []struct {
            // In1 is the in1 argument value.
            In1 http.ResponseWriter
            // In2 is the in2 argument value.
            In2 *http.Request
        }
    }
}

// ServeHTTP calls ServeHTTPFunc.
func (mock *MockHandler) ServeHTTP(in1 http.ResponseWriter, in2 *http.Request) {
    if mock.ServeHTTPFunc == nil {
        panic("MockHandler.ServeHTTPFunc: method is nil but Handler.ServeHTTP was just called")
    }
    callInfo := struct {
        In1 http.ResponseWriter
        In2 *http.Request
    }{
        In1: in1,
        In2: in2,
    }
    lockMockHandlerServeHTTP.Lock()
    mock.calls.ServeHTTP = append(mock.calls.ServeHTTP, callInfo)
    lockMockHandlerServeHTTP.Unlock()
    mock.ServeHTTPFunc(in1, in2)
}

// ServeHTTPCalls gets all the calls that were made to ServeHTTP.
// Check the length with:
//     len(mockedHandler.ServeHTTPCalls())
func (mock *MockHandler) ServeHTTPCalls() []struct {
    In1 http.ResponseWriter
    In2 *http.Request
} {
    var calls []struct {
        In1 http.ResponseWriter
        In2 *http.Request
    }
    lockMockHandlerServeHTTP.RLock()
    calls = mock.calls.ServeHTTP
    lockMockHandlerServeHTTP.RUnlock()
    return calls
}

Чтобы сгенерировать mock_handler.go, я сначала определил Handler в handler.go, а затем в каталоге handler выполнил команду

 moq -out mock_handler.go . Handler

Впоследствии я переместил определение интерфейса Handler в handler_test.go, поскольку оно предназначено только для тестирования.

В этом упрощенном примере я могу запустить go test в пакете Режим списка в каталоге root:

~/g/s/g/k/mockhandler> go test ./... -count=1
ok      github.com/kurtpeek/mockhandler/handler 0.448s

Мой «фактический» модуль имеет похожую структуру, подобную следующей:

.
├── cmd
│   └── root.go
├── images
├── main.go
└── vpp
    ├── ensure_license_test.go
    └── mock_handler.go

Интерфейс Handler определен в в ensure_license_test.go точно так же, как в handler_test.go в упрощенном модуле; ensure_license_test.go начинается следующим образом:

package vpp

import (
    "encoding/json"
    "io/ioutil"
    "net/http"
    "net/http/httptest"
    "net/url"
    "testing"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

type Handler interface {
    ServeHTTP(http.ResponseWriter, *http.Request)
}

type MockTestServer struct {
    TestServer  *httptest.Server
    MockHandler *MockHandler
}

mock_handler.go также точно так же, как mock_handler.go в упрощенном модуле (кроме имени пакета).

Тем не менее, когда я запускаю go test ./... в каталоге root фактического модуля я получаю ошибку undefined для Handler:

~/g/s/g/f/vpp-client> go test ./... -count=1
# github.com/fleetsmith/vpp-client/vpp
vpp/mock_handler.go:17:7: undefined: Handler
ok      github.com/fleetsmith/vpp-client/vpp    0.128s

Странно, когда я запускаю это из пакета vpp , он передает:

> go test ./... -count=1
ok      github.com/fleetsmith/vpp-client/vpp    0.601s

Что может быть причиной того, что go test не может определить определение Handler при запуске его в режиме списка пакетов из каталога root, как в первом примере

1 Ответ

0 голосов
/ 21 января 2020

Оказывается, что это был пакет cmd, для которого тесты не выполнялись, поскольку он не смог импортировать интерфейс Handler из файла теста в пакете vpp. Поэтому я изменил строку 17 из mock_handler.go, чтобы использовать http.Handler вместо Handler:

var _ http.Handler = &MockHandler{}

Теперь тесты пройдены:

~/g/s/g/f/vpp-client> go test ./...
?       github.com/fleetsmith/vpp-client    [no test files]
?       github.com/fleetsmith/vpp-client/cmd    [no test files]
ok      github.com/fleetsmith/vpp-client/vpp    0.462s

Я также смог удалите определение интерфейса Handler из ensure_license_test.go, так как сейчас я напрямую использую определение из стандартной библиотеки http.

Недостаток этого подхода заключается в том, что для него требуется редактирование кода, который был выполнен автоматически -произведенный moq, я не смог понять, как запустить moq для имитации интерфейсов в стандартной библиотеке Go, и в любом случае этот интерфейс вряд ли изменится.

...