[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-sovremennaya-razrabotka-web-kak-organizovat-testirovanie-v-sovremennom-java-proekte":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":20,"progress":21,"seo":22},1197,"kak-organizovat-testirovanie-v-sovremennom-java-proekte",37,"sovremennaya-razrabotka-web","Современная разработка WEB","🌐","Как организовать тестирование в современном Java-проекте?","Стратегия тестирования в 2026 году строится вокруг пирамиды: unit-тесты (много, быстрые), slice-тесты и интеграционные (средне), E2E и contract-тесты (мало), плюс автоматические архитектурные проверки через ArchUnit.\n\n### Пирамида тестирования\n\n```\n        \u002F  E2E  \\           \u003C- Мало (Playwright, API tests)\n       \u002F  Интегр. \\         \u003C- Средне (Testcontainers, @SpringBootTest)\n      \u002F Slice-тесты \\       \u003C- Средне (@WebMvcTest, @DataJpaTest)\n     \u002F   Unit-тесты   \\    \u003C- Много (JUnit 5 + Mockito)\n    \u002F  Архитектурные    \\   \u003C- Автоматически (ArchUnit)\n```\n\n### Unit-тест бизнес-логики\n\n\u003Cdetails>\n\u003Csummary>OrderServiceTest\u003C\u002Fsummary>\n\n```java\n@ExtendWith(MockitoExtension.class)\nclass OrderServiceTest {\n\n    @Mock OrderRepository orderRepository;\n    @Mock PaymentGateway paymentGateway;\n    @Mock OrderEventPublisher eventPublisher;\n    @InjectMocks OrderService orderService;\n\n    @Test\n    void shouldCreateOrderWithCorrectTotal() {\n        CreateOrderCommand command = new CreateOrderCommand(\n            UUID.randomUUID(),\n            List.of(\n                new OrderItemCommand(UUID.randomUUID(), 2, new Money(\"29.99\", \"USD\")),\n                new OrderItemCommand(UUID.randomUUID(), 1, new Money(\"49.99\", \"USD\"))\n            ));\n        when(orderRepository.save(any(Order.class)))\n            .thenAnswer(invocation -> invocation.getArgument(0));\n\n        Order result = orderService.createOrder(command);\n\n        assertThat(result.getStatus()).isEqualTo(OrderStatus.CREATED);\n        assertThat(result.getTotalAmount()).isEqualByComparingTo(new BigDecimal(\"109.97\"));\n        verify(eventPublisher).publishOrderCreated(result);\n        verify(paymentGateway, never()).charge(any(), any());\n    }\n\n    @ParameterizedTest\n    @CsvSource({\n        \"CREATED,    CONFIRMED, true\",\n        \"CONFIRMED,  SHIPPED,   true\",\n        \"DELIVERED,  CANCELLED, false\"\n    })\n    void shouldValidateStatusTransition(OrderStatus from, OrderStatus to, boolean allowed) {\n        assertThat(from.canTransitionTo(to)).isEqualTo(allowed);\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Testcontainers для интеграционных тестов\n\nTestcontainers запускает реальные зависимости (PostgreSQL, Kafka, Redis) в Docker-контейнерах:\n\n\u003Cdetails>\n\u003Csummary>IntegrationTestBase\u003C\u002Fsummary>\n\n```java\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@Testcontainers\nabstract class IntegrationTestBase {\n\n    @Container\n    static PostgreSQLContainer\u003C?> postgres = new PostgreSQLContainer\u003C>(\"postgres:17-alpine\");\n\n    @Container\n    static KafkaContainer kafka = new KafkaContainer(\n        DockerImageName.parse(\"confluentinc\u002Fcp-kafka:7.7.0\"));\n\n    @Container\n    static GenericContainer\u003C?> redis = new GenericContainer\u003C>(\"redis:7-alpine\")\n        .withExposedPorts(6379);\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        registry.add(\"spring.kafka.bootstrap-servers\", kafka::getBootstrapServers);\n        registry.add(\"spring.data.redis.host\", redis::getHost);\n        registry.add(\"spring.data.redis.port\", () -> redis.getMappedPort(6379));\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Slice-тесты\n\n```java\n\u002F\u002F Тест контроллера (без запуска всего контекста)\n@WebMvcTest(OrderController.class)\nclass OrderControllerTest {\n\n    @Autowired MockMvc mockMvc;\n    @MockBean CreateOrderUseCase createOrderUseCase;\n\n    @Test\n    void shouldReturn201WhenOrderCreated() throws Exception {\n        when(createOrderUseCase.create(any())).thenReturn(order);\n\n        mockMvc.perform(post(\"\u002Fapi\u002Fv1\u002Forders\")\n                .contentType(MediaType.APPLICATION_JSON)\n                .content(\"{...}\"))\n            .andExpect(status().isCreated())\n            .andExpect(header().exists(\"Location\"));\n    }\n}\n```\n\n### ArchUnit для архитектурных правил\n\n```java\n@AnalyzeClasses(packages = \"com.example.order\")\nclass ArchitectureTest {\n\n    @ArchTest\n    static final ArchRule domainShouldNotDependOnAdapters =\n        noClasses().that().resideInAPackage(\"..domain..\")\n            .should().dependOnClassesThat().resideInAPackage(\"..adapter..\");\n\n    @ArchTest\n    static final ArchRule domainShouldNotDependOnSpring =\n        noClasses().that().resideInAPackage(\"..domain..\")\n            .should().dependOnClassesThat().resideInAPackage(\"org.springframework..\");\n}\n```\n\n### Частые ошибки\n\n- Тестирование на H2 вместо реальной БД: различия в SQL-диалектах приводят к пропущенным багам\n- Отсутствие тестов на ошибочные сценарии: тестируется только happy path\n- Mock всего подряд: unit-тест, который мокает всё, не тестирует ничего\n- Запуск @SpringBootTest для каждого теста — используйте @WebMvcTest\u002F@DataJpaTest где возможно\n\n> **На собеседовании:** покажите знание пирамиды тестирования и объясните, когда какой тип используется. Ключевая фраза: \"Testcontainers заменил H2 — тестируем на реальном PostgreSQL\". Упоминание ArchUnit для автоматической проверки архитектурных правил показывает зрелость. Частый вопрос: \"Чем @WebMvcTest отличается от @SpringBootTest?\" — первый загружает только web-слой, второй поднимает весь контекст.","","middle",[15,16,17,18,19],"archunit","junit","testcontainers","testing","spring-boot",[],null,{"title":23,"description":24,"ogTitle":25,"ogDescription":26,"keywords":27,"schemaAnswer":38,"featuredSnippetReady":39},"Как организовать тестирование в современном Java-проекте — Gymterview","Пирамида тестирования: unit (JUnit 5 + Mockito), slice (@WebMvcTest), интеграционные (Testcontainers), E2E и архитектурные (ArchUnit). Примеры на Spring Boot.","Тестирование в Java-проекте: JUnit 5, Testcontainers, ArchUnit — Gymterview","Пирамида тестирования: unit, slice, интеграционные (Testcontainers), архитектурные (ArchUnit). Testcontainers заменил H2.",[28,29,30,31,32,33,34,35,36,37],"тестирование Java","JUnit 5","Testcontainers","Mockito","ArchUnit","WebMvcTest","DataJpaTest","SpringBootTest","пирамида тестирования","собеседование","Пирамида тестирования: unit-тесты (JUnit 5 + Mockito, много и быстрые), slice-тесты (@WebMvcTest, @DataJpaTest — загружают часть контекста), интеграционные (Testcontainers с реальным PostgreSQL\u002FKafka\u002FRedis), E2E и contract-тесты, архитектурные (ArchUnit — автоматическая проверка зависимостей). Testcontainers заменил H2 — тестируем на реальной БД.",true]