---
title: "التوثيق وحالات الاختبار: أدوات قوية لحل مشكلات تطوير OpenResty - API7.ai"
description: "يوفر OpenResty ليس فقط وثائق تفصيلية تحتوي على معلمات API، وأنواع القيم المرتجعة، وأمثلة للرموز البرمجية، بل يتم أيضًا اختباره بشكل شامل باستخدام حالات اختبار، مما يساعد المطورين على استخدام OpenResty بفعالية."
url: "https://api7.ai/ar/learning-center/openresty/openresty-documentation-and-test-cases"
---

# التوثيق وحالات الاختبار: أدوات قوية لحل مشكلات تطوير OpenResty

API7.ai

October 23, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

بعد تعلم مبادئ وبعض المفاهيم الأساسية لـ OpenResty، سنبدأ أخيرًا في تعلم واجهة برمجة التطبيقات (API).

من تجربتي الشخصية، تعلم واجهة برمجة التطبيقات لـ OpenResty يعتبر سهلًا نسبيًا، لذا لن يتطلب الأمر العديد من المقالات لتقديمه. قد تتساءل: أليست واجهة برمجة التطبيقات هي الجزء الأكثر شيوعًا وأهمية؟ لماذا لا نقضي الكثير من الوقت عليها؟ هناك اعتباران رئيسيان.

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

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

ثانيًا، توفر OpenResty حالات اختبار شاملة. كما ذكرت، توضح وثائق OpenResty أمثلة كود لواجهات برمجة التطبيقات. ومع ذلك، بسبب قيود المساحة، لا تعرض الوثائق الإبلاغ عن الأخطاء ومعالجتها في مختلف الحالات غير الطبيعية وطريقة استخدام واجهات برمجة التطبيقات المتعددة.

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

بالنسبة لمطوري OpenResty، أفضل مواد تعلم واجهة برمجة التطبيقات هي الوثائق الرسمية وحالات الاختبار، وهي مهنية وصديقة للقارئ.

"أعطِ رجلاً سمكة، تطعمه ليوم واحد؛ علم رجلاً صيد السمك، تطعمه مدى الحياة." دعونا نستخدم مثالًا حقيقيًا لتجربة كيفية استغلال قوة الوثائق ومجموعة حالات الاختبار في تطوير OpenResty.

## لنأخذ واجهة برمجة التطبيقات `get` لـ shdict كمثال

بناءً على منطقة الذاكرة المشتركة لـ NGINX، فإن القاموس المشترك (shared dictionary) هو كائن قاموس Lua، يمكنه الوصول إلى البيانات عبر عدة عمال وتخزين بيانات مثل تحديد المعدل، التخزين المؤقت، إلخ. هناك أكثر من 20 واجهة برمجة تطبيقات متعلقة بالقواميس المشتركة - وهي أكثر واجهات برمجة التطبيقات استخدامًا وأهمية في OpenResty.

