Machine Coding Problem

Unit Testing Framework

maco60macoAllinfrastructurereflectionannotation-logic
Commonly Asked By:JetBrainsMicrosoftGoogle

Requirements & System Scope

Functional Scope (In-Scope)

  • Metadata Annotation / Decorator Discovery: Inspecting, querying, and cataloging target test class behaviors via reflections.
  • Structured Test LifeCycle: Executing hooks in fixed ordering: BeforeAll โ†’ Before โ†’ Test โ†’ After โ†’ AfterAll.
  • Rigorous Assertion Library: Supporting absolute equality checks, boolean validations, and exception capture testing.
  • Test Case Isolation: Guaranteeing zero state pollution by instantiating fresh test classes per method run.

Explicit Boundaries (Out-of-Scope)

  • Dynamic File Scanning: Recursively parsing files on disk to find classes or directory watchers (focused purely on runtime class reflection).
  • Mocking / Stubbing Frameworks: Dynamic proxy generation or bytecode manipulation to replace target system dependencies (e.g. Mockito features).

Class Diagram & Entity Relationships

Class interactions illustrating how the Runner controls Class Instances and captures Results:

Loading...
  • TestResult: Contains test method name, completion status (PASS, FAIL for assertion mismatches, or ERROR for unhandled exceptions), and failure traces.
  • AssertionError: Special semantic exception subclass used specifically to classify expected evaluation errors vs runtime environment crashes.

Design Patterns & SOLID Principles

  • Command Pattern: Each test method is parsed as a Command object executed uniformly by the central TestRunner invocation engine.
  • Template Method Pattern: Defines the algorithmic skeleton of a test run (setup, execute, tear down) while subclasses/target methods furnish the actual test actions.
  • Single Responsibility Principle (SRP): Isolates assertion mechanisms (Assert) from scanning execution (TestRunner) and reporting models.

Core Execution Workflows

Reflection Scanning & Hook Execution

  1. The test runner accepts a test class reference (e.g., MyTests.class).
  2. Reflective methods scan the class to resolve annotated elements (matching @Test, @Before, etc.).
  3. Static class initialization hooks (BeforeAll) run first.
  4. For every discovered test method:
    • A new target object is instantiated via its empty constructor to ensure state isolation.
    • Instance setup hooks (@Before) are invoked.
    • The actual test method is executed dynamically.
    • Errors are checked: AssertionError marks the run as FAIL; other exceptions mark it as ERROR.
    • Instance teardown hooks (@After) run inside a finally block.
  5. Static cleanup hooks (AfterAll) run at the absolute end.

Concurrency & Thread Safety Strategy

Enabling thread-safe parallel test execution:

  • Thread Isolation: Instantiating test classes per run guarantees thread isolation, preventing race conditions on object properties during parallel execution.
  • Thread-Safe Reporters: If test cases share progress dashboards, the reporter should serialize updates or use concurrent data collections (e.g. concurrent lists) to gather outcomes.

Complete Clean Code Blueprint

Clean reference designs demonstrating dynamic metadata scanning and lifecycle runners in Java and Python:

// โ”€โ”€โ”€ JAVA BLUEPRINT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
import java.lang.annotation.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Test {}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Before {}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface After {}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface BeforeAll {}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface AfterAll {}

class Assert {
    public static void assertEquals(Object expected, Object actual) {
        if (!Objects.equals(expected, actual)) {
            throw new AssertionError("Expected " + expected + " but got " + actual);
        }
    }

    public static void assertTrue(boolean condition) {
        if (!condition) {
            throw new AssertionError("Expected true but got false");
        }
    }

    public static <T extends Throwable> void assertThrows(Class<T> expectedException, Runnable executable) {
        try {
            executable.run();
        } catch (Throwable t) {
            if (expectedException.isInstance(t)) {
                return; // Success
            }
            throw new AssertionError("Expected exception of type " + expectedException.getName() + " but got " + t.getClass().getName());
        }
        throw new AssertionError("Expected exception of type " + expectedException.getName() + " but none was thrown");
    }
}

enum TestStatus { PASS, FAIL, ERROR }

class TestResult {
    private final String name;
    private final TestStatus status;
    private final Throwable failureReason;

    public TestResult(String name, TestStatus status, Throwable failureReason) {
        this.name = name;
        this.status = status;
        this.failureReason = failureReason;
    }

    public String getName() { return name; }
    public TestStatus getStatus() { return status; }
    public Throwable getFailureReason() { return failureReason; }

    @Override
    public String toString() {
        return "TestResult{name='" + name + "', status=" + status + ", reason=" + (failureReason != null ? failureReason.toString() : "None") + "}";
    }
}

