Chapter 5: tests, built

Chapter 5 asks two questions: how do you know a function gives the right answer, and how do you run one on its own when all you have run so far is a program? The answer to both is a test: square and cube, two pure functions, and the @Test programs that check them, kept in a dev/ directory of their own. The IDE's own tools make the structure - each function's whole signature, each test's @Test mark and stub - so what is left to type is the logic no tool can write: what square and cube compute, what each test claims, and the references block that brings the functions into the test file. To play it in the IDE: Help > Lessons > Writing code…, or ek9 -ide -lesson book-testing.

The EK9 IDE as the lesson opens

What you'll build

The lesson builds this program from a file holding only its module line - the finished file, as the book prints it.

maths.ek9

#!ek9
defines module book.maths

  defines function

    square() as pure
      -> n as Integer
      <- result as Integer: n * n

    cube() as pure
      -> n as Integer
      <- result as Integer: n * n * n

dev/tests.ek9

#!ek9
defines module book.maths.tests

  references
    book.maths::cube
    book.maths::square

  defines program

    @Test
    SquareTest()
      assert square(3) == 9

    @Test
    CubeTest()
      assert cube(2) == 8

Step 1: square — a pure function

Choose Code > New Construct > Function…

The EK9 IDE once this is done: Choose Code > New Construct > Function…

square is the function's name. Add one parameter, n of type Integer, set Returns to Integer, and tick pure. Choose Preview.

The EK9 IDE just before choosing Preview: square is the function's name

The preview shows square about to be written, pure, n in and Integer out. Choose Apply.

The EK9 IDE just before choosing Apply: The preview shows square about to be written, pure, n in and Integer out

Select the square() as pure line, and above it type the blank line every chapter example opens its first member with. Saves when typed.

The EK9 IDE once this is done: Select the square() as pure line, and above it type the blank line every chapter example opens its first member with

Select the stub, from <- rtn as Integer? down to its //AI line, and type the real calculation, returned as result: n * n. Saves when typed.

The EK9 IDE once this is done: Select the stub, from <- rtn as Integer? down to its //AI line, and type the real calculation, returned as result

Step 2: cube — a pure function

Choose Code > New Construct > Function…

The EK9 IDE once this is done: Choose Code > New Construct > Function…

cube is the function's name. Add one parameter, n of type Integer, set Returns to Integer, and tick pure, as square's were. Choose Preview.

The EK9 IDE just before choosing Preview: cube is the function's name

The preview shows cube about to be written, added to the same defines function block as square. Choose Apply.

The EK9 IDE just before choosing Apply: The preview shows cube about to be written, added to the same defines function block as square

Select the stub, from <- rtn as Integer? down to its //AI line, and type the real calculation, returned as result: n * n * n. Saves when typed.

The EK9 IDE once this is done: Select the stub, from <- rtn as Integer? down to its //AI line, and type the real calculation, returned as result

Step 3: Where tests live

Open dev/tests.ek9 - already there, with just its own module line. Test code goes in a directory named dev/, kept apart from the code you ship.

The EK9 IDE once this is done: Open dev/tests.ek9 - already there, with just its own module line

Select the module line, and below it type a references block bringing in cube and square from book.maths - in alphabetical order, which EK9 requires, so a long list of references is always easy to scan. Saves when typed.

The EK9 IDE once this is done: Select the module line, and below it type a references block bringing in cube and square from book.maths - in alphabetical order, which EK9 requires, so a long list of references is always easy to scan

Step 4: SquareTest

Choose Code > New Construct > Test…

The EK9 IDE once this is done: Choose Code > New Construct > Test…

SquareTest is the test's name - a test IS a program, so it is named the same way: no application, no parameters, nothing injected. Type SquareTest and choose Preview.

The EK9 IDE just before choosing Preview: SquareTest is the test's name - a test IS a program, so it is named the same way

The preview shows SquareTest about to be written, marked @Test, with a placeholder assertion standing in for the real one. Choose Apply.

The EK9 IDE just before choosing Apply: The preview shows SquareTest about to be written, marked @Test, with a placeholder assertion standing in for the real one

Select the @Test line, and above it type the blank line every chapter example opens its first member with. Saves when typed.

The EK9 IDE once this is done: Select the @Test line, and above it type the blank line every chapter example opens its first member with

Select the stub, from result <- "TODO: assert the unit under test" down to its //AI line, and type the real claim: assert square(3) == 9. Saves when typed.

The EK9 IDE once this is done: Select the stub, from result <- "TODO

Step 5: CubeTest

Choose Code > New Construct > Test…

The EK9 IDE once this is done: Choose Code > New Construct > Test…

CubeTest is the name, as plain as SquareTest's. Type CubeTest and choose Preview.

The EK9 IDE just before choosing Preview: CubeTest is the name, as plain as SquareTest's

The preview shows CubeTest about to be written, added to the same defines program block as SquareTest. Choose Apply.

The EK9 IDE just before choosing Apply: The preview shows CubeTest about to be written, added to the same defines program block as SquareTest

Select the stub, from result <- "TODO: assert the unit under test" down to its //AI line, and type the real claim: assert cube(2) == 8. Saves when typed.

The EK9 IDE once this is done: Select the stub, from result <- "TODO

Step 6: Run the tests

Run ▸ Run Tests (⌘T) runs every @Test in the project - SquareTest and CubeTest both - and reports pass or fail, the same as ek9 -t at the command line. Choose Run Tests.

The EK9 IDE once this is done: Run ▸ Run Tests (⌘T) runs every @Test in the project - SquareTest and CubeTest both - and reports pass or fail, the same as ek9 -t at the command line

The Tests tab lists SquareTest and CubeTest, each with a green tick - both passed. The bodies of square and cube in the editor are shaded too: that is coverage, every line your tests ran, turned on automatically by the run (View ▸ Toggles ▸ Coverage Highlighting toggles it by hand). Covered is not the same as tested - square(3) and cube(2) are both small, positive numbers, one kind of input between them.

The EK9 IDE once this is done: The Tests tab lists SquareTest and CubeTest, each with a green tick - both passed

Step 7: Generate Edge-Case Tests

Choose Code > Generate > Edge-Case Tests (whole file)…

The EK9 IDE once this is done: Choose Code > Generate > Edge-Case Tests (whole file)…

The dialog already has sensible defaults - the output routed to dev/, 200 candidates, a random seed. Choose Generate.

However many of the 200 candidates survive compiling and running clean are listed - edge and always-unset inputs that SquareTest and CubeTest never tried. These are liveness checks, not correctness tests: review them, then run them with Run Tests, the same as any other @Test. Choose Close.

The EK9 IDE just before choosing Close: However many of the 200 candidates survive compiling and running clean are listed - edge and always-unset inputs that SquareTest and CubeTest never tried

The result

maths.ek9 and dev/tests.ek9 now read exactly as chapter 5 of Getting Started with EK9 shows them: two pure functions and the @Test programs that check them, each tool-built, each real claim typed. Run Tests found both passing and shaded every line they covered; Generate Edge-Case Tests then tried inputs neither test chose - the reminder that covered is not the same as tested. The same tools - ek9_new_construct, ek9_add_program and ek9_generate_edge_case_tests - are what an AI assistant calls to do the same thing.

The EK9 IDE when the lesson has finished

Where this lesson comes from