طريقة كتابة اختبارات الـ JUnit في لغة الجافا


مستوى المقال: متوسط

بسم الله الرحمن الرحيم

السلام عليكم ورحمة الله وبركاته

هذه المقالة تمهيديه في شرح و كتابة اختبارات الـ Junit للغة الـ Java. والتي من خلالها اصبو الى كتابة المزيد من المقالات في مجال الاختبارات للجافا و للإندرويد في المستقبل ان شاء الله. لإني ارى ان هناك شح كبير جداً في هذا المجال و ان شاء الله اوفق ولو بسد جزء بسيط به, ولا ارى ضرر في كتابة الاخوان في هذا المجال لمن يريد, فالقارئ ربما يفهم من هذا المقال او من ذلك المقال.

 

ماذا سوف تقرئ في هذه المقالة

ماهي مكتبة الـ Junit وماهي متطلباتها؟ ماهي الطريقة التي تتم بها كتابة هذا النوع من الاختبارات؟ ماهي اهم الـ Annotations, و الـ Assertions المستخدمة في هذه المكتبة. مع امثلة برمجية وبعض الملاحظات لتقريب المفهوم. وسوف نرى كيفية انشاء مجلد خاص بهذه الاختبارات, وانشاء اول كلاس لكتابة الاختبارات به, ثم كيفية تشغيلها.

 

ماهو الـ JUnit

عبارة عن مكتبة لكتابة وتشغيل الاختبارات المتكرره بشكل سهل وسلس.

 

الموقع الرسمي

JUnit

 

المتطلبات

  • مكتبة الـ JUnit للإختبارات, الاصدار الرابع (مميزة هذا الاصدار انه مدعوم في الاندرويد كذلك, فجميع هذه الاكواد تعمل في الـ Android Studio بطلاقه).
  • معرفة كيفية استخدام الـ Annotations الخاص بالـ JUnit.

 

طريقة كتابة هذ النوع من الاختبارات
يقوم على كتابة دالات اختبار Unit Test في كلاس خاصة بها, ومن خلالهم تقوم باختبار دالاتك الحقيقه في مشروعك. وكل من هذه الدالات الاختبارية يأتي فوقها Annotation خاص بالـ JUnit تحدده انت حسب الغرض.

 

اهم الـ Annotations المستخدمة في اجراء اختبارات الـ JUnit
هذا جدول يوضح اهم الـ Annotations التي سوف تحتاجها في كتابة دالات اختباراتك. من الافضل حفظهم عن ظهر قلب.

testannotation.thumb.png.8876cfb6226400fc88d6bab135d54b87.png

 

مثال برمجي لتوضيح تسلسل عمل هذه الـ Annotations على دوال الاختبارات

package com.company;
import org.junit.*;

public class MainTest {

    @BeforeClass
    public static void preSetup(){
        System.out.println("preSetup() Runs!");
        System.out.println("-----------------");
    }

    @Before
    public void setup(){
        System.out.println("setup() Runs - Preparing Object");
    }

    @Test
    public void sum1_test(){
        System.out.println("sum1_test() Runs");
    }

    @Test
    public void sum2_test(){
        System.out.println("sum2_test() Runs");
    }

    @Ignore
    @Test
    public void sum3_test(){
        System.out.println("sum3_test() Runs");
    }

    @After
    public void clean(){
        System.out.println("clean() Runs = Cleaning test environment");
    }

    @AfterClass
    public static void postClean(){
        System.out.println("-----------------");
        System.out.println("postClean() Runs!");
    }
}

 

الناتج

preSetup() Runs!
-----------------
setup() Runs - Preparing Object
sum1_test() Runs
clean() Runs = Cleaning test environment

setup() Runs - Preparing Object
sum2_test() Runs
clean() Runs = Cleaning test environment

Test ignored.
-----------------
postClean() Runs!

 

ملاحظات

  • الدالتين setup و clean تم تشغيلهم اكثر من مره, لكل دالة اختبار, وهذا يعود لتطبيق النوتيشن After و Before اعلاهم.
  • الدالتين preSetup و postClean تم تشغيلهم فقط مره واحده, لهذه الكلاس, وهذا يعود لتطبيق النوتيشن BeforeClass و AfterClass اعلاهم ويجب عندها ان يكونان static.
  • دالة الاختبار المسمية بالـ sum3_test قد تم تجاهلها, وهذا يعود الى استخدام النوتيشن Ignore اعلاها.
  • اما دوال الاختبار لدينا المسماه sum1_test و sum2_test فقد تم تشغيلهم بسلاسة ونجاح.

 

انشاء بيئة الاختبارات الخاصة بالـ JUnit على مشروع جافا

