我想对POST api进行功能测试。在该方法中,它将在生产环境中调用DynamoDB客户端。我已经启动了Docker容器供DynamoDB对其进行测试。但是,当我测试此代码时,它实际上调用了生产客户端DynamoDB。
router.HandleFunc("/v1/fxm/worklog", api.SaveFxmWorklog).Methods(PostMethod)
方法:
func SaveFxmWorklog(w http.ResponseWriter, r *http.Request) {
var rb entity.FxmWorklogDetails
err := json.NewDecoder(r.Body).Decode(&rb)
if err != nil {
log.Error().Err(err).Msgf("Failed to parse the fxm worklog request %s", util.ToJSON(rb))
util.ErrorResponse(w, err.Error(), http.StatusBadRequest)
return
}
log.Info().Msgf("Save fxm worklog request received for order %s", rb.OrderId)
response, err := service.FxmService.SaveFxmWorklogEvent(rb)
if err != nil {
log.Error().Err(err).Msgf("Failed to save fxm worklog event %s", util.ToJSON(rb))
util.ErrorResponse(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(response.HTTPStatusCode)
_ = json.NewEncoder(w).Encode(response)
}
var FxmService FxmWorklogService
type FxmWorklogService interface {
SaveFxmWorklogEvent(event entity.FxmWorklogDetails) (*response.BaseResponse, error)
}
type FxmWorklogServiceImpl struct {
fxmRepo fxm.Repository
}
func init() {
FxmService = FxmWorklogServiceImpl{fxmRepo: fxm.NewRepo()}
}
func (f FxmWorklogServiceImpl) SaveFxmWorklogEvent(event entity.FxmWorklogDetails) (*response.BaseResponse, error) {
err := f.fxmRepo.SaveFxmWorklogDetails(&event)
if err != nil {
log.Error().Err(err).Msgf("Failed to save fxm worklog event for order %s", event.OrderId)
return nil, err
}
return &response.BaseResponse{Status: util.SuccessStatusMessage, StatusCode: util.SuccessStatusCode, HTTPStatusCode: http.StatusOK}, nil
}
type Repository interface {
SaveFxmWorklogDetails(worklogDetails *entity.FxmWorklogDetails) error
UpdateFxmWorklogDetails(worklogDetails *entity.FxmWorklogDetails) error
GetEditOrderFxmWorklogDetails(orderId string) (*entity.FxmWorklogDetails, error)
}
var (
tableName = "delivery-assistance.fxm_worklog_events"
key = "orderId"
)
type Repo struct {
Db persistence.DBOperation
}
func NewRepo() *Repo {
return &Repo{Db: persistence.NewDB()}
}
func (r *Repo) SaveFxmWorklogDetails(worklogDetails *entity.FxmWorklogDetails) error {
return r.Db.PutItem(tableName, worklogDetails)
}
func (r *Repo) UpdateFxmWorklogDetails(worklogDetails *entity.FxmWorklogDetails) error {
return r.Db.PutItem(tableName, worklogDetails)
}
func (r *Repo) GetEditOrderFxmWorklogDetails(orderId string) (*entity.FxmWorklogDetails, error) {
var output []entity.FxmWorklogDetails
err := r.Db.GetItemsWithSameKeyAndStringValue(tableName, key, orderId, &output)
if err != nil {
log.Error().Err(err).Msgf("Failed to get fxm worklog details for order %s", orderId)
return nil, err
}
if len(output) > 0 {
for _, event := range output {
if event.Action == util.EditOrderAction && event.ExceptionName == util.EditOrderException {
return &event, nil
}
}
}
return nil, nil
}
// This struct extends the DBOperation interface for DynamoDB implementation
type DynamoDB struct {
client *dynamodb.DynamoDB
}
// DynamoDB Constructor
func NewDB() *DynamoDB {
return &DynamoDB{dynamodb2.Client()}
}
//This function Creates a new item, or replaces an old item with a new item.
func (db *DynamoDB) PutItem(tableName string, data interface{}) error {
av, err := dynamodbattribute.MarshalMap(data)
if err != nil {
log.Error().Err(err).Msgf("Failed to convert to attribute value %s", util.ToJSON(data))
return err
}
input := &dynamodb.PutItemInput{
Item: av,
TableName: aws.String(tableName),
}
log.Info().Msgf("Dynamo db endpoint: %d", db.client.Endpoint)
_, err = db.client.PutItem(input)
if err != nil {
log.Error().Err(err).Msgf("Error calling put item")
return err
}
return nil
}
DynamoDB客户端:
var (
client *dynamodb.DynamoDB
)
func init() {
sess, err := session.NewSession(&aws.Config{
Region: aws.String(config.Client.GetString("AWS_CREDS.region")),
Credentials: credentials.NewStaticCredentialsFromCreds(credentials.Value{
AccessKeyID: config.Client.GetString("AWS_CREDS.access_key_id"),
SecretAccessKey: config.Client.GetString("AWS_CREDS.secret_access_key"),
}),
Endpoint: aws.String(config.Client.GetString("AWS_CREDS.endpoint")),
})
if err != nil {
log.Error().Err(err).Msgf("Failed to connect to aws")
}
client = dynamodb.New(sess)
}
func Client() *dynamodb.DynamoDB {
return client
}
功能测试:
func Test_Functional_Managers(t *testing.T) {
if testing.Short() {
t.Skip("Skipping Functional Test")
return
}
testContainers, err := util.SetupContainers()
if err != nil {
t.Errorf("ErrorInitializing Container, error = %v", err)
}
log.Info().Msgf("http://%s:%s", testContainers.Dynamo.Host(), testContainers.Dynamo.Port())
SaveFxmWorklogTest(t, testContainers)
}
func SaveFxmWorklogTest(t *testing.T, containers *util.Containers) {
var FxmWorklogDetails = `{"orderId":"1231","worklogType":"RECOVERY","businessLine":"Food","serviceLine":"FOOD","action":"ORDER_EDIT","exceptionName":"ITEM_OUT_OF_STOCK","system":"VENDOR","subSystem":"Delivery","createdAt":1234567890}`
var r io.Reader
r = strings.NewReader(FxmWorklogDetails)
url := fmt.Sprintf("http://%s:%s", containers.Dynamo.Host(), containers.Dynamo.Port())
config.Client.SetString("AWS_CREDS.access_key_id", "random")
config.Client.SetString("AWS_CREDS.secret_access_key", "random")
config.Client.SetString("AWS_CREDS.endpoint", url)
if err := containers.Dynamo.CreateTable(test_data.CreateTableRequest); err != nil {
t.Errorf("Error Creating table, err = %v", err)
}
req, _ := http.NewRequest("POST", "/v1/fxm/worklog", r)
w := httptest.NewRecorder()
api.SaveFxmWorklog(w, req)
}
TestContainer:
type Containers struct {
Dynamo *dynamo.Dynamo
}
func SetupContainers() (*Containers, error) {
dynamoContainer, err := dynamo.NewContainer()
if err != nil {
return nil, err
}
return &Containers{
Dynamo: dynamoContainer,
}, nil
}
测试日志:
=== RUN Test_Functional_Managers
2020/06/12 13:21:35 Starting container id: 5a987112c28d image: quay.io/testcontainers/ryuk:0.2.3
2020/06/12 13:21:35 Waiting for container id 5a987112c28d image: quay.io/testcontainers/ryuk:0.2.3
2020/06/12 13:21:35 Container is ready id: 5a987112c28d image: quay.io/testcontainers/ryuk:0.2.3
2020/06/12 13:21:35 Starting container id: 691016e5a216 image: amazon/dynamodb-local
2020/06/12 13:21:35 Waiting for container id 691016e5a216 image: amazon/dynamodb-local
2020/06/12 13:21:36 Container is ready id: 691016e5a216 image: amazon/dynamodb-local
{"level":"info","time":"2020-06-12T13:21:36+05:30","message":"http://localhost:32773"}
{"level":"info","time":"2020-06-12T13:21:37+05:30","message":"Endpoint: http://localhost:32773"}
{"level":"info","time":"2020-06-12T13:21:37+05:30","message":"Save fxm worklog request received for order 1231"}
{"level":"info","time":"2020-06-12T13:21:37+05:30","message":"Dynamo db endpoint: %!d(string=http://dynamodb.ap-southeast-1.amazonaws.com)"}
{"level":"error","error":"ResourceNotFoundException: Requested resource not found","time":"2020-06-12T13:21:37+05:30","message":"Error calling put item"}
{"level":"error","error":"ResourceNotFoundException: Requested resource not found","time":"2020-06-12T13:21:37+05:30","message":"Failed to save fxm worklog event for order 1231"}
{"level":"error","error":"ResourceNotFoundException: Requested resource not found","time":"2020-06-12T13:21:37+05:30","message":"Failed to save fxm worklog event {\"orderId\":\"1231\",\"worklogType\":\"RECOVERY\",\"businessLine\":\"Food\",\"serviceLine\":\"FOOD\",\"action\":\"ORDER_EDIT\",\"exceptionName\":\"ITEM_OUT_OF_STOCK\",\"system\":\"VENDOR\",\"subSystem\":\"Delivery\",\"createdAt\":1234567890}"}
--- PASS: Test_Functional_Managers (2.10s)
PASS
如果包中不存在,则可以实现TestMain(m * testing.M)函数,并更改AWS的凭证:
func TestMain(m *testing.M) {
sess, err := session.NewSession(&aws.Config{
Region: aws.String(config.Client.GetString("AWS_CREDS_TEST.region")),
Credentials: credentials.NewStaticCredentialsFromCreds(credentials.Value{
AccessKeyID: config.Client.GetString("AWS_CREDS_TEST.access_key_id"),
SecretAccessKey: config.Client.GetString("AWS_CREDS_TEST.secret_access_key"),
}),
Endpoint: aws.String(config.Client.GetString("AWS_CREDS_TEST.endpoint")),
})
if err != nil {
log.Error().Err(err).Msgf("Failed to connect to aws")
}
client = dynamodb.New(sess)
exitVal := m.Run()
os.Exit(exitVal)
}