اختبار جولانج خارج حصلت



لا أحد يحب كتابة الاختبارات. بالطبع أنا أمزح ، يحب الجميع كتابتها! كما سيقول قادة الفريق والموارد البشرية ، فإن الإجابة الصحيحة في المقابلات هي أنني أحب وكتبت الاختبارات حقًا. ولكن فجأة ترغب في كتابة الاختبارات بلغة أخرى. كيف تبدأ في كتابة كود الذهاب المغطى بالاختبار؟

الجزء 1. اختبار المعالج


عند الخروج من الصندوق ، هناك دعم لخادم http في "net / http" ، حتى تتمكن من رفعه دون أي جهد. تتيح لنا الفرص التي تم فتحها أن نشعر بالقوة البالغة ، وبالتالي ستعيد الشفرة المستخدم رقم 42.

func userHandler(w http.ResponseWriter, r *http.Request) { var user User userId, err := strconv.Atoi(r.URL.Query().Get("id")) if err != nil { w.Write([]byte( "Error")) return } if userId == 42 { user = User{userId, "Jack", 2} } jsonData, _ := json.Marshal(user) w.Write(jsonData) } type User struct { Id int Name string Rating uint } 

يتلقى هذا الرمز معلمة معرف المستخدم كمدخل ، ثم يحاكي وجود المستخدم في قاعدة البيانات ، ويعود. الآن نحن بحاجة لاختباره ...

هناك شيء رائع "net / http / htpptest" ، يسمح لك بمحاكاة مكالمة مع معالجنا ثم مقارنة الإجابة.

 r := httptest.NewRequest("GET", "http://127.0.0.1:80/user?id=42", nil) w := httptest.NewRecorder() userHandler(w, r) user := User{} json.Unmarshal(w.Body.Bytes(), &user) if user.Id != 42 { t.Errorf("Invalid user id %d expected %d", user.Id, 42) } 

الجزء 2. العسل ، لدينا API خارجي هنا


ولماذا نحتاج أن نأخذ نفسًا ، إذا استعدنا للتو؟ داخل خدماتنا ، عاجلاً أم آجلاً ، ستظهر واجهة برمجة تطبيقات خارجية. هذا هو وحش غريب يختبئ في الغالب ويمكنه التصرف كما يحلو له. بالنسبة للاختبارات ، نود زميلًا أكثر استيعابًا. وسوف يساعدنا htptest المكتشف مؤخرًا هنا أيضًا. كمثال ، رمز الاتصال هو واجهة برمجة تطبيقات خارجية مع نقل البيانات بشكل أكبر.

 func ApiCaller(user *User, url string) error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() return updateUser(user, resp.Body) } 

للتغلب على هذا ، يمكننا إنشاء نموذج API خارجي ، الخيار الأبسط هو:

  ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Access-Control-Allow-Origin", "*") fmt.Fprintln(w, `{ "result": "ok", "data": { "user_id": 1, "rating": 42 } }`) })) defer ts.Close() user := User{id: 1} err := ApiCaller(&user, ts.URL) 

ستحتوي ts.URL على سلسلة من التنسيق `http: //127.0.0.1: 49799` ، والذي سيكون عبارة عن محاكاة واجهة برمجة التطبيقات التي تستدعي التنفيذ لدينا

الجزء 3. دعنا نعمل مع القاعدة


هناك طريقة بسيطة: رفع عامل الميناء مع القاعدة ، والترحيل ، والتركيبات وتشغيل خدماتنا الممتازة. ولكن دعنا نحاول كتابة اختبارات بحد أدنى من التبعيات مع الخدمات الخارجية.

يسمح لك تنفيذ العمل مع القاعدة في العمل باستبدال برنامج التشغيل نفسه ، وتجاوز 100 صفحة من التعليمات البرمجية والانعكاس ، أقترح عليك أن تأخذ المكتبة github.com/DATA-DOG/go-sqlmock
يمكنك التعامل مع sql.Db على الرصيف. لنأخذ مثالاً أكثر إثارة للاهتمام قليلاً ، حيث سيكون هناك orm for - gorm .

 func DbListener(db *gorm.DB) { user := User{} transaction := db.Begin() transaction.First(&user, 1) transaction.Model(&user).Update("counter", user.Counter+1) transaction.Commit() } 