الان لنقم بإنشاء بيئة الاختبارات الخاصة بنا, والتي من خلالها سوف نقوم باختبار مشروعنا التجريبي (عباره عن حاسبة اطفال بلغة الجافا). هذه الطريقة تمكنك من انشاء بيئة الاختبار يدوياً Best Practice.

  1. قم بتشغيل برنامجك المفضل للجافا (في هذا الشرح IntelliJ IDEA).
  2. انشئ مشروع جافا جديد 1.8 ولاتنسى اختيار الـ Command Line App او اختر مشروع سابق.
  3. انشئ مجلد جديد في مسار مشروعك وسمه مثلاً test.
  4. علم هذا المجلد بانه خاص للإختبارات, حتى يفهم الـ IntelliJ IDEA.
  5. اذهب الى اسم كلاسك الرئيسية واضغظ عليها بالزر الايمن للماوس ومن القائمة اختر Go To ثم Test.
  6. اضغظ على Create New Test... في النافذة المنبثقة.
  7. ستظهر لك نافذة جديده للإختبارات, اختر مكتبة الـ JUnit 4 المتطابقة مع هذا الشرح.
  8. انتباه: اذا رأيت الرسالة JUnit4 library not found in the module اضغظ على Fix.
  9. ثم استخدم المكتبة الموجودة بمسار الـ IntelliJ او قم بتحميلها وتحديد مسار اخر لها.

 

الخطوات بالصور حتى يسهل الأمر

  • انشاء مجلد الاختبارات

create-test-folder.png.33464bd6da5de1515debe48029ef52ab.png

 

  • تعليم هذا المجلد وجعله خاص للإختبارات

make-folder-testable.thumb.png.cadc233cd78b716a8f0d3b42d2001152.png

 

  • انشاء اول اختبار للكلاس الرئيسية

create-test-for-class.png.94d213b2fd8f3563a2834567a961553e.png

 

  • ثم انشاء اختبار جديد

create-test.png.6fb56d8250bc83d66d68332cad907944.png

 

  • تعديل الاعدادات واختيار مكتبة الاختبارات كأول مره فقط (لاتنسى اصلاح خلل ايجاد مسار المكتبة)

create-junit-test.png.6629ffbdaca402e7ba7e861141f04263.png

 

  • اصلاح Fix خلل ايجاد مسار مكتبة JUnit على جهازك

add-junit.png.d036781d79b2a221002d50490a8ceb85.png

 

  • النتيجة, كلاس جاهزة انشئت لكتابة الاختبارات في المجلد التي قمت بتخصيصة

ready-to-right-tests.png.5857a173dd88d8aeeb3d337d4ac594ae.png

 

الـ Assertions في الـ JUnit

يأتي مع الـ JUnit دوال تسمى Assertions من خلالهم تستطيع اجراء الاختبارات, ويجب عليك حفظ وفهم هذه الدوال, كما هو الحال مع الـ Annotations. يمكنك الاطلاع عليهم من خلال هذا الرابط.

اهم هذه الدوال هي:

assertions.png.a1eb6ae7ff01330a742dd0fcf491c5da.png

 

شرح المشروع

قبل كتابة الاختبارات لنفهم هذا المشروع قليلاً. فأسم المشروع هو KidCalculator. وتستطيع ايجاد نسخة منه على رابط الجيت هوب لدي.

يتكون من عدة كلاسات (حالياً) وهي:

  • Main.java وهي الكلاس الرئيسية للمشروع.
  • MainTest.Java وهي كلاس الاختبارات التي انشئناها بالخطوات السابقة.
  • Calculator.Java وهي عبارة عن Model لكلاس الحاسبة لدينا, تحتوي على دوال الحاسبة كالجمع والطرح والقسمة والضرب الخ... والتي من خلالها سيبنى المشروع.
  • BigNumberException كلاس exception خاص بالمشروع, حتى نستخدمها لاحقاً.

سيكون عملنا فقط كتابة اكواد داخل كلاس الـ MainTest.Java بما ان الاختبارات هي الهدف من هذه المقالة.

 

كتابة الاختبارات

  • اولاً لنلقي نظرة على كلاس الـ Calculator حتى نفهم هذه الموديل
package com.company;

public class Calculator {
    private String mColor;
    private boolean mPower;

    public Calculator(String color) {
        mColor = color;
    }

    public String getColor() {
        return mColor;
    }

    public void setColor(String color) {
        mColor = color;
    }

    public boolean checkPower() {
        return mPower;
    }

    public void setOn() {
        mPower = true;
    }

    public void setOff() {
        mPower= false;
    }


    public int addition(int firstNumber, int secondNumber) {
        return firstNumber + secondNumber;
    }

    public int subtraction(int firstNumber, int secondNumber) {
        return firstNumber - secondNumber;
    }

    public int multiplication(int firstNumber, int secondNumber) {
        return firstNumber * secondNumber;
    }

    public int division(int firstNumber, int secondNumber) {
        return firstNumber / secondNumber;
    }
}

 

  • والان لنقم بكتابة الاختبارات لهذه الموديل لإختبار المنطق في اجراء العمليات الحسابية حسب ماتم برمجته في الكود السابق.
package com.company;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

public class MainTest {

    private Calculator calculator;

    @Before
    public void setUp() {
        calculator = new Calculator("Red");
    }

    @Test
    public void addition_test() {
        int result = calculator.addition(2, 2);
        assertEquals(result, 4);
    }

