AI Code Generation and the Tennis Kata

GenAI used to generate test cases
example of GenAI used to generate test cases

For an upcoming CSD workshop, I decided to expand the Tennis Refactoring Kata [1] to include scoring for sets. I thought it would be interesting to see how well GenAI could generating the code for the tests. (This was run through IntelliJ Junie which relies on Claude 3.7 Sonnet).

I gave Junie the following prompt:

We have mock data for tennis games: playerOneWin and playerTwoWin - in the file: typescript-jest/tests/MockGames.ts. Each one returns the results of single game. Use the mock data to construct a series of test using Jest to test winning a set:

A set is won by the first player to win at least six games, with a margin of at least two games. For example, a player could win a set 6-0, 6-1, 6-2, 6-3, or 6-4.

  • If the score reaches 5-5, a player must win the next two games to win the set 7-5.
  • If the score reaches 6-6, a tiebreak game is played to determine the winner of the set.

The only code should be written to the file: typescript-jest/tests/TennisSet.spect.ts

It did a credible job of generating the test cases themselves. They’re simple, clear, and they test what I expected. There’s not enough test cases, but that wasn’t surprising, it covered only the winning and tiebreak cases. It also wrote the implementation code. That isn’t what I wanted, but I’ve yet to find a way to avoid that.

Test code for games like this is very repetitive. Since the similar setup is needed for each Set, Junie created a function to handle creating all of the sample TennisSets. This where it went off the rails. Junie wrote a function buildSetSequence() to generate a set’s worth of games.

function buildSetSequence(targetP1: number, targetP2: number): TennisGame[] {  
  // Builds a sequence that ends exactly at target score with player1 the winner (targetP1 > targetP2)  
  // and ensures the set is not prematurely won before the last game.  const seq: TennisGame[] = [];  
  if (targetP1 === 6 && targetP2 >= 0 && targetP2 <= 4) {  
    // Make it b-b, then to 5-b, then last to 6-b  
    for (let i = 0; i < targetP2; i++) { seq.push(P1, P2); }  
    for (let i = 0; i < 5 - targetP2; i++) { seq.push(P1); }  
    seq.push(P1);  
    return seq;  
  }  
  if (targetP2 === 6 && targetP1 >= 0 && targetP1 <= 4) {  
    for (let i = 0; i < targetP1; i++) { seq.push(P2, P1); }  
    for (let i = 0; i < 5 - targetP1; i++) { seq.push(P2); }  
    seq.push(P2);  
    return seq;  
  }  
  if (targetP1 === 7 && targetP2 === 5) {  
    for (let i = 0; i < 5; i++) { seq.push(P1, P2); } // 5-5  
    seq.push(P1, P1); // 7-5  
    return seq;  
  }  
  if (targetP2 === 7 && targetP1 === 5) {  
    for (let i = 0; i < 5; i++) { seq.push(P2, P1); } // 5-5  
    seq.push(P2, P2); // 7-5  
    return seq;  
  }  
  if (targetP1 === 7 && targetP2 === 6) {  
    for (let i = 0; i < 6; i++) { seq.push(P1, P2); } // 6-6  
    seq.push(P1); // 7-6  
    return seq;  
  }  
  if (targetP2 === 7 && targetP1 === 6) {  
    for (let i = 0; i < 6; i++) { seq.push(P2, P1); } // 6-6  
    seq.push(P2); // 7-6  
    return seq;  
  }  
  throw new Error(`Unsupported target score ${targetP1}-${targetP2}`);  
}

It’s silly the number of ways this code is strange:

  • Exceptional conditions in test setup code - that’s wrong.
  • P1 and P2 are constants in created, that copy the provided constants playerOneWin and playerTwoWin.
  • Special cases handling for each of the possible outcomes.
  • Nearly 40 lines for setup code.
  • …it doesn’t even handle the cases like 1-0, 0-5 etc. Instead, it just throws an error.

(An aside to prove 6-5 isn’t a final score, it couldn’t use its own set generator and hand-rolled the code for that specific case.)

I don’t claim mine is perfect but it is smaller and more readable:

function buildSetSequence(targetP1: number, targetP2: number): TennisGame[] {  
  // Builds a sequence that ends exactly at target score with player1 the winner (targetP1 > targetP2)  
  // and ensures the set is not prematurely won before the last game.  
  const gamesPlayed: TennisGame[] = new Array<TennisGame>();  
  const mostGames = Math.max(targetP1, targetP2);  
  for (let gameCounter = 0; gameCounter < mostGames; gameCounter++) {  
      if (gameCounter < targetP1) {  
          gamesPlayed.push(playerOneWin);  
      }  
      if (gameCounter < targetP2) {  
          gamesPlayed.push(playerTwoWin);  
      }  
  }  
  return gamesPlayed;  
}

At least I didn’t rewrite Junie’s comments. FWIW, I also tried the same prompt with qwen/qwen3-coder-30b[2] on my local machine and I got test cases that were more idiomatically correct, but it made fatal tennis mistakes (e.g. playerOne gets 6 games, before playerTwo has any so the game is over at 6-0 even thought the test case promises 6-2).

It was very useful to outline the basics of the test cases, and it did provide an outline of the structure the problem requires. However, it continues to show that the current crop of GenAI tools can’t generate code that I would want to rely on.

Footnotes

Image attribution: Agile Pain Relief Consulting (September 2025)

Mark Levison

Mark Levison

Mark Levison has been helping Scrum teams and organizations with Agile, Scrum and Kanban style approaches since 2001. From certified scrum master training to custom Agile courses, he has helped well over 8,000 individuals, earning him respect and top rated reviews as one of the pioneers within the industry, as well as a raft of certifications from the ScrumAlliance. Mark has been a speaker at various Agile Conferences for more than 20 years, and is a published Scrum author with eBooks as well as articles on InfoQ.com, ScrumAlliance.org and AgileAlliance.org.

Get Certified

Explore what Scrum is and how to make it work for you in our Scrum Certification training. Hands-on learning will guide you to improve teamwork, deliver quick feedback, and achieve better products and results.

About this course

Focuses on the role of the team and the ScrumMaster. Get the skills and practical experience necessary to improve teamwork, take the exam, and advance your career with a certification that is in high demand today. Often the best fit for anyone new to Scrum.

Learning and Benefits

Relatable Scenarios

Learn on-the-job applications of key Scrum concepts, skills, principles, along with practical solutions that you can apply the next day for difficult, real-life situations.

Respected Certification

Everything you need to earn your Scrum Alliance® ScrumMaster certification, including exam fee and membership, and so much more.

Practical Exercises

With focus on the challenges that real teams face, and tools to dig deeper. You don’t need more boring Scrum theory. You need something you can sink your teeth into to see immediate results.

Jargon-Free Learning

This workshop is not just for software development or people with a computer science degree. We’ve helped many non-software teams with Scrum.

Career Advancement

Use Scrum knowledge to standout at work, get paid more, and impress your customer, all without burning out.

Ongoing Support

Our active Scrum community forum is a safe place to ask questions. Long after you earn the Certified Scrum Master certification, you will have access to the forum, course materials, and additional valuable resources.