class TestRunner {
    public List<TestResult> run(Class<?> testClass) {
        List<TestResult> results = Collections.synchronizedList(new ArrayList<>());
        List<Method> testMethods = new ArrayList<>();
        List<Method> beforeMethods = new ArrayList<>();
        List<Method> afterMethods = new ArrayList<>();
        List<Method> beforeAllMethods = new ArrayList<>();
        List<Method> afterAllMethods = new ArrayList<>();

        for (Method method : testClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Test.class)) testMethods.add(method);
            if (method.isAnnotationPresent(Before.class)) beforeMethods.add(method);
            if (method.isAnnotationPresent(After.class)) afterMethods.add(method);
            if (method.isAnnotationPresent(BeforeAll.class)) beforeAllMethods.add(method);
            if (method.isAnnotationPresent(AfterAll.class)) afterAllMethods.add(method);
        }

        try {
            // Run BeforeAll
            for (Method m : beforeAllMethods) {
                m.invoke(null); // Expect static methods
            }

            if (!testMethods.isEmpty()) {
                java.util.concurrent.ExecutorService executor = java.util.concurrent.Executors.newFixedThreadPool(4);
                try {
                    java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(testMethods.size());
                    for (Method testMethod : testMethods) {
                        executor.submit(() -> {
                            try {
                                Object instance = testClass.getDeclaredConstructor().newInstance();
                                Throwable failure = null;
                                TestStatus status = TestStatus.PASS;

                                try {
                                    // Run Before
                                    for (Method before : beforeMethods) {
                                        before.invoke(instance);
                                    }

                                    // Run Test
                                    testMethod.invoke(instance);

                                } catch (InvocationTargetException ite) {
                                    Throwable cause = ite.getCause();
                                    if (cause instanceof AssertionError) {
                                        status = TestStatus.FAIL;
                                    } else {
                                        status = TestStatus.ERROR;
                                    }
                                    failure = cause;
                                } catch (Exception e) {
                                    status = TestStatus.ERROR;
                                    failure = e;
                                } finally {
                                    // Run After
                                    try {
                                        for (Method after : afterMethods) {
                                            after.invoke(instance);
                                        }
                                    } catch (Exception e) {
                                        status = TestStatus.ERROR;
                                        failure = e;
                                    }
                                }

                                results.add(new TestResult(testMethod.getName(), status, failure));
                            } catch (Exception e) {
                                results.add(new TestResult(testMethod.getName(), TestStatus.ERROR, e));
                            } finally {
                                latch.countDown();
                            }
                        });
                    }
                    latch.await();
                } finally {
                    executor.shutdown();
                }
            }

            // Run AfterAll
            for (Method m : afterAllMethods) {
                m.invoke(null); // Expect static methods
            }

        } catch (Exception e) {
            System.err.println("Lifecycle initialization error: " + e.getMessage());
        }

        return results;
    }
}

// โ”€โ”€โ”€ DEMO TEST SUITE FOR REFLECTION SCANNING โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
class MathTestSuite {
    private static int globalCounter = 0;
    private int localState = 0;

    @BeforeAll
    public static void initAll() {
        globalCounter = 100;
        System.out.println("[MathTestSuite] @BeforeAll invoked. globalCounter = " + globalCounter);
    }

    @Before
    public void initEach() {
        localState = 5;
        System.out.println("[MathTestSuite] @Before invoked. localState = " + localState);
    }

    @Test
    public void testAdditionSuccess() {
        System.out.println("[MathTestSuite] Executing testAdditionSuccess");
        Assert.assertEquals(10, localState + 5);
    }

    @Test
    public void testAssertionFailure() {
        System.out.println("[MathTestSuite] Executing testAssertionFailure");
        Assert.assertEquals(12, localState + 5);
    }

    @Test
    public void testRuntimeError() {
        System.out.println("[MathTestSuite] Executing testRuntimeError");
        String nullString = null;
        nullString.length(); // Throws NullPointerException
    }

    @After
    public void cleanupEach() {
        System.out.println("[MathTestSuite] @After invoked.");
    }

    @AfterAll
    public static void cleanupAll() {
        System.out.println("[MathTestSuite] @AfterAll invoked.");
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== RUNNING UNIT TESTING FRAMEWORK DEMO ===");
        TestRunner runner = new TestRunner();
        List<TestResult> results = runner.run(MathTestSuite.class);

        System.out.println("\n=== TEST RESULTS REPORT ===");
        for (TestResult result : results) {
            System.out.println(result);
        }
        System.out.println("=== SIMULATION COMPLETE ===");
    }
}

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...