    @Test
    public void subtraction_test() {
        int result = calculator.subtraction(10, 4);
        assertEquals(result, 6);
    }

    @Test
    public void multiplication_test() {
        int result = calculator.multiplication(5, 5);
        assertEquals(result, 25);
    }

    @Test
    public void division_test() {
        int result = calculator.division(10, 2);
        assertEquals(result, 5);
    }

}

 

ملاحظات:

  • استخدمنا هنا assertEquals وهي عباره عن دالة JUnit تاخد قيمة متوقعة (result) ثم تقارنها بالقيمة الحقيقة اي الصحيحة.
  • قمنا بأنشاء عنصر من موديل حاسبتنا Calculator في دالة الـ setUp والتي تعمل قبل كل الدوال حتى تنشئ عنصرنا ونتمكن من استخدامه في باقي دوال الاختبارات.

وتشغيل هذه الاختبار من خلال الضغظ بالزر الايمن على اسم الكلاس واختيار Run 'MainTest' او من خلال الايقونة الصغيره الخضراء كشكل السهم المستدير بجانب اسم الكلاس, كما بالصورة التالية:

how-to-run-tests.png.7c7f77f495f006c2e57549b9ead1e60b.png

 

نتيجة الاختبار

all-tests-passed.png.56cefb7d900b1162822f94381cbea635.png

نجاح جميع الاختبارات كما هو متوقع.

ملاحظة: لاتعير اهتمام للسطر الاحمر فهو خلل برمجي في الجافا, يحدث للماك وهذه اقتباس من احد العاملين بشركة اوراكل:

اقتباس

The message is benign, there is no negative impact from this problem since both copies of that class are identical (compiled from the exact same source). It is purely a cosmetic issue.

 

المزيد من الاختبارات

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

first-fail.thumb.png.79891f12197f480fb08f7bca3809cd77.png

 

ملاحظات:

  • قمنا بكتابة و تشغيل ٦ اختبارات, ٥ نجحو, وواحد فشل.
  • المهم هو ان الدالة check_color_and_change_it_test قد نجحت في تغيير اللون, ونجحت ايضاً في التأكيد من ان اللون قد تغير الى الازرق.
  • اما الدالة check_color_is_still_changed قد فشلت في التأكيد من ان اللون قد اصبح ازرق؟ لماذا؟ اليست الدالة التي قبلها قد قامت بتغيير هذا اللون؟

 

اقتباس

هذه النقطة هي من اكثر الامور تشتيت في الـ JUnit. وهي ان كل دالة معلمة بالنوتيشن Test تعتبر منفصلة عن اخواتها, هي فقط تتشارك مع الدوال كـ setUp المعلمة بالـ Before وغيرها.

هل تتذكر ان دالة الـ setUp و دالة الـ clean يتكرر تشغيلهم لكل دالة ذي نوتيشن Test في المثال الاول؟

اذن مع تشغيل هذه الدوال الاختبارية الـ ٦, نستطيع القول كاننا قمنا بتشغيل ٦ كلاسات منفصلة عن بعضها البعض!

اتمنى ان فهم هذه النقطة وصل, لان هذا الشئ يخلق مشاكل كثيرة في كتابة الاختبارات في المستقبل.


وهكذا قمنا بأنشاء مجلد اختبارات + انشاء كلاس اختبارات + كتابة ٦ دوال اختبارات باستخدام بعض من الـ Annotations و Assert واحده وهي assertEquals. واتوقف هنا حتى لايصبح للقارئ overwhelming و burnout, واكمل في المقالات اللاحقة ان شاء الله.

mzdhr

0


اراء المستخدمين


لاتوجد تعليقات لعرضها .



انشئ حساب جديد او قم بتسجيل دخولك لتتمكن من اضافه تعليق جديد

يجب ان تكون عضوا لدينا لتتمكن من التعليق

انشئ حساب جديد

سجل حسابك الجديد لدينا في الموقع بمنتهي السهولة .


سجل حساب جديد

تسجيل الدخول

هل تمتلك حساب بالفعل ؟ سجل دخولك من هنا.


سجل دخولك الان

