[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-mikroservisy-kak-testirovat-mikroservisy":3},{"id":4,"slug":5,"topicId":6,"topicSlug":7,"topicName":8,"topicEmoji":9,"question":10,"answer":11,"codeLang":12,"codeSrc":12,"important":12,"commonMistakes":12,"modernUsage":12,"difficulty":13,"tags":14,"related":16,"progress":17,"seo":18},907,"kak-testirovat-mikroservisy",23,"mikroservisy","Микросервисы","🔗","Как тестировать микросервисы?","Тестирование микросервисов строится на основе пирамиды тестирования, адаптированной для распределённой архитектуры: unit-тесты (много), component-тесты, contract-тесты (между сервисами), integration-тесты и E2E-тесты (мало).\n\n```\n         ╱  E2E тесты  ╲         — мало (дорогие, хрупкие)\n        ╱ Integration    ╲        — средне\n       ╱ Contract tests   ╲       — много (между сервисами)\n      ╱ Component tests    ╲      — средне (один сервис целиком)\n     ╱ Unit tests           ╲     — очень много (быстрые)\n```\n\n\u003Cdetails>\u003Csummary>1. Unit-тесты: тестирование бизнес-логики\u003C\u002Fsummary>\n\n```java\n@ExtendWith(MockitoExtension.class)\nclass PaymentServiceTest {\n    @Mock private PaymentRepository paymentRepository;\n    @Mock private CustomerClient customerClient;\n    @InjectMocks private PaymentService paymentService;\n\n    @Test\n    void shouldRejectPaymentWhenInsufficientFunds() {\n        when(paymentRepository.findById(1L))\n            .thenReturn(Optional.of(new Payment(1L, new BigDecimal(\"100\"))));\n\n        assertThrows(InsufficientFundsException.class, () ->\n            paymentService.withdraw(1L, new BigDecimal(\"200\")));\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\u003Csummary>2. Component-тесты: один сервис с поднятым контекстом и тестовой БД\u003C\u002Fsummary>\n\n```java\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@Testcontainers\nclass PaymentServiceComponentTest {\n\n    @Container\n    static PostgreSQLContainer\u003C?> postgres = new PostgreSQLContainer\u003C>(\"postgres:16\");\n\n    @DynamicPropertySource\n    static void configureProperties(DynamicPropertyRegistry registry) {\n        registry.add(\"spring.datasource.url\", postgres::getJdbcUrl);\n        registry.add(\"spring.datasource.username\", postgres::getUsername);\n        registry.add(\"spring.datasource.password\", postgres::getPassword);\n    }\n\n    @Autowired\n    private TestRestTemplate restTemplate;\n\n    @Test\n    void shouldCreatePayment() {\n        PaymentRequest request = new PaymentRequest(BigDecimal.valueOf(1000), \"RUB\");\n\n        ResponseEntity\u003CPaymentResponse> response = restTemplate\n            .postForEntity(\"\u002Fapi\u002Fpayments\", request, PaymentResponse.class);\n\n        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);\n        assertThat(response.getBody().getStatus()).isEqualTo(\"CREATED\");\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\u003Csummary>3. Contract Testing с Pact\u003C\u002Fsummary>\n\n```java\n\u002F\u002F Consumer-сторона (сервис, который вызывает payment-service)\n@ExtendWith(PactConsumerTestExt.class)\n@PactTestFor(providerName = \"payment-service\")\nclass PaymentClientContractTest {\n\n    @Pact(consumer = \"order-service\")\n    public V4Pact createPact(PactDslWithProvider builder) {\n        return builder\n            .given(\"Платёж с ID 1 существует\")\n            .uponReceiving(\"Запрос платежа по ID\")\n            .path(\"\u002Fapi\u002Fpayments\u002F1\")\n            .method(\"GET\")\n            .willRespondWith()\n            .status(200)\n            .body(newJsonBody(body -> {\n                body.numberType(\"id\", 1L);\n                body.stringType(\"status\", \"COMPLETED\");\n                body.decimalType(\"amount\", 1000.00);\n            }).build())\n            .toPact(V4Pact.class);\n    }\n\n    @Test\n    @PactTestFor(pactMethod = \"createPact\")\n    void shouldGetPayment(MockServer mockServer) {\n        PaymentClient client = new PaymentClient(mockServer.getUrl());\n        PaymentResponse payment = client.getPayment(1L);\n\n        assertThat(payment.getStatus()).isEqualTo(\"COMPLETED\");\n    }\n}\n\n\u002F\u002F Provider-сторона (payment-service проверяет, что соответствует контракту)\n@Provider(\"payment-service\")\n@PactBroker(url = \"http:\u002F\u002Fpact-broker:9292\")\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\nclass PaymentProviderContractTest {\n\n    @TestTarget\n    public final Target target = new SpringBootHttpTarget();\n\n    @State(\"Платёж с ID 1 существует\")\n    void setupPaymentExists() {\n        paymentRepository.save(new Payment(1L, BigDecimal.valueOf(1000), \"COMPLETED\"));\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\u003Csummary>4. Integration-тесты с WireMock\u003C\u002Fsummary>\n\n```java\n@SpringBootTest\n@AutoConfigureWireMock(port = 0)\nclass PaymentServiceIntegrationTest {\n\n    @Test\n    void shouldCallCustomerServiceAndProcessPayment() {\n        stubFor(get(urlEqualTo(\"\u002Fapi\u002Fcustomers\u002F1\"))\n            .willReturn(aResponse()\n                .withHeader(\"Content-Type\", \"application\u002Fjson\")\n                .withBody(\"\"\"\n                    {\"id\": 1, \"name\": \"Иванов\", \"status\": \"ACTIVE\"}\n                    \"\"\")));\n\n        PaymentResponse result = paymentService.processPayment(\n            new PaymentRequest(1L, BigDecimal.valueOf(5000)));\n\n        assertThat(result.getStatus()).isEqualTo(\"COMPLETED\");\n        verify(getRequestedFor(urlEqualTo(\"\u002Fapi\u002Fcustomers\u002F1\")));\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n> **На собеседовании:** ключевое отличие от монолита — contract testing. Именно оно гарантирует, что API между сервисами не рассинхронизируется. Упомяните Pact и пирамиду тестов. Частая ошибка — пытаться покрыть всё E2E-тестами вместо contract-тестов.","","middle",[15],"microservices",[],null,{"title":19,"description":20,"ogTitle":19,"ogDescription":21,"keywords":22,"schemaAnswer":23,"featuredSnippetReady":24},"Как тестировать микросервисы? — Gymterview","Тестирование микросервисов строится на основе пирамиды тестирования, адаптированной для распределённой архитектуры: unit-тесты (много), component-тесты, contrac","Тестирование микросервисов строится на основе пирамиды тестирования, адаптированной для распределённой архитектуры: unit",[15,13],"Тестирование микросервисов строится на основе пирамиды тестирования, адаптированной для распределённой архитектуры: unit-тесты (много), component-тесты, contract-тесты (между сервисами), integration-тесты и E2E-тесты (мало).",true]