

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
12345## Context67This instruction file applies to all test source files across the project. Tests are a first-class concern — untested code is unacceptable for production. This file governs test structure, naming, tooling, and quality standards across Java (JUnit 5) and Angular (Jasmine/Jest) test suites. Test code follows the same style and quality bar as production code: no magic numbers, no copy-paste, no meaningless assertions.89---1011## Java Test Standards1213### Framework Stack1415| Tool | Role |16|------|------|17| JUnit 5 (`org.junit.jupiter`) | Test lifecycle and execution |18| AssertJ | Fluent, readable assertions |19| Mockito 5.x | Mocking and verification |20| Awaitility | Async/concurrent test assertions |21| Testcontainers | Real infrastructure in integration tests |22| Spring Boot Test Slices | `@WebMvcTest`, `@DataJpaTest`, `@JsonTest` |23| Pact / Spring Cloud Contract | Consumer-driven contract testing |2425### Test Naming Convention2627Use the pattern: `methodName_scenario_expectedResult`2829```java30@Test31void findById_existingCustomer_returnsCustomerDto() { ... }3233@Test34void findById_nonExistentId_throwsCustomerNotFoundException() { ... }3536@Test37void createOrder_nullCustomerId_throwsIllegalArgumentException() { ... }3839@Test40void processPayment_insufficientBalance_returnsDeclinedResult() { ... }41```4243### Class Structure4445```java46@ExtendWith(MockitoExtension.class)47class OrderServiceTest {4849 @Mock50 private OrderRepository orderRepository;5152 @Mock53 private PaymentGateway paymentGateway;5455 @InjectMocks56 private OrderService orderService;5758 @BeforeEach59 void setUp() {60 // shared test data initialization61 }6263 @Test64 void createOrder_validRequest_persistsAndReturnsOrder() {65 // Arrange66 var request = OrderTestFactory.validCreateRequest();67 var savedOrder = OrderTestFactory.savedOrder();68 when(orderRepository.save(any(Order.class))).thenReturn(savedOrder);6970 // Act71 OrderResponse response = orderService.createOrder(request);7273 // Assert74 assertThat(response).isNotNull();75 assertThat(response.orderId()).isEqualTo(savedOrder.getId());76 assertThat(response.status()).isEqualTo(OrderStatus.PENDING);77 verify(orderRepository).save(any(Order.class));78 }79}80```8182### Assertions8384```java85// CORRECT: AssertJ fluent assertions86assertThat(result).isNotNull();87assertThat(result.getName()).isEqualTo("John Doe");88assertThat(result.getItems()).hasSize(3).extracting("sku").contains("SKU-001", "SKU-002");89assertThat(exception).isInstanceOf(OrderNotFoundException.class)90 .hasMessageContaining("Order not found");9192// Multi-field checks: use assertSoftly to report all failures at once93assertSoftly(softly -> {94 softly.assertThat(order.getId()).isNotNull();95 softly.assertThat(order.getStatus()).isEqualTo(OrderStatus.CONFIRMED);96 softly.assertThat(order.getCustomerId()).isEqualTo(expectedCustomerId);97});9899// WRONG: Never use JUnit assertEquals100assertEquals("John Doe", result.getName()); // ❌101assertTrue(result != null); // ❌102```103104### Parameterized Tests105106```java107@ParameterizedTest(name = "amount={0} should result in {1}")108@MethodSource("paymentAmountProvider")109void calculateFee_variousAmounts_returnsCorrectFee(BigDecimal amount, BigDecimal expectedFee) {110 assertThat(feeCalculator.calculate(amount)).isEqualByComparingTo(expectedFee);111}112113private static Stream<Arguments> paymentAmountProvider() {114 return Stream.of(115 Arguments.of(new BigDecimal("100.00"), new BigDecimal("2.50")),116 Arguments.of(new BigDecimal("0.00"), BigDecimal.ZERO),117 Arguments.of(new BigDecimal("999.99"), new BigDecimal("24.99"))118 );119}120```121122### Exception Testing123124```java125@Test126void findById_notFound_throwsOrderNotFoundException() {127 when(orderRepository.findById(any(UUID.class))).thenReturn(Optional.empty());128129 assertThatThrownBy(() -> orderService.findById(UUID.randomUUID()))130 .isInstanceOf(OrderNotFoundException.class)131 .hasMessageContaining("not found");132}133```134135### Async Tests — Awaitility136137```java138@Test139void processAsync_eventPublished_listenerReceivesEvent() {140 orderService.processAsync(orderId);141142 await()143 .atMost(Duration.ofSeconds(5))144 .pollInterval(Duration.ofMillis(200))145 .untilAsserted(() ->146 assertThat(eventCaptor.getEvents()).anyMatch(e -> e.getOrderId().equals(orderId))147 );148}149```150151### Spring MVC Controller Tests152153```java154@WebMvcTest(OrderController.class)155class OrderControllerTest {156157 @Autowired158 private MockMvc mockMvc;159160 @Autowired161 private ObjectMapper objectMapper;162163 @MockBean164 private OrderService orderService;165166 @Test167 void getOrder_existingOrder_returns200WithBody() throws Exception {168 var response = OrderTestFactory.orderResponse();169 when(orderService.findById(response.orderId())).thenReturn(response);170171 mockMvc.perform(get("/api/v1/orders/{id}", response.orderId())172 .accept(MediaType.APPLICATION_JSON))173 .andExpect(status().isOk())174 .andExpect(jsonPath("$.orderId").value(response.orderId().toString()))175 .andExpect(jsonPath("$.status").value("PENDING"));176 }177178 @Test179 void createOrder_invalidBody_returns422WithViolations() throws Exception {180 var invalidRequest = new CreateOrderRequest(null, List.of());181182 mockMvc.perform(post("/api/v1/orders")183 .contentType(MediaType.APPLICATION_JSON)184 .content(objectMapper.writeValueAsString(invalidRequest)))185 .andExpect(status().isUnprocessableEntity())186 .andExpect(jsonPath("$.violations").isArray());187 }188}189```190191### Spring Data JPA Repository Tests192193```java194@DataJpaTest195@Testcontainers196class OrderRepositoryIT {197198 @Container199 static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16-alpine");200201 @DynamicPropertySource202 static void properties(DynamicPropertyRegistry registry) {203 registry.add("spring.datasource.url", db::getJdbcUrl);204 registry.add("spring.datasource.username", db::getUsername);205 registry.add("spring.datasource.password", db::getPassword);206 }207208 @Autowired209 private OrderRepository orderRepository;210211 @Test212 void findByIdWithLineItems_orderWithItems_fetchesEagerly() {213 // given: pre-seeded test data via @Sql or test fixture214 UUID orderId = UUID.fromString("...");215216 // when217 Optional<Order> result = orderRepository.findByIdWithLineItems(orderId);218219 // then220 assertThat(result).isPresent();221 assertThat(result.get().getLineItems()).hasSize(2);222 }223}224```225226### Test Data — Factory Pattern227228```java229// Use static factory methods or builder pattern — never raw `new` in test body230public final class OrderTestFactory {231232 private OrderTestFactory() {}233234 public static Order pendingOrder() {235 return Order.builder()236 .id(UUID.randomUUID())237 .customerId(UUID.randomUUID())238 .status(OrderStatus.PENDING)239 .createdAt(Instant.now())240 .lineItems(List.of(lineItem()))241 .build();242 }243244 public static CreateOrderRequest validCreateRequest() {245 return new CreateOrderRequest(UUID.randomUUID(), List.of(lineItemRequest()));246 }247}248```249250---251252## Angular / TypeScript Test Standards253254### Component Tests255256```typescript257describe('CustomerListComponent', () => {258 let component: CustomerListComponent;259 let fixture: ComponentFixture<CustomerListComponent>;260 let customerServiceSpy: jasmine.SpyObj<CustomerService>;261262 beforeEach(async () => {263 customerServiceSpy = jasmine.createSpyObj('CustomerService', ['getAll']);264 customerServiceSpy.getAll.and.returnValue(of([{ id: '1', name: 'Alice' }]));265266 await TestBed.configureTestingModule({267 imports: [CustomerListComponent],268 providers: [{ provide: CustomerService, useValue: customerServiceSpy }],269 }).compileComponents();270271 fixture = TestBed.createComponent(CustomerListComponent);272 component = fixture.componentInstance;273 fixture.detectChanges();274 });275276 it('should display customers returned by service', () => {277 const items = fixture.nativeElement.querySelectorAll('.customer-list__item');278 expect(items.length).toBe(1);279 expect(items[0].textContent).toContain('Alice');280 });281282 it('should call getAll on init', () => {283 expect(customerServiceSpy.getAll).toHaveBeenCalledTimes(1);284 });285});286```287288### Service Tests289290```typescript291describe('CustomerService', () => {292 let service: CustomerService;293 let httpMock: HttpTestingController;294295 beforeEach(() => {296 TestBed.configureTestingModule({297 imports: [HttpClientTestingModule],298 providers: [CustomerService],299 });300 service = TestBed.inject(CustomerService);301 httpMock = TestBed.inject(HttpTestingController);302 });303304 afterEach(() => httpMock.verify());305306 it('getAll should return typed customer array', () => {307 const mockCustomers: Customer[] = [{ id: '1', name: 'Alice', active: true }];308309 service.getAll().subscribe(customers => {310 expect(customers).toEqual(mockCustomers);311 });312313 const req = httpMock.expectOne('/api/v1/customers');314 expect(req.request.method).toBe('GET');315 req.flush(mockCustomers);316 });317});318```319320---321322## Coverage Requirements323324- **Business logic classes:** 80% line coverage, 70% branch coverage minimum325- **Controllers:** All endpoints tested for happy path + at least one error case326- **Repositories:** All custom queries tested327- **Every `if` condition:** Must have at least one true-branch and one false-branch test328- **Every `catch` block:** Must have a test that triggers the exception329- **Every `Optional.empty()` path:** Must have a corresponding test330331---332333## What Copilot Must NOT Generate in Tests334335| Anti-Pattern | Why |336|-------------|-----|337| `Thread.sleep(1000)` | Use `Awaitility` — sleep is flaky |338| Empty `@Test` methods | A test with no assertions is misleading |339| `assertTrue(true)` or `assertNotNull(result)` only | Must assert meaningful business outcomes |340| Mocking the class under test | Defeats the purpose of the test |341| `@SpringBootTest` for a pure unit test | Use slices or pure Mockito — full context is slow |342| Test methods sharing mutable state | Tests must be independent and order-agnostic |343| Raw `new` in test body for domain objects | Use factory methods or builders |344| Testing private methods directly | Test behaviour through the public API |345| Ignoring or disabling tests without explanation | `@Disabled` requires a comment with a tracking issue |346
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| doubts-suplab/eeik-bootstrap.clinerules/golden-rules.md · 1 | Cline rules | gitsecuritydo-not | 61/100 | today | |
| doubts-suplab/eeik-bootstrap.clinerules/project.md · 1 | Cline rules | teststylegit | 63/100 | today | |
| doubts-suplab/eeik-bootstrap.cursor/rules/architecture.mdc · 1 | Cursor rules | do-not | 52/100 | today | |
| doubts-suplab/eeik-bootstrap.cursor/rules/capabilities.mdc · 1 | Cursor rules | teststylegit | 58/100 | today | |
| doubts-suplab/eeik-bootstrap.cursor/rules/golden-rules.mdc · 1 | Cursor rules | gitsecuritydo-not | 61/100 | today | |
| doubts-suplab/eeik-bootstrap.cursor/rules/python.mdc · 1 | Cursor rules | lint-formatstyletypesapi+1 | 77/100 | today | |
| doubts-suplab/eeik-bootstrap.cursor/rules/security.mdc · 1 | Cursor rules | security | 39/100 | today | |
| doubts-suplab/eeik-bootstrap.github/copilot-instructions.md · 1 | Copilot instructions | lint-formatstyletesting-strategygit+2 | 54/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/a2a-protocol.instructions.md · 1 | Copilot instructions | styleagent-behaviour | 48/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/ai-governance.instructions.md · 1 | Copilot instructions | stylearchdo-notagent-behaviour | 61/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/angular.instructions.md · 1 | Copilot instructions | teststyletypestesting-strategy+4 | 69/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/architecture-governance.instructions.md · 1 | Copilot instructions | testlint-formatstylegit+4 | 65/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/autogen.instructions.md · 1 | Copilot instructions | typessecurityagent-behaviour | 50/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/aws-architecture.instructions.md · 1 | Copilot instructions | styletypessecurityperformance | 58/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/aws-data-ml-ai.instructions.md · 1 | Copilot instructions | deployment | 54/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/cdk-terraform.instructions.md · 1 | Copilot instructions | teststylearchtypes+2 | 96/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/cicd.instructions.md · 1 | Copilot instructions | stylesecuritydeploymentdo-not+1 | 65/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/containerisation.instructions.md · 1 | Copilot instructions | buildstylesecuritydo-not | 77/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/crewai.instructions.md · 1 | Copilot instructions | styleagent-behaviour | 48/100 | today | |
| doubts-suplab/eeik-bootstrap.github/instructions/data-engineering.instructions.md · 1 | Copilot instructions | teststyletypesgit+5 | 69/100 | today |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| HerringtonDarkholme/megarepo.github/copilot-instructions.md · 17 | Copilot instructions | setupbuildtestlint-format+7 | 100/100 | 14 days ago | |
| louislam/uptime-kuma.github/copilot-instructions.md · 90k | Copilot instructions | setupbuildtestlint-format+9 | 100/100 | 14 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/global.instructions.md · 65 | Copilot instructions | buildlint-formatstylearch+4 | 100/100 | 14 days ago | |
| pytorch/pytorch.github/copilot-instructions.md · 102k | Copilot instructions | setupbuildteststyle+5 | 100/100 | 14 days ago | |
| JCodesMore/ai-website-cloner-template.github/copilot-instructions.md · 32k | Copilot instructions | buildlint-formatstylearch+3 | 97/100 | 7 days ago | |
| bagisto/bagisto.github/copilot-instructions.md · 28k | Copilot instructions | setupbuildteststyle+5 | 97/100 | 14 days ago | |
| hiyouga/LlamaFactory.github/copilot-instructions.md · 74k | Copilot instructions | setupbuildtestlint-format+5 | 97/100 | 13 days ago | |
| darkmatter/nixmac.github/copilot-instructions.md · 25 | Copilot instructions | setupbuildtestlint-format+8 | 96/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/doubts-suplab-eeik-bootstrap-github-instructions-test-instructions)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.