ستركز هذه المقالة على تكامل تطبيقات الويب المكتوبة باستخدام Spring والعمل عبر HTTP. إن اسم Spring Cloud Contract ، في رأيي ، مضلل ، لأنه لا علاقة له بالسحابة.
سيكون حول عقود API.
لاختبار وحدات التحكم ، غالبًا ما يتم استخدام mockMCV أو RestAssured. بالنسبة إلى mokas على الجانب الأمامي ، يتم استخدام خوادم mosk ، مثل Wiremock أو Pact. ولكن في كثير من الأحيان ، يتم كتابة اختبارات الوحدة بواسطة بعض الأشخاص والبعض الآخر بواسطة moki.
هذا يمكن أن يؤدي إلى مشاكل في التكامل.
على سبيل المثال ، قد يعرض الخادم في حالة عدم وجود بيانات 204 NO_CONTENT ، وقد يتوقع العميل 200 ملف JS فارغ وفارغ.
لا يهم أيهما صحيح. المشكلة هي أن شخصًا ما ارتكب خطأ ولن يتم العثور عليه قبل مرحلة الاندماج.
تم تصميم هذه المشكلة ليتم حلها عن طريق عقد سحابة الربيع.
ما هو عقد سحابة الربيع
هذا ملف يصف فيه اللهجة yaml أو groovyDSL كيف يجب أن يبدو الطلب والاستجابة. بشكل افتراضي ، جميع العقود موجودة في المجلد /src/test/resources/contracts/*
.
على سبيل المثال ، اختبر أبسط نقطة نهاية GET
@GetMapping("/bets/{userId}") public ResponseEntity<List<Bet>> getBets(@PathVariable("userId") String userId) { List<Bet> bets = service.getByUserId(userId); if (bets.isEmpty()) { return ResponseEntity.noContent().build(); } return ResponseEntity.ok(bets); }
سنصف العقد
org.springframework.cloud.contract.spec.Contract.make { request { method 'GET' urlPath '/bets/2' } response { status 200 headers { header('Content-Type', 'application/json') } body(''' { "sport":"football", "amount": 1 } ''' ) } }
علاوة على ذلك ، يتم إنشاء اختبارات الوحدة و json لـ wiremock من هذا الملف باستخدام البرنامج المساعد maven أو gradle.
سيبدو وصف JSON للصورة المقلدة للمثال أعلاه كما يلي:
{ "id" : "df8f7b73-c242-4664-add3-7214ac6356ff", "request" : { "urlPath" : "/bets/2", "method" : "GET" }, "response" : { "status" : 200, "body" : "{\"amount\":1,\"sport\":\"football\"}", "headers" : { "Content-Type" : "application/json" }, "transformers" : [ "response-template" ] }, "uuid" : "df8f7b73-c242-4664-add3-7214ac6356ff" }
يمكن تشغيل Wiremock محليًا ، ما عليك سوى تنزيل البرطمان من هنا . بشكل افتراضي ، يجب أن يضع json mokee مجلد mappings
.
$java -jar wiremock-standalone-2.18.0.jar
يظهر أدناه هو الاختبار الذي تم إنشاؤه. يتم استخدام مكتبة RestAssured بشكل افتراضي ، ولكن يمكن استخدام mockMVC أو spockframework.
public class UserControllerTest extends ContractBae { @Test public void validate_get_200() throws Exception {
وتجدر الإشارة إلى أن جميع الفئات التي تم إنشاؤها ترث نوعًا من الفئة الأساسية (قد يكون هناك العديد من الفئات الأساسية) ، حيث يتم تهيئة جميع المعلمات الضرورية للاختبارات. يتم وصف المسار إلى الفئة الأساسية في إعدادات المكون الإضافي.
بالنسبة لهذا المثال ، قد تبدو الفئة الأساسية كما يلي:
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SccDemoApplication.class) public abstract class ContractBae { @LocalServerPort int port; @Autowired protected WebApplicationContext webApplicationContext; @Before public void configureRestAssured() { RestAssured.port = port; MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) .build(); RestAssuredMockMvc.mockMvc(mockMvc); } }
نتيجة لذلك ، حصلنا على كل من الاختبارات والموكى تم الحصول عليها من مصدر واحد. إذا مرت اختبارات الوحدة وتشغيل الواجهة الأمامية الشرطية على mokas ، فلن تكون هناك مشاكل في التكامل.
لكن هذا ليس كل شيء
يمكن لـ Moki استخدام الواجهة الأمامية فقط ، ولكن التطبيق نفسه للتكامل مع تطبيق آخر. يمكن لـ Spring أن يبدأ خادم Mosk ، ما عليك سوى إنشاء برطمان مع mokami وتمرير المسار إليه @AutoConfigureStubRunner
التعليقات التوضيحية
لنفترض أن وحدة التحكم الخاصة بنا تقوم بعمل HTTP لتطبيق آخر:
@GetMapping("/bets/{userId}") public ResponseEntity<List<Bet>> getBets(@PathVariable("userId") String userId) { if(!isUsetExists(userId)) { return ResponseEntity.notFound().build(); } List<Bet> bets = service.getByUserId(userId); if (bets.isEmpty()) { return ResponseEntity.noContent().build(); } return ResponseEntity.ok(bets); } private boolean isUsetExists(String userId) { try { restTemplate.getForObject("/exists/" + userId, Void.class); return true; } catch (HttpStatusCodeException ignore) { return false; } }
ثم تحتاج فقط إلى وصف ترك الجرة مع mokami في الفئة الأساسية
@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SccDemoApplication.class) @AutoConfigureStubRunner(ids = {"me.dehasi.contracts.demo:sub-service-stubs:+:stubs:8090"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL) public abstract class ContractBase {
لأن هذه هي اختبارات وحدة التحكم ، ثم يمكن إنشاء قصاصات ascii-doc من نفس الاختبارات (مقالة كاملة عن بقية المستندات موجودة بالفعل في المحور ).
ماذا لدينا
اتضح أن لدينا مصدرًا واحدًا لعقد API ، الموصوف بلغة سهلة القراءة من الإنسان ، ومنه نقوم بإنشاء اختبارات الوحدة (نظريًا أيضًا التوثيق) ، ومنه moki. يقلل هذا النهج من مخاطر أخطاء التكامل بين تطبيقات الويب.
يمكن الاطلاع على الأمثلة على الموقع الرسمي على سبيل المثال .
يتم أخذ أمثلة التعليمات البرمجية في المقالة من أبسط مشروع هنا .