آمل أن يجعلك هذا المثال على الأقل تفكر في كيفية اختباره. في "mock.ExpectExec" يمكنك استبدال تعبير عادي يغطي الحالة التي تحتاجها. الشيء الوحيد الذي يجب تذكره هو أن الترتيب الذي يتم فيه تعيين التوقعات يجب أن يتطابق مع ترتيب وعدد المكالمات.

 func TestDbListener(t *testing.T) { db, mock, _ := sqlmock.New() defer db.Close() mock.ExpectBegin() result := []string{"id", "name", "counter"} mock.ExpectQuery("SELECT \\* FROM `Users`").WillReturnRows(sqlmock.NewRows(result).AddRow(1, "Jack", 2)) mock.ExpectExec("UPDATE `Users`").WithArgs(3, 1).WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit() gormDB, _ := gorm.Open("mysql", db) DbListener(gormDB.LogMode(true)) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } } 

لقد وجدت العديد من الأمثلة لاختبار القاعدة هنا .

الجزء 4. العمل مع نظام الملفات


لقد جربنا أيدينا في مناطق مختلفة وتوصلنا إلى أن كل شيء جيد للبلل. كل شيء ليس واضحًا هنا. أقترح نهجين ، بلل أو استخدام نظام الملفات.

الخيار 1 - سنبلل جميعًا على github.com/spf13/afero

الإيجابيات :
  • لست مضطرًا لإعادة أي شيء إذا كنت تستخدم هذه المكتبة بالفعل. (ولكن بعد ذلك تشعر بالملل من قراءته)
  • العمل مع نظام ملفات افتراضي ، والذي سيسرع بشكل كبير من اختباراتك.


السلبيات :
  • تعديل الرمز الحالي مطلوب.
  • لا يعمل chmod على نظام الملفات الظاهري. ولكن يمكن أن تكون ميزات منذ ذلك الحين تنص الوثائق على "تجنب مشكلات الأمان والأذونات".

من هذه النقاط القليلة ، أجريت اختبارين على الفور. في الإصدار مع نظام الملفات ، قمت بإنشاء ملف غير قابل للقراءة وتحقق من كيفية عمل النظام.

 func FileRead(path string) error { path = strings.TrimRight(path, "/") + "/" //     files, err := ioutil.ReadDir(path) if err != nil { return fmt.Errorf("cannot read from file, %v", err) } for _, f := range files { deleteFileName := path + f.Name() _, err := ioutil.ReadFile(deleteFileName) if err != nil { return err } err = os.Remove(deleteFileName) //     } return nil } 

يتطلب استخدام afero.Fs تعديلات طفيفة ، ولكن لا شيء يتغير بشكل أساسي في التعليمات البرمجية

 func FileReadAlt(path string, fs afero.Fs) error { path = strings.TrimRight(path, "/") + "/" //     files, err := afero.ReadDir(fs, path) if err != nil { return fmt.Errorf("cannot read from file, %v", err) } for _, f := range files { deleteFileName := path + f.Name() _, err := afero.ReadFile(fs, deleteFileName) if err != nil { return err } err = fs.Remove(deleteFileName) //     } return nil } 

لكن مرحنا لن يكتمل ما لم نكتشف مدى سرعة afero من الأصلي.
دقيقة المعيار:

 BenchmarkIoutil 5000 242504 ns/op 7548 B/op 27 allocs/op BenchmarkAferoOs 300000 4259 ns/op 2144 B/op 30 allocs/op BenchmarkAferoMem 300000 4169 ns/op 2144 B/op 30 allocs/op 

لذا ، تعد المكتبة مرتبة من حيث الحجم قبل المعيار ، ولكن استخدام نظام الملفات الظاهري أو الحقيقي هو حسب تقديرك.

أوصي بـ:

haisum.imtqy.com/2017/09/11/golang-ioutil-readall
matthias-endler.de/2018/go-io-testing

خاتمة


أنا بصراحة أحب التغطية بنسبة 100٪ ، لكن التعليمات البرمجية غير المكتبية لا تحتاجها. وحتى أنه لا يضمن الحماية من الخطأ. ركز على متطلبات العمل ، وليس قدرة الدالة على إرجاع 10 أخطاء مختلفة.

بالنسبة لأولئك الذين يحبون كزة رمز وإجراء الاختبارات ، مستودع .

Source: https://habr.com/ru/post/ar425025/


All Articles