thecodex.expert · The Codex Family of Knowledge
Tier 2 · Intermediate · Python Project

Quiz Engine

A reusable quiz system that loads questions from a file, supports categories, and tracks scores. A real, tested, data-driven app.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want a quiz engine — not a hard-coded quiz, but a system that loads questions from a JSON file, lets you pick a category, runs the quiz, and reports your score. It teaches building a reusable, data-driven application where the content lives in data, not code.

Where this shows up: learning platforms, certification tests, trivia games, survey tools, any app where non-programmers add content. Separating the engine from the content is a hallmark of professional software.

2 How to Think About It

Think about engine versus content, before any code:

The plan — in plain English
1. Questions live in a file (data), each with a category, question, options, and answer. → 2. The engine loads them, filters by chosen category, asks each, and scores. → 3. Adding questions means editing the data file — no code change. That separation is the whole point.

Load questions from file

Pick a category

Filter questions to category

Ask each, check answer

Track score

Show final score

3 The Build — explained part by part

Here is the complete quiz engine. Each part is explained below.

Pythonquiz_engine.py
import json

def load_questions(filename):
    with open(filename) as f:
        return json.load(f)

def filter_by_category(questions, category):
    return [q for q in questions if q["category"] == category]

def check_answer(question, given):
    return given.strip().lower() == question["answer"].strip().lower()

def run_quiz(questions):
    score = 0
    for q in questions:
        print("\n" + q["question"])
        for i, option in enumerate(q["options"], 1):
            print(f"  {i}. {option}")
        given = input("Your answer: ")
        if check_answer(q, given):
            print("Correct!")
            score += 1
        else:
            print(f"Wrong. Answer: {q['answer']}")
    return score

if __name__ == "__main__":
    questions = load_questions("questions.json")
    category = input("Category: ")
    selected = filter_by_category(questions, category)
    score = run_quiz(selected)
    print(f"\nScore: {score} / {len(selected)}")
What each part does — in plain words
load_questions — read the questions from a JSON file. The content is data, fully separate from the engine.

filter_by_category — keep only the questions in the chosen category, using a list comprehension.

check_answer — compare the player’s answer to the correct one, cleaned with strip().lower() so spacing and capitals do not matter. Pulling this into its own function makes it testable.

enumerate(q["options"], 1) — number the multiple-choice options starting at 1.

run_quiz — the engine loop: ask each question, check, score. It works for any set of questions, which is what makes it an engine rather than a single quiz.
Common mistakes — and how to avoid them
✗ Hard-coding questions in the code — then non-programmers cannot add them and it is not an engine.
✓ Load questions from a data file so content is separate.
✗ Comparing answers without cleaning — “Paris ” fails.
✓ Use strip().lower() on both sides.
✗ Mixing input/print into the checking logic — then you cannot test it.
✓ Keep check_answer pure (no input/print) so it is testable.

4 Test & Prove Each Part

We test the engine’s logic — filtering and answer-checking — without running an interactive quiz.

Filtering returns only the chosen category
A correct answer is accepted (ignoring case/spaces)
A wrong answer is rejected
Pythontest_quiz_engine.py
def filter_by_category(questions, category):
    return [q for q in questions if q["category"] == category]


def check_answer(question, given):
    return given.strip().lower() == question["answer"].strip().lower()


QUESTIONS = [
    {"category": "math", "question": "2+2?", "answer": "4", "options": ["3", "4"]},
    {"category": "geo", "question": "Capital of France?", "answer": "Paris", "options": ["Paris", "Rome"]},
]


def test_filter():
    """Filtering to 'math' returns one question."""
    assert len(filter_by_category(QUESTIONS, "math")) == 1


def test_correct_answer():
    """A correct answer (any case) is accepted."""
    assert check_answer(QUESTIONS[1], " paris ") is True


def test_wrong_answer():
    """A wrong answer is rejected."""
    assert check_answer(QUESTIONS[0], "5") is False

Run with pytest -v. Because the engine’s logic is in small functions, we test filtering and checking without any typing. This is how you test an interactive app: isolate the logic from the input/output.

5 The Interface

INPUTquestions.jsoncategory, question, options, answer
What it expects
[{"category": "math", "question": "2+2?",
  "options": ["3", "4"], "answer": "4"}]
OUTPUTscorecorrect out of total
What it returns
2+2?
  1. 3
  2. 4
Your answer: 4
Correct!
Score: 1 / 1

6 Run It & Automate It

Run it locally
python3 quiz_engine.py
Create a questions.json file, pick a category, and take the quiz.

Jenkins runs the tests automatically — each line explained below.

What you should see when it works
Terminala real run
2+2?
  1. 3
  2. 4
Your answer: 4
Correct!

Score: 1 / 1
If it breaks — how to fix it
🚨 KeyError loading questions.
A question is missing a field (category/question/options/answer). Check the JSON structure.
🚨 Correct answers marked wrong.
Clean both sides with strip().lower() before comparing.
GroovyJenkinsfile
// Jenkinsfile — automated tests on every change.
pipeline {
    agent any
    stages {
        stage('Get the code') { steps { checkout scm } }       // download the code
        stage('Install') { steps { sh 'python3 -m venv venv && . venv/bin/activate && pip install pytest' } }  // test tool
        stage('Test') { steps { sh '. venv/bin/activate && pytest -v' } }   // run every test
    }
    post {
        success { echo 'All tests passed.' }
        failure { echo 'A test failed — look above.' }
    }
}
🎯 Try this next — make it yours
  1. Shuffle questions. Randomise order each run. (Teaches: random.shuffle.)
  2. Timed mode. Limit seconds per question. (Teaches: timing.)
  3. High scores. Save best scores per category. (Teaches: persistence.)
What you learned
You learned to separate an engine from its content — questions live in data, the code just runs them — plus filtering and isolating logic for testability. This data-driven design is what makes software reusable and editable by non-programmers. Related: json, Comprehensions.