مرحبا يا هبر! أقدم إليكم ترجمة المقال
"أفضل 10 أخطاء ربيعية شائعة
" بقلم توني كوكورين.
الربيع هو على الأرجح أحد أطر عمل Java الأكثر شعبية ، بالإضافة إلى أنه وحش قوي يمكن ترويضه. على الرغم من أن مفاهيمها الأساسية سهلة الفهم إلى حد ما ، إلا أن الأمر يتطلب بعض الوقت والجهد لتصبح مطور ربيع قوي.
في هذه المقالة ، سننظر في بعض الأخطاء الأكثر شيوعًا في Spring ، وخاصة تلك المتعلقة بتطبيقات الويب و Spring Boot. كما هو مذكور في
موقع Spring Boot ، يفرض فكرة عن كيفية إنشاء التطبيقات الصناعية ، لذلك سنحاول في هذه المقالة شرح هذه الفكرة وإعطاء نظرة عامة على بعض النصائح التي تتناسب تمامًا مع عملية تطوير تطبيق الويب Boot Spring القياسية.
إذا لم تكن معتادًا على برنامج Spring Boot ، ولكنك لا تزال ترغب في تجربة بعض الأشياء المذكورة ، فقد أنشأت
مستودع GitHub المصاحب لهذه المقالة . إذا كنت تشعر بأنك فقدت في أي مكان في المقالة ، فإنني أوصي باستنساخ المستودع على الكمبيوتر المحلي لديك والتشغيل باستخدام الكود.
خطأ شائع # 1: النزول منخفضة للغاية
نواجه هذا الخطأ الشائع لأن متلازمة
"لم يخترع هنا" شائعة جدًا في عالم تطوير البرمجيات ، وتشمل الأعراض إعادة كتابة أجزاء من التعليمات البرمجية المستخدمة بشكل متكرر ، ويبدو أن العديد من المطورين يعانون من هذا.
على الرغم من أن فهم الدواخل الداخلية لمكتبة معينة وتنفيذها في معظمها أمر جيد وضروري (ويمكن أن يكون عملية تعليمية ممتازة) ، إلا أن حل نفس تفاصيل التنفيذ ذات المستوى المنخفض يعد ضارًا بتطويرك كمهندس برامج. هناك سبب لوجود أعمال تجريدية وأطر عمل مثل Spring والتي تفصلك تمامًا عن الحرف اليدوية المتكررة وتتيح لك التركيز على التفاصيل ذات المستوى الأعلى - كائنات المجال ومنطق العمل.
لذلك ، استخدم التجريدات - في المرة القادمة التي تواجه فيها مشكلة معينة ، قم أولاً بالبحث السريع وتحديد ما إذا كانت المكتبة التي تحل هذه المشكلة مدمجة في Spring. حاليًا ، من المرجح أن تجد حلاً مناسبًا حاليًا. كمثال على مكتبة مفيدة ، في أمثلة بقية هذه المقالة ، سأستخدم التعليقات التوضيحية
لمشروع لومبوك . يستخدم لومبوك كمولد لشفرات القوالب والمطور البطيء بداخلك ، ونأمل ألا تواجه مشكلة مع فكرة هذه المكتبة. على سبيل المثال ، انظر إلى ما يبدو عليه
"حبة Java القياسية" مع Lombok:
@Getter @Setter @NoArgsConstructor public class Bean implements Serializable { int firstBeanProperty; String secondBeanProperty; }
كما يمكنك أن تتخيل ، فإن الكود المذكور أعلاه يجمع ما يلي:
public class Bean implements Serializable { private int firstBeanProperty; private String secondBeanProperty; public int getFirstBeanProperty() { return this.firstBeanProperty; } public String getSecondBeanProperty() { return this.secondBeanProperty; } public void setFirstBeanProperty(int firstBeanProperty) { this.firstBeanProperty = firstBeanProperty; } public void setSecondBeanProperty(String secondBeanProperty) { this.secondBeanProperty = secondBeanProperty; } public Bean() { } }
ومع ذلك ، لاحظ أنك ستضطر على الأرجح إلى تثبيت المكون الإضافي إذا كنت تنوي استخدام Lombok مع IDE الخاص بك. يمكن العثور على نسخة البرنامج المساعد لبرنامج IntelliJ IDEA
هنا .
خطأ شائع رقم 2: تسرب محتوى داخلي
يعد الكشف عن البنية الداخلية لديك دائمًا فكرة سيئة ، لأنه يخلق مرونة في تصميم الخدمة ، وبالتالي يسهم في ممارسة الترميز السيئة. يتجلى "تسرب" المحتوى الداخلي في حقيقة أن بنية قاعدة البيانات يمكن الوصول إليها من نقاط نهاية API معينة. كمثال ، افترض أن POJO التالي ("كائن Java القديم البسيط") يمثل جدولًا في قاعدة البيانات الخاصة بك:
@Entity @NoArgsConstructor @Getter public class TopTalentEntity { @Id @GeneratedValue private Integer id; @Column private String name; public TopTalentEntity(String name) { this.name = name; } }
افترض أن هناك نقطة نهاية تحتاج إلى الوصول إلى بيانات TopTalentEntity. على الرغم من أنه من المغري إعادة مثيلات TopTalentEntity ، سيكون الحل الأكثر مرونة هو إنشاء فئة جديدة لعرض بيانات TopTalentEntity في نقطة نهاية واجهة برمجة التطبيقات:
@AllArgsConstructor @NoArgsConstructor @Getter public class TopTalentData { private String name; }
وبالتالي ، فإن إجراء تغييرات على الواجهة الخلفية لقاعدة البيانات لن يتطلب أي تغييرات إضافية على طبقة الخدمة. فكر فيما يحدث إذا قمت بإضافة حقل كلمة المرور إلى TopTalentEntity لتخزين تجزئات كلمة مرور المستخدم في قاعدة البيانات - بدون موصل مثل TopTalentData ، إذا نسيت تغيير الخدمة ، فستعرض الواجهة الأمامية بطريق الخطأ بعض المعلومات السرية غير المرغوب فيها جدًا!
الخطأ الشائع رقم 3: عدم الفصل بين الواجبات
مع نمو التطبيق الخاص بك ، يصبح تنظيم الكود الخاص بك مشكلة متزايدة الأهمية. ومن المفارقات ، أن معظم المبادئ الجيدة لتطوير البرمجيات بدأت تنتهك في كل مكان - وخاصة في الحالات التي لا يُولى فيها اهتمام كبير بتصميم بنية التطبيق. من بين أكثر الأخطاء شيوعًا التي يواجهها المطورون خلط المسؤوليات البرمجية ، ومن السهل جدًا القيام بذلك!
ما ينتهك عادة مبدأ
الفصل بين الواجبات هو ببساطة "إضافة" وظائف جديدة إلى الطبقات الموجودة. هذا ، بطبيعة الحال ، حل ممتاز على المدى القصير (بالنسبة للمبتدئين ، فإنه يتطلب كتابة أقل) ، لكنه سيصبح حتماً مشكلة في المستقبل ، سواء أثناء الاختبار أو الصيانة أو في مكان ما بينهما. خذ بعين الاعتبار وحدة التحكم التالية ، والتي تُرجع TopTalentData من المخزون الخاص به:
@RestController public class TopTalentController { private final TopTalentRepository topTalentRepository; @RequestMapping("/toptal/get") public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(this::entityToData) .collect(Collectors.toList()); } private TopTalentData entityToData(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } }
في البداية ، لا يُلاحظ وجود خطأ في هذا الكود. يوفر قائمة TopTalentData التي يتم استردادها من مثيلات TopTalentEntity. ومع ذلك ، إذا نظرت عن كثب ، فسنرى في الحقيقة أن TopTalentController يقوم ببعض الأشياء هنا. وهي: تقوم بتعيين خرائط لنقطة نهاية محددة ، وتسترد البيانات من المستودع وتحول الكيانات التي تم الحصول عليها من TopTalentRepository إلى تنسيق آخر. والحل "الأنظف" هو تقسيم هذه المسؤوليات إلى فئات خاصة بها. قد يبدو مثل هذا:
@RestController @RequestMapping("/toptal") @AllArgsConstructor public class TopTalentController { private final TopTalentService topTalentService; @RequestMapping("/get") public List<TopTalentData> getTopTalent() { return topTalentService.getTopTalent(); } } @AllArgsConstructor @Service public class TopTalentService { private final TopTalentRepository topTalentRepository; private final TopTalentEntityConverter topTalentEntityConverter; public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(topTalentEntityConverter::toResponse) .collect(Collectors.toList()); } } @Component public class TopTalentEntityConverter { public TopTalentData toResponse(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } }
ومن المزايا الإضافية لهذا التسلسل الهرمي أنه يتيح لنا تحديد مكان وجود الوظيفة من خلال التحقق ببساطة من اسم الفئة. بالإضافة إلى ذلك ، أثناء الاختبار ، يمكننا بسهولة استبدال أي من الفئات بتطبيق وهمي ، إذا لزم الأمر.
خطأ شائع رقم 4: عدم الاتساق وسوء معالجة الأخطاء
إن موضوع التناسق لا يقتصر بالضرورة على Spring (أو Java ، في هذا الشأن) ، لكنه لا يزال يمثل جانبًا مهمًا يجب مراعاته عند العمل في مشاريع Spring. على الرغم من أن أسلوب كتابة التعليمات البرمجية يمكن أن يكون موضوعًا للمناقشة (وعادةً ما يكون ذلك بمثابة اتفاق على الفريق أو في جميع أنحاء الشركة) ، فإن وجود معيار مشترك هو أمر مفيد للغاية في الأداء. هذا صحيح بشكل خاص لفرق من عدة أشخاص. يسمح الاتساق بنقل الكود دون حساب الموارد اللازمة للصيانة أو تقديم تفسيرات مفصلة بشأن مسؤوليات الفئات المختلفة.
النظر في مشروع الربيع مع مختلف ملفات التكوين والخدمات ، ووحدات التحكم. لكونه متسقًا بشكل شبه منطقي في تسميتهم ، يتم إنشاء بنية يمكن البحث فيها بسهولة يمكن لأي مطور جديد من خلالها التحكم في كيفية التعامل مع التعليمات البرمجية: على سبيل المثال ، تتم إضافة التكوين اللاحق إلى فئات التكوين ولاحقة الخدمة إلى الخدمات ولاحق التحكم إلى وحدات التحكم.
ترتبط ارتباطًا وثيقًا بموضوع الاتساق ، تستحق معالجة الأخطاء من جانب الخادم اهتمامًا خاصًا. إذا كان عليك التعامل مع إجابات الاستثناء من واجهة برمجة تطبيقات مكتوبة بشكل سيئ ، فمن المحتمل أن تعرف السبب في أنه قد يكون من المؤلم تحليل الاستثناءات ، ومن الصعب تحديد سبب حدوث هذه الاستثناءات في الأصل.
كمطور لواجهة برمجة التطبيقات ، من الأفضل أن تغطي جميع نقاط المستخدم النهائية وترجمتها إلى تنسيق خطأ شائع. هذا يعني عادةً أن لديك رمز خطأ شائعًا ووصفًا ، وليس مجرد عذر في شكل: أ) إعادة الرسالة "500 خطأ داخلي في الخادم" أو ب) مجرد إعادة تعيين تتبع المكدس إلى المستخدم (والذي يجب تجنبه بأي ثمن ، لأنه يُظهر الدواخل الداخلية لك بالإضافة إلى تعقيد المعالجة من جانب العميل).
قد يكون مثال تنسيق استجابة الأخطاء الشائعة:
@Value public class ErrorResponse { private Integer errorCode; private String errorMessage; }
عادة ما يوجد شيء مشابه في واجهات برمجة التطبيقات الأكثر شيوعًا وعادة ما يعمل بشكل جيد ، حيث يمكن توثيقه بسهولة وبشكل منهجي. يمكنك ترجمة الاستثناءات إلى هذا التنسيق من خلال توفير الطريقة مع التعليق التوضيحيExceptionHandler (يتم تقديم مثال على التعليق التوضيحي في الخطأ العام رقم 6).
خطأ شائع # 5: تعدد مؤشرات غير صحيحة
بغض النظر عما إذا كان موجودًا في تطبيقات سطح المكتب أو الويب ، في Spring أم لا في Spring ، فإن تعدد العمليات يمكن أن يكون مهمة شاقة. المشكلات الناتجة عن تشغيل البرامج المتوازية صعب المنال وغالبًا ما يكون من الصعب للغاية تصحيحها - في الواقع ، نظرًا لطبيعة المشكلة ، بمجرد أن تفهم أنك تتعامل مع مشكلة التنفيذ المتوازي ، فمن المحتمل أن تتخلى عن مصحح الأخطاء تمامًا وأن تبدأ تحقق من الكود يدويًا حتى تجد سبب الخطأ. لسوء الحظ ، لحل هذه المشاكل لا يوجد حل قالب. حسب الحالة المحددة ، سيتعين عليك تقييم الموقف ثم مهاجمة المشكلة من زاوية تعتبرها الأفضل.
من الناحية المثالية ، بالطبع ، ترغب في تجنب الأخطاء متعددة مؤشرات الترابط تمامًا. مرة أخرى ، لا يوجد منهج واحد لهذا ، ولكن فيما يلي بعض الاعتبارات العملية لتصحيح الأخطاء ومنع أخطاء تعدد مؤشرات الترابط:
تجنب الوضع العالمي
أولاً ، تذكر دائمًا مشكلة "الحالة العالمية". إذا كنت تقوم بإنشاء تطبيق متعدد الخيوط ، فيجب أن تتم مراقبة كل شيء يمكن تغييره عالميًا بعناية ، وإذا أمكن ، إزالته بالكامل. إذا كان هناك سبب يجب أن يظل المتغير العام قابلاً للتغيير ، فاستخدم
المزامنة بعناية ومراقبة أداء التطبيق الخاص بك للتأكد من أنه لا يتباطأ بسبب فترات الانتظار الجديدة.
تجنب قابلية
يأتي هذا مباشرة من
البرمجة الوظيفية ووفقًا ل OOP ، يجب تجنب تقلبات الصف وتغيير الحالة. باختصار ، ما تقدم يعني وجود مستوطنين وحقول نهائية خاصة في جميع فئات النموذج. قيمهم تتغير فقط أثناء البناء. وبالتالي ، يمكنك التأكد من أنه لن تكون هناك مشاكل في السباق على الموارد وأن الوصول إلى خصائص الكائن سيوفر دائمًا القيم الصحيحة.
تسجيل البيانات الهامة
تقييم حيث يمكن أن يسبب التطبيق الخاص بك مشاكل ، وتسجيل جميع البيانات الهامة مسبقا. في حالة حدوث خطأ ، ستكون ممتنًا للحصول على معلومات حول الطلبات التي تم تلقيها ويمكنك أن تفهم بشكل أفضل سبب تصرف طلبك بشكل سيء. مرة أخرى ، تجدر الإشارة إلى أن التسجيل يزيد من ملف الإدخال / الإخراج ، لذلك يجب عدم إساءة استخدامه ، حيث يمكن أن يؤثر ذلك بشكل خطير على أداء التطبيق الخاص بك.
إعادة استخدام التطبيقات الحالية
كلما احتجت إلى إنشاء سلاسل الرسائل الخاصة بك (على سبيل المثال ، لتقديم طلبات غير متزامنة لمختلف الخدمات) ، أعد استخدام التطبيقات الآمنة الموجودة ، بدلاً من إنشاء الحلول الخاصة بك. بالنسبة للجزء الأكبر ، فإن هذا يعني استخدام
ExecutorServices و
CompletableFutures في النمط الوظيفي الأنيق لـ Java 8 لإنشاء سلاسل رسائل. Spring يتيح أيضًا معالجة الطلبات غير المتزامنة من خلال فئة
DeferredResult .
الخطأ الشائع رقم 6: عدم استخدام التحقق من صحة المستند إلى التعليق التوضيحي
دعونا نتخيل أن خدمة TopTalent ، المذكورة أعلاه ، تحتاج إلى نقطة نهاية لإضافة مواهب جديدة فائقة. بالإضافة إلى ذلك ، افترض أنه لسبب وجيه حقًا ، يجب أن يكون طول كل اسم جديد 10 أحرف بالضبط. طريقة واحدة للقيام بذلك يمكن أن تكون على النحو التالي:
@RequestMapping("/put") public void addTopTalent(@RequestBody TopTalentData topTalentData) { boolean nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .map(TopTalentData::getName) .map(name -> name.length() == 10) .orElse(true); if (nameNonExistentOrInvalidLength) {
ومع ذلك ، فإن ما سبق (بالإضافة إلى كونه سيئ التصميم) ليس حقًا "نظيفًا". نحن نتحقق من أكثر من نوع واحد من الصلاحية (أي أن TopTalentData ليس خاليًا ، وأن TopTalentData.name ليس خاليًا ، وأن TopTalentData.name يبلغ طوله 10 أحرف) ، ويلقي استثناءًا أيضًا إذا كانت البيانات غير صالحة.
يمكن القيام بذلك بطريقة أكثر نظافة باستخدام
أداة التحقق من السبات مع Spring. أولاً ، نعيد كتابة طريقة addTopTalent لدعم التحقق من الصحة:
@RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) {
بالإضافة إلى ذلك ، يجب علينا الإشارة إلى الخاصية التي نريد التحقق منها في فئة TopTalentData:
public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; }
سيقوم Spring الآن باعتراض الطلب والتحقق منه قبل استدعاء الطريقة - ليست هناك حاجة لاستخدام اختبارات يدوية إضافية.
هناك طريقة أخرى يمكننا من خلالها تحقيق نفس الشيء وهي إنشاء تعليقاتنا الخاصة. على الرغم من أن التعليقات التوضيحية المخصصة لا تستخدم عادة إلا عندما تتجاوز احتياجاتك
مجموعة ثوابت السبات المدمجة ، على سبيل المثال ، دعنا نتخيل عدم وجود تعليقات توضيحية
طولية . يجب عليك إنشاء مدقق يتحقق من طول السلسلة عن طريق إنشاء فئتين إضافيتين ، واحدة للفحص والأخرى للخصائص التوضيحية:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = { MyAnnotationValidator.class }) public @interface MyAnnotation { String message() default "String length does not match expected"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; int value(); } @Component public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> { private int expectedLength; @Override public void initialize(MyAnnotation myAnnotation) { this.expectedLength = myAnnotation.value(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return s == null || s.length() == this.expectedLength; } }
لاحظ أنه في هذه الحالات ، تتطلب أفضل الممارسات لفصل الواجبات أن تحدد خاصية ما إذا كانت خالية (لا = =
n فارغة في طريقة isValid) ، ثم استخدم التعليق
NotNull إذا كان هذا مطلبًا إضافيًا للخاصية:
public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; }
خطأ شائع # 7: استخدام (لا يزال) تكوين XML
على الرغم من أن XML كانت ضرورية للإصدارات السابقة من Spring ، إلا أنه في الوقت الحالي يمكن إجراء معظم التهيئة حصريًا باستخدام كود / تعليقات Java. تمثل تكوينات XML ببساطة رمزًا إضافيًا غير ضروري.
تستخدم هذه المقالة (ومستودع GitHub المصاحب لها) التعليقات التوضيحية لتكوين Spring و Spring يعرف الفاصوليا التي يجب الاتصال بها لأن الحزمة الجذرية قد تم تعليقها باستخدام التعليق التوضيحي المركبSpringBootApplication ، على سبيل المثال:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
يعطي هذا التعليق التوضيحي المركب (يمكنك معرفة المزيد عن ذلك في وثائق
Spring ) مجرد تلميح إلى Spring حول الحزم التي ينبغي فحصها لاستخراج البقول. في حالتنا الخاصة ، يعني هذا أنه سيتم استخدام الفئات التالية لتوصيل الفول ، بدءًا من الحزمة ذات المستوى الأعلى (co.kukurin):
@Component (TopTalentConverter, MyAnnotationValidator) @RestController (TopTalentController) @Repository (TopTalentRepository) @Service (TopTalentService)
@Component (TopTalentConverter, MyAnnotationValidator) @RestController (TopTalentController) @Repository (TopTalentRepository) @Service (TopTalentService)
@Component (TopTalentConverter, MyAnnotationValidator) @RestController (TopTalentController) @Repository (TopTalentRepository) @Service (TopTalentService)
@Component (TopTalentConverter, MyAnnotationValidator) @RestController (TopTalentController) @Repository (TopTalentRepository) @Service (TopTalentService)
إذا كان لدينا أي فئات إضافية مشروحة باستخدامConfiguration ، فسيتم أيضًا التحقق من تهيئة Java.
خطأ شائع رقم 8: نسيان ملفات التعريف
المشكلة التي غالباً ما تصادف عند تطوير الخوادم هي الفرق بين أنواع مختلفة من التكوينات ، عادةً التكوينات الصناعية والتنموية. بدلاً من تغيير معلمات التكوين المختلفة يدويًا في كل مرة تقوم فيها بالتبديل من الاختبار إلى نشر التطبيق ، تتمثل الطريقة الأكثر فعالية في استخدام ملفات التعريف.
النظر في القضية عند استخدام قاعدة البيانات في الذاكرة للتنمية المحلية وقاعدة بيانات MySQL في PROM. في الأساس ، سيعني هذا أنك ستستخدم عناوين URL مختلفة (ونأمل) بيانات اعتماد مختلفة للوصول إلى كل منها. دعونا نرى كيف يمكن القيام بذلك من خلال ملفين مختلفين للتكوين:
تطبيق الملف. YAML
# set default profile to 'dev' spring.profiles.active: dev # production database details spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal' spring.datasource.username: root spring.datasource.password:
ملف التطبيق - DEV.YAML
spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2
على ما يبدو ، لا تريد تنفيذ أي إجراءات بطريق الخطأ في قاعدة البيانات الصناعية الخاصة بك أثناء الفوضى باستخدام الرمز ، لذلك فمن المنطقي تعيين ملف التعريف الافتراضي في dev. ثم على الخادم ، يمكنك تجاوز ملف تعريف التكوين يدويًا عن طريق تحديد -Dspring.profiles.active = prod المعلمة لـ JVM. بالإضافة إلى ذلك ، يمكنك أيضًا ضبط متغير بيئة نظام التشغيل على ملف التعريف الافتراضي المطلوب.
خطأ شائع رقم 9: عدم القدرة على قبول حقن التبعية
الاستخدام السليم لحقن التبعية في Spring يعني أنه يسمح لك بربط جميع الكائنات الخاصة بك معًا عن طريق مسح جميع فئات التكوين المطلوبة ؛ هذا مفيد للعلاقات فصل ، كما يجعل الاختبار أسهل بكثير. بدلاً من الفصول الثابتة ، عن طريق القيام بشيء مثل هذا:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } }
تركنا الربيع يقوم بالربط لنا:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } }
يشرح Misko Hevery من Google talk بالتفصيل "أسباب" حقن التبعية ، لذلك دعونا نرى كيف يتم استخدام ذلك في الممارسة العملية. في تقسيم المسؤوليات (الأخطاء الشائعة رقم 3) ، أنشأنا فصول الخدمة والتحكم. افترض أننا نريد اختبار وحدة تحكم على افتراض أن TopTalentService يتصرف بشكل صحيح. يمكننا إدراج كائن وهمي بدلاً من تطبيق الخدمة الفعلي ، مع توفير فئة تكوين منفصلة:
@Configuration public class SampleUnitTestConfig { @Bean public TopTalentService topTalentService() { TopTalentService topTalentService = Mockito.mock(TopTalentService.class); Mockito.when(topTalentService.getTopTalent()).thenReturn( Stream.of("Mary", "Joel") .map(TopTalentData::new).collect(Collectors.toList())); return topTalentService; } }
بعد ذلك يمكننا تضمين كائن وهمية عن طريق إخبار Spring لاستخدام SampleUnitTestConfig كموفر التكوين:
@ContextConfiguration(classes = { SampleUnitTestConfig.class })
بعد ذلك ، سيسمح لنا ذلك باستخدام تكوين السياق لتضمين الحبة المخصصة في اختبار الوحدة.
خطأ شائع رقم 10: عدم وجود اختبار أو اختبار غير صحيح
على الرغم من حقيقة أن فكرة اختبار الوحدة كانت معنا منذ فترة طويلة ، يبدو أن العديد من المطورين "ينسون" القيام بذلك (خاصةً إذا لم يكن ذلك ضروريًا) ، أو ببساطة ترك الأمر لوقت لاحق. من الواضح أن هذا أمر غير مرغوب فيه ، لأن الاختبارات يجب ألا تتحقق فقط من صحة التعليمات البرمجية الخاصة بك ، ولكن يجب أن تكون بمثابة وثائق حول كيفية تصرف التطبيق في مواقف مختلفة.
عند اختبار خدمات الويب ، نادراً ما تقوم بإجراء اختبارات وحدة "نظيفة" بشكل استثنائي ، حيث أن التفاعل من خلال HTTP يتطلب عادةً استدعاء DispatcherServlet Spring ومعرفة ما يحدث عند تلقي HttpServletRequest الفعلي (مما يجعله اختبار تكامل ، مع باستخدام التحقق من الصحة ، التسلسل ، وما إلى ذلك).
REST Assured - أثبتت Java DSL لاختبار خدمات REST على رأس MockMVC أنها حل أنيق للغاية.
النظر في جزء التعليمات البرمجية التالي مع التبعية حقن: @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception {
يتيح SampleUnitTestConfig تطبيق وهمية TopTalentService في TopTalentController ، في حين أن جميع الفئات الأخرى متصلة باستخدام التكوين القياسي الذي تم الحصول عليه عن طريق مسح الحزم التي لها جذور في حزمة فئة التطبيق. يستخدم RestAssuredMockMvc ببساطة لإنشاء بيئة خفيفة الوزن وإرسال طلب GET إلى نقطة نهاية / toptal / get.تصبح سيد الربيع
Spring هو إطار قوي يسهل البدء فيه ، ولكنه يحتاج إلى بعض التفاني والوقت لتحقيق التمكن الكامل. إذا كنت تقضي وقتًا في التعرف على الإطار ، فستزيد بالتأكيد من إنتاجيتك على المدى الطويل وتساعدك في النهاية على كتابة رمز أكثر نظافة وتصبح مطورًا أفضل.إذا كنت تبحث عن موارد إضافية ، فإن Spring In Action هو كتاب تدريبي جيد يغطي العديد من موضوعات Spring الأساسية.العلاماتجافا SpringFrameworkتعليقات
Timothy Schimandle№2 , . — , , . , , , dto — .
. .
SPIRITED to Timothy Schimandle. , , , @JsonIgnore ( ), . , …
Arokiadoss Asirvatham, : 1) 2) Singleton, singleton.
Hlodowig№8, , . :
- : : , - / ? , , . , , config .gitignore, .
- : , , , .
- قابلية النقل: أعرف أن هذه مجرد حجة JVM ، لكن الصفر أفضل من واحد. بلا حدود خطأ أقل عرضة.
حاولت إيجاد طريقة لاستخدام متغيرات البيئة في ملفات التكوين الخاصة بي بدلاً من "الترميز الثابت" للقيم ، لكن حتى الآن لم أفلح ، أعتقد أنني بحاجة إلى إجراء المزيد من البحث.مقال عظيم توني ، استمروا في العمل الجيد!اكتملت الترجمة: tele.gg/middle_java