An assertion compares observed behavior with an expectation and reports the mismatch. A test runner decides how to execute independent tests and report their results. Keeping those jobs separate lets a failed assertion be recorded without stopping the remaining tests.
You will write a small runner with equality checks, expected-exception checks and fresh fixtures. It is JUnit-like in that limited sense. It doesn't implement JUnit annotations, discovery, extensions or the JUnit Platform. Test boundaries deserve the same care as product boundaries: a validator may accept zero, arrays require content comparison, and shared mutable fixtures can make later results depend on test order. Each task gives the observations as data so you can check the runner itself, including all-pass, all-fail and empty batches.