Ads Belongs To mzdhr

  • ماهي قاعدة بيانات الـMongoDB؟
    في السابق كان الحديث حول قواعدة البيانات المعروفه و المتوارد بين اقطاب المبرمجين من مختلف الأصعدة ابتدا من web developers إلى الـsoftware developers على مختلف المنصات windows, OSX, Linux و جل الحديث كان يدور حول نوع واحد فقط وهو RDBMS Relational database management system مثل  MS SQL Server, IBM DB2, Oracle, MySQL, and Microsoft Access  التي تعتمد في حفظ البيانات داخل جداول ولكن في الآونة الأخيرة و بعد صدور عدد من JS Frameworks  و ارتفاع عدد مستخدمي لغة الجافاسكربت بدأت شركة 10GEN في العمل على قاعدة بيانات من نوع مختلف تماما عما سبق ذكرة وهي NoSQL تحت اسم MongoDB اي قاعدة بيانات لا توجد بها علاقة بين الجداول وتعتمد في حفظ البيانات كمستندات BOSN ويعد هذا النوع الأشهر بين عائلة الـNoSQL وهنا ننوه بأن جميع ما تعرفه عن الـSQL Databases قد يتغير فبالتاكيد لن يكون هناك كوريز بالطريقة التي اعتدت العمل عليها.
     
    1- أمثله
    وبما ان الكثير من المبرمجين لديه خلفية في الـMySQL سأقوم بمقارنة سريعة حول الأختلافات السطحية بين المونجو والـMySQL:
    اسم الـTable في الـMongoDB هو Collection اسم الـRow في الـMongoDB هو Document تعتمد قواعد بيانات الـRDBMS على الجداول كالنحو التالي:
     

     
    بينما قاعدة بيانات الـMongoDB تعتمد على BSON وهذا يعني Binary Encoding Of JSON Objects وتكون كالنحو التالي:
    { "_id" : ObjectId("57fe832b13e6a51130a23d2a"), "ID" : 1, "username" : "abdulla", "email" : "[email protected]" } { "_id" : ObjectId("57fe832b13e6a51130a23d2b"), "ID" : 2, "username" : "omar", "email" : "[email protected]" } { "_id" : ObjectId("57fe832b13e6a51130a23d2c"), "ID" : 3, "username" : "mohammed", "email" : "[email protected]" }  
    امثلة حول الفروقات في عمليات الـQueries مابين الـMongoDB و الـMySQL OR RDBMS ولنفترض بأن لدينا جدول و كولكشن يحتويان على عدد من الصفوف والدوكيومنتس فيهم معلومات حول مستخدمين
    عرض جميع المستخدمين من هم دون السن الـ24 SELECT * FROM users WHERE age < 24 db.users.find({"age": { $lt: 24 }}) // $lt = less than  
    عرض جميع المستخدمين الذين  اعمارهم مابين الـ20 والـ30 SELECT * FROM `users` WHERE `age` BETWEEN 20 AND 30 db.users.find({ age: { $gt: 20, $lt: 30 }}) // $gt = greater than  
    عرض المستخدمين الذين اسمائهم تحتوي على حرف A في الاول او الاخير او المنتصف لايهم SELECT * FROM `users` WHERE username LIKE %A% db.users.find({username: /A/})  
    و في حالة الإضافة INSERT INTO `users` (username,age,status) VALUES ('abdulla',25,'active') db.users.insertOne({username: "abdulla", age: 25, status: "active"})  
    ولكن هناك فارق كبير في هذه الكويري و بما ان قاعدة بيانات الـMongoDB ليست Relational بالتالي ليس هناك Schema محدده عند الإضافه فتستطيع مثلا إضافة document بمتغير col,field ليس موجود في الـdocument السابق او التالي في نفس الـCollection وبهذا يقال للـMongoDB بأن اهم مميزات هذا النوع من قواعد البيانات بأنه Flexible او Dynamic Schema  ومثال على ذلك:
    db.users.insert([ { username: { fname: "abdulla", lname: "bahraini" }, age: 25, status: "active" }, { username: "mohammed", age: 19, status: "not active" } ])  
    2- لماذا التحول الى MongoDB ( وجهة نظر )

     
    كما اسلفنا سابقا ومع صدور عدد من js frameworks وبالاخص Meanjs المعني ب expressjs , angularjs & nodejs بالاضافه الmongodb و توسع هذه الفريموركس بشكل ضخم و رهيب جدا ومنها النود التي اصبحت خطر داهم على لغة الphp مقارنة بالسرعة, الاداء الرهيب وتناغم المونجو مع هذه الفريموركس بشكل افضل سبب في توجه الكثير من المطورين الى المونجو.
     
    ليس عند هذا الحد وحسب بل نظرا الى سرعة المونجو حسب الاحصائيات تبين اهمه الانتقال من قواعد البيانات الاعتيادية الى المونجو.
    تجربة اضافه 10000 صف و دوكيومنت في MySQL و MongoDB .. نلاحظ الفرق في سرعة الاداء احتاج المونجو الى ثانتين لتنفيذ المهمه بينما الMySQL احتاج الى 3 دقائق تقريبا.
     


     
    3-التثبيت
    لتثبيت الـMongoDB قم بالدخول على الرابط واختر ما يناسب نظام جهازك
     
    بعد التثبيت إن كنت  من متسخدمي الوندوز انشأ مجلد في داخل الـC (في حال تم تثبيت المونجو في السي)
     
    أضف المتغير التالي في PATH Environment وتجده في المسار التالي
    this pc >> proprieties >> Advanced System Settings >>  Advanced tab >> Environment Variables >> in System Variables find PATH then Click Edit
     
    افتح الـCMD واكتب الكوماند التالي لتفعيل خدمه الـMongoDB في جهازك وستعمل على بورت 27017 بشكل افتراضي
     
    ثم افتح CMD أخر واكتب الكوماند التالي للدخول على MongoDB SHELL
     

     
    بالتالي ستكون داخل الـMongoDB SHELL وتستطيع التعامل معه من خلال الاوامر المباشرة وللإطلاع اكثر اكتب help
     
    مستوى المقال: مبتدئ

    بواسطه AbdullaScript , في

  • المستوى: مُبتدأ/متوسّط
     
    يُمكن الاستفادة من مكتبة Multer في nodejs لرفع الصور والملفّات إلى السيرفر، فبأسطر برمجية بسيطة يُمكن رفع الملفات التي قام المُستخدم بتحديدها
    let app = require("express")() let multer = require("multer") let upload = multer({ dest: 'uploads/' }).any() app.get("/", (req,res)=>{ res.send("Teting...") }) app.post("/upload", (req, res)=>{ upload(req,res, (err)=>{ if (err) throw err }) res.send(req.files) }) app.listen(3000, ()=>{ console.log("Let's Upload....") }) الكود السابق يقوم باستلام الملفّات التي يُرسلها المستخدم إلى المسار /upload ليقوم برفعها ثم طباعة اسم الملف المُستلم. لكن وبعد تنفيذه قد تتفاجئ بأن عملية الرفع تجري بنجاح، إلا أن الاسم لا يظهر ويتم إرسال مصفوفة فارغة على الرغم من أن عملية الرفع تمّت دون مشاكل.
    السبب في ذلك هو طبيعة Nodejs، والتي سبق وأن تحدّثت عنها في مقال سابق بعنوان "تنفيذ الطلبات بتسلسل وترتيب في Nodejs باستخدام Async/Await". انصحك بقرائته لفهم آلية عمل إطار العمل بشكل كامل.
    ما يحدث على أرض الواقع هو أن النظام يبدأ بالرفع، ولأن تنفيذ المهام في nodejs يتم بشكل متوازي لتجنّب تأخير الرد على طلب المستخدم، سيقوم النظام فورًا بطباعة محتويات مصفوفة الملفّات req.files، التي ستكون فارغة بالفعل لأن النظام لم ينتهي بالأصل من عملية الرفع. والحل في هذه الحالة هو استخدام async/await ليُصبح الكود من الشكل:
    let app = require("express")() let multer = require("multer") let upload = multer({ dest: 'uploads/' }).any() app.get("/", (req,res)=>{ res.send("Testing Routes.....") }) let uploadPromise = (req, res)=>{ return new Promise((Resolve, Reject)=>{ upload(req,res, (err)=>{ if (err) Reject(err) Resolve() }) }) } app.post("/upload", async(req, res)=>{ try{ await uploadPromise(req, res) res.send(req.files) }catch(uploadErr){ throw uploadErr } upload(req,res, (err)=>{ if (err) throw err }) }) app.listen(3000, ()=>{ console.log("Let's Upload....") }) حاول تنفيذ الكود السابق وسترى أن اسم الملف سيظهر دون مشاكل لأن النظام قام أولًا برفع الملف ثم قام بطباعة الاسم، وهذا يعني أن المصفوفة لم تعد فارغة بشكل كامل. الفيديو التالي يشرح الفرق بشكل عملي:
    أما الراغبين بتحديد لاحقة الملف أو قبول نوع مُحدّد من الملفات فهم بحاجة لتعديل بعض جزئيات الكود، ولتجنّب تعقيد الأفكار ما بين Async/Await، سأقوم باستخدا مثال بسيط عن Multer.
    let app = require("express")() let multer = require("multer") let upload = multer({dest: "uploads"}) app.post("/", upload.single("photos"), (req,res)=>{ res.send("OK...") }) app.listen(3000, ()=>{ console.log("Ready to upload...") }) الكود السابق يشرح طريقة رفع ملف إلى مُجلّد uploads باستخدام multer، فعند طلب الرابط domain.com/ باستخدام POST فإن النظام سيأخذ الملف الموجود في الحقل photos وسيقوم برفعه. لكن ماذا لو أردنا تغيير الاسم أو اختيار اللاحقة؟ الأمر يتم من خلال ميثود موجودة داخل multer تُعرف بـ diskStorage ليُصبح الكود من الشكل
    let app = require("express")() let multer = require("multer") let storage = multer.diskStorage({ destination: function(req, file, cb){ cb(null, "uploads") }, filename: function(req, file, cb){ let ext = file.mimetype.split("/") ext = ext[ext.length-1] cb(null, file.originalname + "-"+Date.now()+"."+ext) } }) let upload = multer({storage:storage}) app.get("/", (req, res)=>{ console.log(Date.now()) }) app.post("/", upload.single("photos"), (req,res)=>{ res.send("OK...") }) app.listen(3000, ()=>{ console.log("Ready to upload...") }) قُمنا في الميثود بتعريف خاصيّتين الأولى هي destination الخاصّة بتحديد مكان الرفع، والثانية هي اسم الملف. الشرط الوحيد في الخصائص داخل multer هو استخدام دالة cb، وهي اختصار لـ Call Back لتمرير النتيجة التي نرغب بها.
    في مثالي في الأعلى قُمت بفحص نوع الملف باستخدام الـ mimeType الخاص به، فلو كان صورة سيظهر بالشكل image/jpeg أو image/png وهكذا. بعدها قمت بإضافة تلك اللاحقة للاسم الذي يُمكنك تسميته كيفما تشاء. أما بخصوص رفع نوع واحد من الملفات دونًا عن الغير فهذا مُمكن عبر تعديل السطر الخاص بتعريف upload ليُصبح من الشكل 
    let upload = multer({storage:storage, fileFilter: function(req, file, cb){ if (file.mimetype != "image/jpeg"){ return cb(null, false) } cb(null, true) }}) قُمنا بإضافة ما يُعرف بالـ fileFilter، وهي خاصيّة موجودة في multer قُمنا فيها بفحص نوع الملف مرّة أُخرى عبر mimeType وفي حالة عدم مُطابقة الشروط نقوم برفض الملف من خلال استخدام إعادة الـ callback فارغ مع false. بهذه الحالة سيقفز النظام عن الملف وسيتجاوزه.
    الفيديو التالي يشرح كل شيء
     
    في حالة وجود أي استفسار يُمكن تركه في التعليقات أو مُراسلتي عبر حسابي في تويتر @FerasAllaou
    مستوى المقال: مبتدئ

    بواسطه feras , في

  • السلام عليكم ورحمة الله وبركاته 
    طرحنا مسابقة عالم البرمجة للنقاش الهادف ، و هدفنا إثراء المحتوى العربي في مجال البرمجة ، فاز معنا الطرح المتميز و المفيد حيث قام بتقييم المقالات وترقيتها فريق عالم البرمجة سنذكر بهذه المقالة الفائزين معنا ، و مقالاتهم ، و نشكرهم على ماقدموا من فائدة للجميع ، و إثراء المحتوى العربي في البرمجة.
     
    الفائزون في شهر January 2018
    المقال الفائز:
    المقال الفائز:
    المقال الفائز:
    المقال الفائز:
    المقال الفائز:
    المقال الفائز:
    المقال الفائز:
    المقال الفائز:
    المقال الفائز:
    المقال الفائز:
     
     
    في الختام:
    هدفنا في موقع عالم البرمجة إثراء المحتوى العربي في مجال البرمجة ، و تعزيز حب المساعدة بين المبرمجين تستطيع كتابة ماتحب في ساحات النقاش ، و يمكن تكون احد الفائزين معنا  بمسابقة عالم البرمجة للنقاش الهادف فهي مازالت مستمره ايضا تستطيع مساعدة المبرمجين بالإجابة عن أسئلتهم ، و حل المشاكل التي تواجههم بقسم سؤال وجواب ؛ لتكون مرجع لبقية المبرمجين شعارنا في عالم البرمجة "إن في قضاء حوائج الناس لذة لا يَعرفها إلا من جربها، فافعل الخير مهما استصغرته فإنك لا تدري أي حسنة تدخلك الجنة." -ابن القيم-  وممكن تستفيد من
    حبيبي القارئ اعلم ان فريق عالم البرمجة يصب كل جهودة لمساعدة المبرمجين الذين يخصصون من وقتهم لنشر العلم المفيد ويساعدون الغير ويارب يقدرنا نوقف معكم ونساعدكم قد مانقدر.
    مستوى المقال: مبتدئ

    بواسطه Ali Majrashi , في

  • بسم الله الرحمن الرحيم
    السلام عليكم ورحمة الله، كيف حال الجميع؟ عساكم بألف خير و عافية.
    سأحاول باذن الله البدأ في سلسلة جديدة حول تعلم أساسيات رياكت (React) و (RN = React Native) كما تعلمون هي مكتبة عرض (View) أطلقتها شركة الفيسبوك لبناء واجهاتك البرمجية (User Interfaces)  بطريقة سلسة و سهلة مبنية على نظام (Virtual DOM). و أول نسخة رسمية منها بتاريخ مارس 2013. و لما نقول أنها مكتبة عرض ليست اطار عمل (MVC) ركّز هنا جيّداً أخي القارئ؛ حيث تمثل هي حرف (V) فقط لا تقدم خدمة المتحكم (Controller) و لا (Model).
    بداية تعلمك React يصادفك شئ جديد عند كتابة أول مكوّن (Component's) حيث أنها تعود (returning) بصيغة تشبه صيغة HTML! ربما لم تلاحظها في مكتبات أو أطر عمل أخرى. تسمى بالـ JSX
    فما هي يا ترى JSX ؟ 
    JSX عبارة عن صيغة (syntax) أقول أُخترعت لمكتبة React التي تبدو مشابهة جداً لـ xHTML التي تمكنك من انشاء عناصر واجهتك البرمجية (Create Elements) بسهولة و يسر عن طريقها بدلاً من استعمال وضائف (functions) تستدعيها يدوياً لتنفذ مهمة معيّنة. ربما هذه نقطة مبهمة للبعض لكن ستفهمها بعد قليل باذن الله.
    من المهم أن تعلم أن جميع مكونات React المبنية بصيغة JSX تترجم من شفرة لشفرة (transpiler) الى تعليمات جافاسكربت حقيقية (Real JavaScript) باستخدام مكتبة تدعى بابل (Babel) تقوم بتحويل الرياكت الى شفرة ES5 القديمة لتشتغل على جميع المتصفحات (حتى القديمة منها).
    لنكتب مثالا بسيطاً بالـ JSX : 
    return <span> أهلا بعالم البرمجة! </span>; هل لاحظت سهولة كتابة جملة، و ارجاعها للظهور في المصتفح بكل سهولة مرفقة بكلمة (Return). تخيّل لو كتبناها بصيغة ES5 القديمة؟! 
    return React.createElement( 'span', {}, 'أهلا بعالم البرمجة!'); كم سطراً كتبناه لنعيد الجملة للظهور باستخدام جافاسكربت طبيعية! في هذا المثال استعملنا وظيفة (function) تسمى بـ React.createElement صيغتها كالتالي: 
    React.createElement( string|element, [propsObject], [children...]) ماذا لو أردنا كتابة عدة جمل متفرقة بداخل الشفرة البرمجية! ستكون هكذا بلا شك!
    React.createElement('div', {}, React.createElement('div', {}, 'text1'), React.createElement('div', {}, 'text2', ) ); لكن لسهولة JSX و هذا ما تميّزت به شركة الفيسبوك صراحة في مكتباتها البرمجية، الشفرة السابقة مثلاً باستخدام JSX : 
    function text1() { return <span>text1</span>; } function text2() { return <span>text2</span>; } هنا نستنتج مرة أخرى، أن JSX رائعة جدا و اختزال (shorthand) لكثرة الشفرات البرمجية التي تؤدي نفس الوظيفة. مما يكلفك وقتا اضافيا و كثرة المشاكل (bug's) عند تنقيح برمجياتك و تطبيقاتك؛ هذا كود شامل و أوسع، تلاحظ أنه يمكنك من تقسيم واجهاتك البرمجية عبر مكونات مختلفة و التحكم بها كلٌ على حِدة.
    من الثلاث خصائص المتوفرة في JSX هي : 
    العناصر المتداخلة (Nested elements): حيث يمكنك الجمع بين عدة عناصر (Multiple elements)  في حاوية واحدة (container element) كـ <div> مثل المثال الذي بالأسفل.
    السمات (Attributes): داخل كل عصنر div لك أن تضيف وظائف متعددة كـ (className، selected، style) ..الخ. 
    تعبيرات الجافاسكربت (JavaScript expressions): يمكنك اضافة تعابير (ليست شرطية statements)  داخل JSX كعمليات الحساب و المهم أن تكون ضمن قوسين {} .
    import React from 'react'; class App extends React.Component { render() { return ( <div> <p>Header</p> <p>Content</p> <p>Footer</p> </div> <div> <h1>{10+1}</h1> </div> <div style={{ height: 10 }}> Hello World! </div> ); } } export default App; في التعبيرات ذكرنا أنه لا يمكن استخدام التعابير الشرطية كـ if و else باستثناء التعبير الشرطي الثلاثي: { i === 1 ? 'true' : 'false' }  مثال : 
    import React from 'react'; class App extends React.Component { render() { const i = 1; return ( <div> <h1>{ i === 1 ? 'true' : 'false' }</h1> </div> ); } } export default App; من النصائح التي أقدمها و لا يجب عليك نسيانها: جميع أسماء المكونات (components) تكتب بحروف صغيرة (lowercase) التي تكون مدمجة بـعناصر HTML أو SVG كـ (div, ul, rect, etc.) و لا تنسى اغلاق العناصر بأوسمتها (Close Every Element) مثال مبسط: 
    // DO THIS: return <br/>; return <input type='password' .../>; return <li>text</li>; // NOT THIS: return <br>; return <input type='password' ...>; return <li>text; هذه أهم أساسيات JSX التي يجب على كل شخص معرفتها لمحبي تعلم ReactJS و RN عموماً. أتمنى تكون مفهومة للجميع. 
    و السلام عليكم.
    مستوى المقال: مبتدئ

    بواسطه عبدالهادي , في

  • بسم الله الرحمن الرحيم
    السلام عليكم ورحمة الله وبركاته
    تعرفنا في بداية سلسلة مواضيعنا على طريقة من طرق تخزين البيانات وهي القوائم سواء الأحادية أو الثنائية، كما ونعلم بأن هناك
    طريقة أخرى احفظ مجموعة البيانات وهي أبسط طريقة والتي تتمثل في المصفوفات سواء المصفوفات العادية أو مصفوفات القوائم.
    في هذا الدرس سنتعرف على أحد أفضل الطرق في حفظ البيانات والتي تعتبر من الأفضل من ناحية العرض أو البحث وغيرها
    من الأمور المتعلقة بهذه المجموعة من البيانات. كما هو الحال في القوائم فإن الأشجار لديها أكثر من نوع وكل واحد منهم له خواصة واستخداماته.
    في هذا الدرس سنتعرف على أحد أنواع الأشجار وهو الأشجار الثنائية.
     
    ماذا نقصد بالأشجار الثنائية (Binary Trees):
    هو نوع من أنواع تمثيل البيانات ويتكون من مكونين أساسيين هما العقد (Nodes) و الأقصان (Arcs). 
    عندما نقوم بتمثيل هذه الشجرة فإننا نقلب مفهومها فيكون الجذر (Root) في الأعلى والأقصان في الأسفل.
    سميت بالأشجار الثنائية لأنه كل عقد لديه عقيدين يرتبط فيهم وهما أبنائه ولديه والد واحد.
    هناك بعض المصطلحات التي يجب أن نتعرف عليها قبل الشروع الى صلب الموضوع:
    - العقد (Node): وهو كل عنصر موجود في الشجرة، وهو المكان الذي يتم حفظ البيانات داخله.
    أنواع خاصة من العقد (Nodes):
                   - الجذر (Root): وهو العنصر الذي ليس لديه والد أي أنه أعلى عنصر في الشجرة. وكل شجرة تحتوي على جذر واحد فقط.
                   - الأوراق (Leaves): وهذه هي مجموعة العقد التي ليس لديها أي أولاد. أي أنها في طرف الشجرة.
    - القصن (Arc): هو الرابط الذي يربط العقد بين بعضها وهو ذي اتجاه واحد من الأعلى الى الأسفل.

     
    يختلف مفهوم الشجرة الثنائية Binary Tree  حيث أن بعضها لا تحقق بعض الشروط مما يؤدي بها الى أن تكون عبارة عن قائمة عادية
    لكن الأنواع الأخرى تحتوي على شروط التي تجعلها شجرة فعالة بأكبر قدر ممكن و تسمى بـ Binary Search Tree وهذا اساس جميع ما يلي.
     
    تمثيل الشجرة الثنائية (Binayr Tree):
    ١. عن طريق المصفوفات (ArrayLists): وهذه الطريقة لديها مشاكلها كما ذكراناها سابقاً.
    ٢. عن طريق القوائم الثنائية (Linked lists): وهذه هي الطريق الأمثل لتمثيل الشجر.
     
    أكواد الشجرة الثنائية (Binary Tree):
    أ- إنشاء كلاس خاص بالعقد (Nodes) :
    هذا الكلاس يسمى بـ BSTNode وهو كالتالي:
    public class BSTNode { protected int key; protected BSTNode right, left; public BSTNode(){ this(0); } public BSTNode(int k){ this(k, null, null); } public BSTNode(int k, BSTNode r, BSTNode l){ this.key = k; this.right = r; this.left = l; } public void addRight(BSTNode r){ this.right = r; } public void addLeft(BSTNode l){ this.left = l; } } هذا الكلاس يحتوي على المتغيرات التالية:
    - Key: وهو من نوع و int وهو المكان الذي يتم حفظ البيانات داخله.
     - right & left: وهما القصنان (Arcs) والذان يربطان العقد بأبنيه اليمين والشمال.
    كما نرى فإننا نستطيع إنشاء عنصر من هذا الكلاس سواء كان في بيانات أو لا. لديه أولاد أولا.
    كما وأنه لدينا ميثودين نستخدمها لإضافة ابن لليمين أو لليسار.
     
    ب- انشاء كلاس الشجرة الثنائية (Binary Search Tree):
    public class BST { private BSTNode root = null; public BST(){ } public void clear(){ this.root =null; } public boolean isEmpty(){ return root == null; } public void visit(BSTNode n){ System.out.println(n.key + " "); } } هنا نقوم بانشاء الهيكلة الأساسية للشجرة ويحتوي على التالي:
    - root: الجذر وهو رأس الشجرة والوحيد الذي يربطنا بكامل عناصر الشكرة. وفي بداية انشاء الشجرة يكون الجذر فاضي.
    لدينا الكثير من الميثود لكن سنذكر هنا الأشياء الأساسية ونترك الباقي للدرس القادم بحيث أنها تحتوي على الكثير من التفاصيل:
    * clear(): تستخدم لتفريغ الشجرة من البيانات؛ وبما أن الجذر هو الرابط الوحيد لعناصر الشجرة فإن حذف الجذر سيؤدي الى حذف الشجرة كلها.
    *isEmpty(): وستخدم للاستعمال اذا ما كانت الشجرة خالية أو لا. ترسل true في حال كانت الشجرة خالية.
    *visit(): تستخدم لطباعة قيمة القيمة داخل العقد المرسل.
     
    خلال الشرح القادم إن شاء الله سنتطرق لاضافة وحذف عناصر من الشجرة، كذلك كيف نقوم بطباعة جميع عناصر الشجرة.
     
    اسأل الله العلي العظيم بأني وفقت لاصال المعلومة بابسط طريقة
     
    في حفظ الله
    مستوى المقال: متوسط

    بواسطه Abather , في

  • Ads Belongs To mzdhr

    عالم البرمجة

    عالم البرمجة مقالات برمجة و دورات مجانية لإحتراف البرمجة هدفنا تبسيط البرمجة ونشرها بيد الكل بشكل ممتع ومتطور ومحدث بإستمرار لمواكبة جديد تطورات البرمجة الحديثة و المتقدمة بدون مقابل