لنأخذ أبسط عملية `get` كمثال؛ يمكنك النقر على [رابط الوثائق](https://github.com/openresty/lua-nginx-module/#ngxshareddictget) للمقارنة. مثال الكود التالي هو نسخة مصغرة مأخوذة من الوثائق الرسمية.

```
http {
      lua_shared_dict dogs 10m;
      server {
          location /demo {
              content_by_lua_block {
                  local dogs = ngx.shared.dogs
                  dogs:set("Jim", 8)
                  local v = dogs:get("Jim")
                  ngx.say(v)
              }
          }
      }
  }
```

كملاحظة سريعة، قبل أن نتمكن من استخدام القاموس المشترك في كود Lua، نحتاج إلى إضافة كتلة ذاكرة في `nginx.conf` باستخدام توجيه `lua_shared_dict`، والذي يتم تسميته "dogs" وحجمه 10 ميجابايت. بعد تعديل `nginx.conf`، تحتاج إلى إعادة تشغيل العملية والوصول إليها باستخدام متصفح أو أمر `curl` لرؤية النتائج.

ألا يبدو هذا مرهقًا بعض الشيء؟ دعونا نعدله بشكل أكثر بساطة. كما ترى، استخدام واجهة سطر الأوامر resty بهذه الطريقة له نفس تأثير تضمين الكود في `nginx.conf`.

```
$ resty --shdict 'dogs 10m' -e 'local dogs = ngx.shared.dogs
 dogs:set("Jim", 8)
 local v = dogs:get("Jim")
 ngx.say(v)
 '
```

أنت الآن تعرف كيف يعمل `nginx.conf` وكود Lua معًا، وقد نجحت في تشغيل طرق set و get للقاموس المشترك. بشكل عام، يتوقف معظم المطورين عند هذا الحد. هناك بعض الأشياء التي تستحق الملاحظة هنا.

1. أي مراحل لا يمكن استخدام واجهات برمجة التطبيقات المتعلقة بالذاكرة المشتركة؟
2. نرى في كود المثال أن دالة get لها قيمة إرجاع واحدة فقط. متى سيكون هناك أكثر من قيمة إرجاع؟
3. ما هو نوع الإدخال لدالة get؟ هل هناك حد للطول؟

لا تقلل من شأن هذه الأسئلة؛ يمكن أن تساعدنا على فهم OpenResty بشكل أفضل، وسأأخذك خلالها واحدة تلو الأخرى.

### السؤال 1: أي مراحل لا يمكن استخدام واجهات برمجة التطبيقات المتعلقة بالذاكرة المشتركة؟

لننظر إلى السؤال الأول. الإجابة بسيطة؛ الوثائق لديها قسم `context` (أي قسم السياق) يوضح البيئات التي يمكن استخدام واجهة برمجة التطبيقات فيها.

```
context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
```

كما ترى، لا يتم تضمين مراحل `init` و `init_worker`، مما يعني أن واجهة برمجة التطبيقات `get` للذاكرة المشتركة لا يمكن استخدامها في هاتين المرحلتين. يرجى ملاحظة أن كل واجهة برمجة تطبيقات للذاكرة المشتركة يمكن استخدامها في مراحل مختلفة. على سبيل المثال، يمكن استخدام واجهة برمجة التطبيقات `set` في مرحلة `init`.

دائمًا، اقرأ الوثائق عند استخدامها. بالطبع، قد تحتوي وثائق OpenResty أحيانًا على أخطاء ونقائص، لذا تحتاج إلى التحقق منها باختبارات فعلية.

بعد ذلك، دعونا نعدل مجموعة الاختبار للتأكد من أن مرحلة `init` يمكنها تشغيل واجهة برمجة التطبيقات `get` للقاموس المشترك.

كيف يمكننا العثور على مجموعة حالات الاختبار المتعلقة بالذاكرة المشتركة؟ حالات اختبار OpenResty كلها موجودة في الدليل `/t` ويتم تسميتها بشكل منتظم، أي `self-incremented-number-function-name.t`. ابحث عن `shdict`، وستجد `043-shdict.t`، وهي مجموعة حالات اختبار الذاكرة المشتركة، والتي تحتوي على ما يقرب من 100 حالة اختبار، بما في ذلك اختبارات لظروف مختلفة طبيعية وغير طبيعية.

لنجرب تعديل أول حالة اختبار.

يمكنك استبدال مرحلة `content` بمرحلة `init` وإزالة الكود الزائد لمعرفة ما إذا كانت واجهة `get` تعمل. ليس عليك فهم كيفية كتابة وتنظيم وتشغيل حالة الاختبار في هذه المرحلة. تحتاج فقط إلى معرفة أنها تختبر واجهة `get`.

```
 === TEST 1: string key, int value
     --- http_config
         lua_shared_dict dogs 1m;
     --- config
         location = /test {
             init_by_lua '
                 local dogs = ngx.shared.dogs
                 local val = dogs:get("foo")
                 ngx.say(val)
             ';
         }
     --- request
     GET /test
     --- response_body
     32
     --- no_error_log
     [error]
     --- ONLY
```

يجب أن تكون قد لاحظت أنه في نهاية حالة الاختبار، أضفت علامة `--ONLY`، مما يعني تجاهل جميع حالات الاختبار الأخرى، وتشغيل هذه الحالة فقط، وبالتالي تحسين سرعة التشغيل. لاحقًا في قسم الاختبار، سأشرح العلامات المختلفة.

بعد التعديل، يمكننا تشغيل حالة الاختبار باستخدام أمر `prove`.

```
prove t/043-shdict.t
```

ثم، ستظهر لك رسالة خطأ تؤكد قيود المرحلة الموضحة في الوثائق.

```
nginx: [emerg] "init_by_lua" directive is not allowed here
```

### السؤال 2: متى يكون لدالة `get` أكثر من قيمة إرجاع؟

لننظر إلى السؤال الثاني، والذي يمكن تلخيصه من الوثائق الرسمية. تبدأ الوثائق بوصف `syntax` لهذه الواجهة.

```
value, flags = ngx.shared.DICT:get(key)
```

في الظروف العادية.

- المعلمة الأولى `value` تُرجع القيمة المقابلة للمفتاح `key` في القاموس؛ ومع ذلك، عندما لا يكون المفتاح موجودًا أو انتهت صلاحيته، تكون قيمة `value` هي `nil`.
- المعلمة الثانية، `flags`، أكثر تعقيدًا بعض الشيء؛ إذا كانت واجهة set قد حددت flags، فإنها تُرجعها. وإلا، فلا.

إذا حدث خطأ في استدعاء واجهة برمجة التطبيقات، فإن `value` تُرجع `nil`، و `flags` تُرجع رسالة خطأ محددة.

من المعلومات الموجزة في الوثائق، يمكننا أن نرى أن `local v = dogs:get("Jim")` مكتوبة بمعلمة استقبال واحدة فقط. هذا النوع من الكتابة غير مكتمل لأنه يغطي فقط سيناريو الاستخدام النموذجي دون استقبال معلمة ثانية أو إجراء معالجة الاستثناءات. يمكننا تعديلها إلى ما يلي.

```
local data, err = dogs:get("Jim")
if data == nil and err then
    ngx.say("get not ok: ", err)
    return
end
```

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

```
  === TEST 65: get nil key
     --- http_config
         lua_shared_dict dogs 1m;
     --- config
         location = /test {
             content_by_lua '
                 local dogs = ngx.shared.dogs
                 local ok, err = dogs:get(nil)
                 if not ok then
                     ngx.say("not ok: ", err)
                     return
                 end
                 ngx.say("ok")
             ';
         }
     --- request
     GET /test
     --- response_body
     not ok: nil key
     --- no_error_log
     [error]
```

في حالة الاختبار هذه، يكون إدخال واجهة `get` هو `nil`، ورسالة الخطأ المرتجعة هي `nil key`. هذا يؤكد أن تحليلنا للوثائق صحيح ويقدم إجابة جزئية للسؤال الثالث. على الأقل، لا يمكن أن يكون الإدخال لـ get هو nil.

### السؤال 3: ما هو نوع الإدخال لدالة `get`؟

أما بالنسبة للسؤال الثالث، ما هي أنواع معاملات الإدخال التي يمكن أن تكون لـ `get`؟ دعونا نتحقق من الوثائق أولاً، ولكن للأسف، ستجد أن الوثائق لا تحدد أنواع المفاتيح القانونية. ماذا نفعل؟

لا تقلق. على الأقل نعرف أن `key` يمكن أن يكون من نوع سلسلة ولا يمكن أن يكون nil. هل تتذكر أنواع البيانات في Lua؟ بالإضافة إلى السلاسل و nil، هناك الأرقام، المصفوفات، الأنواع المنطقية، والوظائف. النوعان الأخيران غير ضروريين كمفاتيح، لذا نحتاج فقط إلى التحقق من الأولين: الأرقام والمصفوفات. يمكننا البدء بالبحث في ملف الاختبار عن الحالات التي يتم فيها استخدام الأرقام كـ `key`.

```
=== TEST 4: number keys, string values
```

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

```
$ resty --shdict 'dogs 10m' -e 'local dogs = ngx.shared.dogs
 dogs:get({})
 '
```

كما هو متوقع، ظهر الخطأ التالي.

```
ERROR: (command line -e):2: bad argument #1 to 'get' (string expected, got table)
```

باختصار، يمكننا أن نستنتج أن أنواع `key` المقبولة من قبل واجهة برمجة التطبيقات `get` هي السلاسل والأرقام.

هل هناك حد لطول المفتاح الذي يتم تمريره؟ هناك حالة اختبار مقابلة هنا.

```
=== TEST 67: get a too-long key
     --- http_config
         lua_shared_dict dogs 1m;
     --- config
         location = /test {
             content_by_lua '
                 local dogs = ngx.shared.dogs
                 local ok, err = dogs:get(string.rep("a", 65536))
                 if not ok then
                     ngx.say("not ok: ", err)
                     return
                 end
                 ngx.say("ok")
             ';
         }
     --- request
     GET /test
     --- response_body
     not ok: key too long
     --- no_error_log
     [error]
```

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

## الخلاصة

أخيرًا، أود أن أذكرك أنه في واجهة برمجة التطبيقات لـ OpenResty، أي قيمة إرجاع تحتوي على رسالة خطأ يجب أن يكون لها متغير لاستقبالها وإجراء معالجة الأخطاء، وإلا ستحدث أخطاء. على سبيل المثال، إذا تم وضع اتصال خاطئ في مجموعة الاتصالات، أو إذا فشل استدعاء واجهة برمجة التطبيقات في متابعة المنطق الذي يليه، فإن ذلك سيسبب شكاوى مستمرة.

لذا، إذا واجهت مشكلة عند كتابة كود OpenResty، ما هي الطريقة المعتادة لحلها؟ هل هي الوثائق، قوائم البريد، أو قنوات أخرى؟

مرحبًا بمشاركة هذه المقالة مع زملائك وأصدقائك حتى نتمكن من التواصل والتحسين.

Share article link

Share

Related Posts

[![أسئلة وأجوبة حول OpenResty | كيفية استخدام OpenResty في الممارسة العملية](https://static.api7.ai/uploads/2023/02/12/jbv9M0zi_Hello%20World%20in%20OpenResty%20%283%29.png)](https://api7.ai/learning-center/openresty/faq-5)

## [أسئلة وأجوبة حول OpenResty | كيفية استخدام OpenResty في الممارسة العملية](https://api7.ai/learning-center/openresty/faq-5)

[![الجزء الثالث: كيفية بناء بوابة API للخدمات المصغرة باستخدام OpenResty](https://static.api7.ai/uploads/2023/02/07/3XJXY9Jy_45.png)](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

## [الجزء الثالث: كيفية بناء بوابة API للخدمات المصغرة باستخدام OpenResty](https://api7.ai/learning-center/openresty/how-to-build-a-microservices-api-gateway-part-3)

Table of Contents

- [لنأخذ واجهة برمجة التطبيقات `get` لـ shdict كمثال](#%D9%84%D9%86%D8%A3%D8%AE%D8%B0-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-get-%D9%84%D9%80-shdict-%D9%83%D9%85%D8%AB%D8%A7%D9%84)

  - [السؤال 1: أي مراحل لا يمكن استخدام واجهات برمجة التطبيقات المتعلقة بالذاكرة المشتركة؟](#%D8%A7%D9%84%D8%B3%D8%A4%D8%A7%D9%84-1-%D8%A3%D9%8A-%D9%85%D8%B1%D8%A7%D8%AD%D9%84-%D9%84%D8%A7-%D9%8A%D9%85%D9%83%D9%86-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D9%84%D9%82%D8%A9-%D8%A8%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%AA%D8%B1%D9%83%D8%A9)
  - [السؤال 2: متى يكون لدالة `get` أكثر من قيمة إرجاع؟](#%D8%A7%D9%84%D8%B3%D8%A4%D8%A7%D9%84-2-%D9%85%D8%AA%D9%89-%D9%8A%D9%83%D9%88%D9%86-%D9%84%D8%AF%D8%A7%D9%84%D8%A9-get-%D8%A3%D9%83%D8%AB%D8%B1-%D9%85%D9%86-%D9%82%D9%8A%D9%85%D8%A9-%D8%A5%D8%B1%D8%AC%D8%A7%D8%B9)
  - [السؤال 3: ما هو نوع الإدخال لدالة `get`؟](#%D8%A7%D9%84%D8%B3%D8%A4%D8%A7%D9%84-3-%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D9%88%D8%B9-%D8%A7%D9%84%D8%A5%D8%AF%D8%AE%D8%A7%D9%84-%D9%84%D8%AF%D8%A7%D9%84%D8%A9-get)

- [الخلاصة](#%D8%A7%D9%84%D8%AE%D9%84%D8%A7%D8%B5%D8%A9)
