HTTPリクエストをAPIに送信しながらモックする方法 -- unit-testing フィールド と go フィールド 関連 問題

How to mock while sending http request to API












0
vote

問題

日本語

Go-Ginを使ってGo GoにREST APIを実装しました。次の

のようなハンドラ機能をテストしようとしています。 <事前> <コード> func editNameHandler(c *gin.Context) { // make a ReST call to another server callToAnotherServer() c.Status(200) }

Mock callToAnotherServer メソッドによると、テストケースはまったく3番目のパーティサーバーを呼び出さないようにしたい。

私のテストケースは

のように見えます <事前> <コード> func TestSeriveIdStatusRestorePatch(t *testing.T) { // Request body send := strings.NewReader(`{"name":"Robert"}` // this function sends an HTTP request to the API which ultimately calls editNameHandler // Ignore the variables.The variables are retrieved in code this is to simplify question ValidTokenTestPatch(API_VERSION+"/accounts/"+TestAccountUUID+"/students/"+TestStudentId, t, send, http.StatusOK) }

私はモック関数どれを渡すことができるモックする機能HTTPリクエストを送信しながら機能を渡すことができる方法は疑問に思いますか?そのような場合は、どのようにして機能をモックにすることができますか。ベストプラクティスは何ですか?

英語

I have implemented a ReST API in Go using go-gin and I am trying to test a handler function which looks like the following

func editNameHandler(c *gin.Context) {         // make a ReST call to another server         callToAnotherServer()         c.Status(200) } 

I want to to mock callToAnotherServer method so that my test case doesn't call the 3rd party server at all.

My test case looks like

 func TestSeriveIdStatusRestorePatch(t *testing.T) {      // Request body     send := strings.NewReader(`{"name":"Robert"}`       // this function sends an HTTP request to the API which ultimately calls editNameHandler     // Ignore the variables.The variables are retrieved in code this is to simplify question     ValidTokenTestPatch(API_VERSION+"/accounts/"+TestAccountUUID+"/students/"+TestStudentId, t, send, http.StatusOK)         } 

I went through Mock functions in Go which mentions how we can pass a function to mock. I am wondering how we can pass a function while sending http request? How can I mock function in such case. What is the best practice?

</div
     
 
 

回答リスト

0
 
vote

この質問に対する独身の回答があるとは思わないが、私が現在Go-Ginを使って現在依存療法の注入を行っているのかについてのアプローチを共有します(ただし、他のルーターとほぼ同じであるべきです。 )

ビジネスの観点から、私はビジネスルール/処理を担当する私のサービスへのすべてのアクセスを折り返す構造体を持っています。

<事前> <コード> // WchyContext is an application-wide context type WchyContext struct { Health services.HealthCheckService Tenant services.TenantService ... whatever }

私のサービスはちょうどインターフェースです。

<事前> <コード> // HealthCheckService is a simple general purpose health check service type HealthCheckService interface { IsDatabaseOnline() bool } MockedHealthCheck PostgresHealthCheck 、<コード> PostgresTenantService など、 PostgresHealthCheck などの

私のルータはwchycontextに依存するよりも依存します。このコードは次のようになります。

<事前> <コード> func GetMainEngine(ctx context.WchyContext) *gin.Engine { router := gin.New() router.Use(gin.Logger()) router.GET("/status", Status(ctx)) router.GET("/tenants/:domain", TenantByDomain(ctx)) return router }`

<コード> Status および<コード> TenantByDomain Orceは、ハンドラファクトリのように機能します。

のような特定のコンテキストに基づく新しいハンドラを作成することです。 <事前> <コード> type statusHandler struct { ctx context.WchyContext } // Status creates a new Status HTTP handler func Status(ctx context.WchyContext) gin.HandlerFunc { return statusHandler{ctx: ctx}.get() } func (h statusHandler) get() gin.HandlerFunc { return func(c *gin.Context) { c.JSON(200, gin.H{ "healthy": gin.H{ "database": h.ctx.Health.IsDatabaseOnline(), }, "now": time.Now().Format("2006.01.02.150405"), }) } }

あなたが見ることができるように、私の健康チェックハンドラは私のサービスの具体的な実装を気にしません、私は ctx.

にあるものは何でもそれを使うだけです

最後の部分は現在の実行環境によって異なります。自動テスト中に、Mocked / Stubbed Servicesを使用して新しい<コード> // HealthCheckService is a simple general purpose health check service type HealthCheckService interface { IsDatabaseOnline() bool } 0 を作成し、そのようにGetMainEngineに送信します。

<事前> <コード> // HealthCheckService is a simple general purpose health check service type HealthCheckService interface { IsDatabaseOnline() bool } 1

およびHTTPポートを本当に聴くように設定すると、ワイヤーアップは次のようになります。

<事前> <コード> // HealthCheckService is a simple general purpose health check service type HealthCheckService interface { IsDatabaseOnline() bool } 2

これについて好きではないことがいくつかあります、おそらく後でリファクタリング/改善しますが、それはこれまでうまく機能しています。

フルコード参照を見たい場合は、ここでこのプロジェクトに取り組んでいます。 https:// github。 COM / WECANHEARYOU / WCHY

どういうわけかあなたを助けることができることを願っています

 

I don't think there is single response for this question, but I'll share my approach on how I'm currently doing Dependency Injection on Go with go-gin (but should be the nearly the same with any other router).

From a business point of view, I have a struct that wraps all access to my services which are responsible for business rules/processing.

// WchyContext is an application-wide context type WchyContext struct {     Health   services.HealthCheckService     Tenant   services.TenantService     ... whatever } 

My services are then just interfaces.

// HealthCheckService is a simple general purpose health check service type HealthCheckService interface {     IsDatabaseOnline() bool } 

Which have mulitple implementations, like MockedHealthCheck, PostgresHealthCheck, PostgresTenantService and so on.

My router than depends on a WchyContext, which the code looks like this:

func GetMainEngine(ctx context.WchyContext) *gin.Engine {     router := gin.New()     router.Use(gin.Logger())     router.GET("/status", Status(ctx))     router.GET("/tenants/:domain", TenantByDomain(ctx))     return router }` 

Status and TenantByDomain act like a handler-factory, all it does is create a new handler based on given context, like this:

type statusHandler struct {     ctx context.WchyContext }  // Status creates a new Status HTTP handler func Status(ctx context.WchyContext) gin.HandlerFunc {     return statusHandler{ctx: ctx}.get() }  func (h statusHandler) get() gin.HandlerFunc {     return func(c *gin.Context) {         c.JSON(200, gin.H{             "healthy": gin.H{                 "database": h.ctx.Health.IsDatabaseOnline(),             },             "now":     time.Now().Format("2006.01.02.150405"),         })     } } 

As you can see, my health check handler doesn't care about concrete implementation of my services, I just use it whatever is in the ctx.

The last part depends on current execution environment. During automated tests I create a new WchyContext using mocked/stubbed services and send it to GetMainEngine, like this:

ctx := context.WchyContext{     Health: &services.InMemoryHealthCheckService{Status: false},     Tenant: &services.InMemoryTenantService{Tenants: []*models.Tenant{         &models.Tenant{ID: 1, Name: "Orange Inc.", Domain: "orange"},         &models.Tenant{ID: 2, Name: "The Triathlon Shop", Domain: "trishop"},     }} } router := handlers.GetMainEngine(ctx)  request, _ := http.NewRequest(method, url, nil) response := httptest.NewRecorder() router.ServeHTTP(response, request) ... check if response matches what you expect from your handler 

And when you setup it to really listen to a HTTP port, the wiring up looks like this:

var ctx context.WchyContext var db *sql.DB  func init() {     db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))      ctx = context.WchyContext{         Health: &services.PostgresHealthCheckService{DB: db},         Tenant: &services.PostgresTenantService{DB: db}     } }  func main() {     handlers.GetMainEngine(ctx).Run(":" + util.GetEnvOrDefault("PORT", "3000")) } 

There are a few things that I don't like about this, I'll probably refactor/improve it later, but it has been working well so far.

If you want to see full code reference, I'm working on this project here https://github.com/WeCanHearYou/wchy

Hope it can help you somehow.

</div
 
 

関連する質問

137  float64をint内のintに変換します  ( Convert a float64 to an int in go ) 
Goのint in ont on intにどのように変換するのでしょうか。 strconv パッケージを使用して、文字列との間で、文字列ではないデータ型間では使用できません。私は fmt.Sprintf を文字列に変換してから strconv を必要なデー...

-1  チャンクアップロードに行きます  ( Go chunk upload ) 
チャンクアップロードをサポートしていますか? ファイルを1部のマルチパートアップロードとしてアップロードします。 知っている限り: type Part は、マルチパートボディの単一部分を表し、<コード> func (*Part) Read は、ヘッダーの後...

0  'Cors Policy'をブロックしたが、ヘッダが存在する  ( Blocked by cors policy but header is present ) 
Goと <li>0 を使用してREST APIを作成し、VUEを使用してフロントエンドを作成しました。 API要求を作成するたびに、私のブラウザコンソールのエラー <li>1 が取得されます。このエラーが発生しない唯一の時間は、 <li>2 Routeです...

1  Golangを用いた複数条件によるMongoクエリの問題  ( Issue in mongo query with multiple conditions by using golang ) 
私は次のような文書を持っています - <事前> <コード> { "_id" : "580eef0e4dcc220df897a9cb", "brandId" : 15, "category" : "air_conditioner...

1  すべての未読メッセージを検索する方法IMAP CMD / GO  ( How to search all unread messages imap cmd go ) 
電子メールクライアントを開発しています.IMAPサーバーはすべての未読メッセージを送っていない理由もあります。 2915の電子メールのうち、それは2888のみに戻ります。私は連続して読み取り/未読としてマークを付けようとしました、そして、それらすべての読み上げ...

0  1つのクライアントのメッセージをWebSocket(Golang)上の別のクライアントに反映させる  ( Reflect one clients message to another client on websocket golang ) 
バックエンドでWebSocketを使用して問題があります。 1つのクライアントが他のクライアントの場所を追跡している間、GolangとOld DeveloperをWebScocketを使用して古い古い開発者を再書き直す必要があります。 与えられたURL でW...

75  http.handleFuncのパターン内のワイルドカード  ( Wildcards in the pattern for http handlefunc ) 
GO(言語)にハンドラを登録する場合は、パターンにワイルドカードを指定する方法がありますか? 例えば: <事前> <コード> http.HandleFunc("/groups/*/people", peopleInGroupHandler) ここで、 *...

3  (go-sql-driver / mysql)packets.goの予期しないEOFとビジーバッファ  ( Unexpected eof and busy buffer in go sql driver mysql packets go ) 
<コード> SetConnMaxLifetime 、 SetMaxIdleConns とと> <select name="pname" class="textfields" id="model" style="width:180px;" onchange...

7  Rubyのチャンネルに行きます  ( Go channels in ruby ) 
Goプログラミング言語では、「チャンネル」というファイルを使用してメッセージを送ることができます。 http://dorg/doc/effect_go.html#channels 私は、特にIPCのために、そのようなものをルビーで使うのが大好きです。 ...

2  EMACS:ロードファイルを開くことができません、Go-AutoOpomplete  ( Emacs cannot open load file go autocomplete ) 
現在emacsでemacsで Package Managerを使用してGocodeに必要なEmacs(Regular)オートコンプリートバージョンをインストールしました(gocodeはこのマネージャではこのマネージャでは利用できません)。 私のディレクトリ...




© 2022 cndgn.com All Rights Reserved. Q&Aハウス 全著作権所有