GUIDE / ai evals
DeepEval Tutorial: Unit Testing for LLM Applications
DeepEval tutorial for unit testing LLM applications with pytest-style metrics, G-Eval rubrics, faithfulness examples, and DeepEval vs Ragas.
This DeepEval tutorial is for QA engineers and developers who want unit testing habits for LLM applications. Classical unit tests assert exact values. LLM features need a different shape of test: an input, an actual model output, optional retrieval context or expected behavior, and metrics that score quality with thresholds you can enforce in CI.
DeepEval is built around that workflow. It helps you express LLM quality checks in a pytest-style manner so prompt edits, model swaps, and chain changes get automatic feedback instead of hallway opinions.
What DeepEval Is and Why It Exists
LLM apps fail in ways that expect(text).toBe(gold) cannot handle well:
- wording changes while meaning stays correct
- answers that sound right but invent facts
- RAG systems that retrieve the wrong context
- agents that call the wrong tool
- subtle tone or policy violations
DeepEval approaches these as evaluation test cases with metrics. Instead of only comparing strings, you score behaviors such as:
- faithfulness to context
- answer relevancy
- similarity to expected output
- custom rubric judgments via G-Eval style metrics
- task-specific checks you compose into a suite
If you need the conceptual map of metric families first, read LLM evaluation metrics. This tutorial focuses on practice.
Core Concepts
Test case
A test case usually includes:
input: the user prompt or requestactual_output: what your app producedexpected_output: optional reference answerretrieval_contextor context list: optional documents for groundedness checks- metadata such as scenario tags
Metric
A metric turns a test case into a score, often between 0 and 1, plus a reason or debug details.
Threshold
You decide what “pass” means. For example, faithfulness must be at least 0.7 on the critical policy set.
Suite
A suite is a collection of cases and metrics that protect a feature or risk area.
Mental Model: Pytest for LLM Outputs
Classic test:
def test_total():
assert cart_total([10, 5]) == 15
LLM-oriented test:
def test_refund_policy_answer():
output = answer_question("What is the refund window?")
# evaluate output with metrics against policy context
assert faithfulness_score(output, context) >= 0.7
assert relevancy_score("What is the refund window?", output) >= 0.7
DeepEval formalizes that second pattern so you are not hand-rolling every judge prompt and score parser from scratch.
Setup Workflow
High-level setup steps:
- Create or open your Python project for the LLM app or eval harness.
- Install DeepEval in the environment your CI will use.
- Configure any model provider keys needed for LLM-as-judge metrics.
- Decide which app entrypoint the tests will call.
- Build a small dataset of inputs and contexts.
- Write one smoke metric test.
- Add thresholds and suite structure.
- Wire a CI job with secrets and cost controls.
Exact package versions change over time, so follow current DeepEval docs for install pins. The testing design in this tutorial stays stable even when APIs get minor renames.
Designing Your First DeepEval Cases
Start with five to fifteen cases, not five hundred.
Good first cases:
- Simple known FAQ with clear source context.
- Question that should refuse when context is insufficient.
- Adversarial prompt that tries to override policy.
- Verbose user question with the same intent as case 1.
- Near-miss retrieval context that is related but wrong.
- Multilingual or messy punctuation if your users write that way.
- Structured output requirement if your app returns JSON.
Each case should have a name, intent, and owner. Treat the dataset as product code.
DeepEval Faithfulness Metric Example
Faithfulness is one of the highest value metrics for RAG and knowledge-grounded assistants.
Scenario:
- User asks about password reset timing.
- Retrieved context says reset links expire in 30 minutes.
- Model answers 24 hours.
A faithfulness metric should score that answer poorly because it contradicts context.
Case design
input: "How long is the password reset link valid?"
retrieval_context:
- "Password reset links expire 30 minutes after they are issued."
actual_output: "Your reset link is valid for 24 hours."
expected metric: faithfulness below threshold
What you are asserting
Not “the wording matches.” You are asserting “claims are supported by context.”
Implementation pattern
# Pseudocode-style example for teaching the pattern
from my_app import answer_with_rag
from eval_harness import LLMTestCase, FaithfulnessMetric, assert_metric
def test_reset_link_faithfulness():
question = "How long is the password reset link valid?"
context = [
"Password reset links expire 30 minutes after they are issued."
]
output = answer_with_rag(question)
case = LLMTestCase(
input=question,
actual_output=output,
retrieval_context=context,
)
metric = FaithfulnessMetric(threshold=0.7)
assert_metric(case, metric)
In real projects, prefer the official DeepEval class names and assert helpers from the version you installed. The pattern above is what matters: app output in, metric threshold enforced, context provided.
Answer Relevancy and Expected Output Checks
Faithfulness alone is not enough. An answer can stick to context and still miss the question.
Add relevancy when:
- users ask multi-intent questions
- models ramble into adjacent topics
- retrieval returns broad documents
Use expected output similarity when:
- there is a stable gold answer
- the task is constrained
- you are regression-testing a carefully edited response style
Avoid forcing exact string equality on open-ended answers unless the product truly requires fixed text.
DeepEval G-Eval Metric Tutorial
G-Eval style metrics shine when quality is rubric-based.
Example criteria for a support assistant:
Score the answer from 0 to 1.
1.0 = fully correct, policy-aligned, clear next step, no unsupported claims.
0.5 = partially helpful but missing a critical detail or mildly unclear.
0.0 = wrong, unsafe, or invents policy.
Deduct heavily if the answer invents deadlines or refund rules.
When to use G-Eval
- tone requirements
- pedagogy quality for tutoring apps
- custom domain correctness not captured by generic metrics
- “did the agent ask a clarifying question when needed?”
When not to use G-Eval alone
- simple schema validation (use deterministic checks)
- exact safety denylist (use explicit rules)
- cheap smoke tests where a smaller metric works
Making G-Eval reliable
- Include few-shot graded examples in the rubric when possible.
- Keep the judge model fixed in CI.
- Store rationales for failed cases to speed debugging.
- Calibrate thresholds against human labels.
- Separate custom rubrics by feature so failures are actionable.
Pytest-Style LLM Evaluation With DeepEval
A maintainable layout:
tests/
eval/
test_support_faithfulness.py
test_support_refusals.py
test_sql_agent_smoke.py
data/
support_cases.jsonl
sql_cases.jsonl
Tips for pytest discipline
- mark expensive evals with
@pytest.mark.eval - run smoke evals on every PR
- run full evals nightly
- fail fast on deterministic checks before calling judges
- cache fixtures where safe
- record model and prompt versions in reports
Example organization:
import pytest
pytestmark = pytest.mark.eval
@pytest.mark.parametrize("case", load_cases("support_cases.jsonl"))
def test_support_suite(case):
output = run_support_bot(case["input"])
evaluate_case(case, output)
Parametrization keeps the dataset data-driven while still producing readable failures.
Comparing Prompt or Model Versions
DeepEval suites are most valuable when they compare variants.
Workflow:
- Freeze a dataset version.
- Run suite on baseline prompt/model.
- Run suite on candidate prompt/model.
- Compare pass rates and score distributions by tag.
- Inspect failures, not only averages.
- Ship only if critical tags pass thresholds and overall deltas are acceptable.
Tags might include billing, auth, safety, multilingual, rag_hard.
DeepEval vs Ragas
Teams often ask which tool to pick.
| Dimension | DeepEval | Ragas |
|---|---|---|
| Primary framing | Unit testing for LLM apps | RAG evaluation toolkit |
| Developer ergonomics | Strong pytest-style testing angle | Strong research/eval pipelines for RAG |
| Metric breadth | Broad app metrics including custom judges | Deep focus on RAG quality metrics |
| Best starting point | App-level regression suites | Retrieval and grounded answer diagnosis |
| Common pairing | Product CI gates | RAG improvement loops |
Practical recommendation:
- Use DeepEval when you want engineer-owned tests around an application.
- Use Ragas when you are deeply iterating retrieval quality and need RAG metric focus.
- Many teams evaluate RAG with both perspectives over time.
For the RAG-centered walkthrough, see RAGAS: Evaluating RAG Pipelines.
Integrating DeepEval Into CI
PR pipeline
- deterministic tests: schema, tool-allow lists, unit logic
- small DeepEval smoke set: 10-30 critical cases
- budget guardrails for judge tokens
Nightly pipeline
- larger regression set
- multi-model comparisons if needed
- publish a simple HTML or markdown report
- open issues for new recurring failures
Secrets and cost control
- store provider keys in CI secrets
- cap max cases on forked PRs if needed
- skip judge metrics on docs-only changes
- pin model names used for judging
Debugging a Failed Metric
When faithfulness fails:
- Read the actual output claims.
- Read the retrieval context.
- Check whether retrieval is wrong or generation is wrong.
- Check if the threshold is unrealistically high for partial answers.
- Check judge or metric configuration changes.
When G-Eval fails:
- Read the rationale.
- Confirm the rubric is unambiguous.
- See if the judge is punishing style rather than substance.
- Compare with a human rating on the same case.
- Adjust rubric or app behavior deliberately, not both at once.
Combining Deterministic Checks With DeepEval Metrics
Hybrid tests are powerful.
def test_create_ticket_tool_agent():
result = run_agent("Create a high priority ticket for login failures")
assert result["tool_calls"][0]["name"] == "create_ticket"
assert result["tool_calls"][0]["args"]["priority"] in {"high", "P1"}
# then graded metric on user-facing confirmation message
assert_relevancy(result["message"], "Create a high priority ticket for login failures")
Deterministic checks catch hard bugs cheaply. Metrics catch language quality and groundedness.
Dataset Hygiene for DeepEval Suites
Your suite is only as good as the cases.
Rules:
- prefer real anonymized traffic over synthetic-only sets
- include expected failure cases, not only happy paths
- refresh quarterly or after major product changes
- keep expected contexts current when docs change
- avoid training or few-shot contamination from test items
- track flaky cases separately
For dataset design depth, use building an LLM eval dataset.
Prompt Regression Testing With DeepEval
Every prompt edit should answer:
- What behavior are we trying to improve?
- Which suite tags should get better?
- Which tags must not get worse?
That is prompt regression testing in practice. DeepEval gives you the measurement harness; the discipline is not shipping prompt diffs without suite evidence. See also prompt regression testing.
Common Mistakes When Using DeepEval
Mistake 1: Thresholds Copied From Blog Posts
Your domain and risk tolerance define thresholds. Calibrate them.
Mistake 2: One Giant Suite With No Tags
When failures happen, you need to know whether billing, safety, or small talk regressed.
Mistake 3: Judging Without Fixed Model Settings
If the judge model or temperature drifts, scores are not comparable.
Mistake 4: No Deterministic Guards
Do not spend judge money to discover invalid JSON if a schema check would catch it instantly.
Mistake 5: Actual Output Not From the Real App Path
Testing a raw model call while production uses a different chain misses retrieval and tool bugs.
Mistake 6: Ignoring Cost and Runtime
A two-hour PR eval gate will be bypassed. Keep smoke suites lean.
Mistake 7: Treating Metrics as Product Truth Without Humans
Spot-check failures and successes. Metrics approximate quality; they do not replace responsibility.
Mistake 8: Freezing a Stale Dataset Forever
Old cases protect old product behavior. Update as docs, policies, and UX change.
Worked Mini-Project Plan
Build a first DeepEval harness in one day:
- Morning: pick 12 support questions and policy contexts.
- Midday: wire app entrypoint and one faithfulness test.
- Afternoon: add relevancy and one G-Eval tone rubric.
- End of day: run baseline scores and set provisional thresholds.
- Next day: add CI smoke job and a failure triage doc.
By the end of two days, you have something far more useful than a slide saying “we will evaluate quality.”
Sample Score Review Table
| Case tag | Baseline pass | Candidate pass | Decision |
|---|---|---|---|
| billing_policy | 9/10 | 10/10 | improved |
| auth_howto | 8/10 | 8/10 | stable |
| safety_refusal | 10/10 | 9/10 | investigate |
| rag_hard | 6/10 | 7/10 | improved |
| tone | 8/10 | 7/10 | product call |
Never ship on an average alone when safety tags drop.
Release Checklist
- Critical smoke eval passes.
- No safety tag regressions.
- Faithfulness threshold holds on policy cases.
- Prompt and model versions recorded.
- Dataset version recorded.
- Judge configuration pinned.
- Cost and latency within budget.
- Failed cases triaged or accepted with rationale.
- Human reviewed a sample of changes for high-risk features.
Flaky Eval Quarantine Policy
Some graded metrics will flake under provider latency or rare judge variance. Use an explicit quarantine policy instead of silent deletions.
Rules that work well:
- A case may be quarantined only with an owner and a written reason.
- Quarantine expires in 14 days unless renewed.
- Safety and auth cases cannot be quarantined without lead approval.
- Quarantined cases still run nightly for visibility, but do not block PRs.
- Every quarantine ends in fix, rewrite, or permanent retirement with rationale.
This keeps CI trustworthy without pretending LLM evaluation is as deterministic as integer arithmetic.
Naming Conventions for Cases and Metrics
Clear names make failures actionable in CI logs.
Prefer:
billing_refund_window_annual_faithfulnessauth_reset_link_expiry_relevancysafety_prompt_injection_ignore_policies
Avoid:
test1case_final_finalmisc_quality
Include the risk domain, scenario, and metric intent. When a nightly job fails, engineers should understand the business meaning before opening the artifact bundle.
Also version dataset files with dates or semantic versions, and record which dataset version produced a given CI report. Otherwise you cannot tell whether the product changed or the golden set changed.
Structured Output Testing Patterns
Many LLM apps must return JSON, markdown tables, or tool argument objects. DeepEval metrics should sit on top of strict structure checks.
Recommended order:
- Parse JSON or schema. Fail immediately if invalid.
- Check required keys and enums deterministically.
- Run faithfulness/relevancy on user-visible fields.
- Run G-Eval only for subjective residual quality.
Example hybrid case ideas:
- support bot returns
{ "reply": "...", "intent": "...", "needs_handoff": true/false } - SQL copilot returns
{ "sql": "...", "warning": "..." } - classifier returns label plus confidence band
This ordering keeps CI costs down and failure messages obvious.
Red-Team Slice Inside DeepEval
Reserve a tagged slice for abuse and policy tests:
- prompt injection attempting to ignore system rules
- requests for other users’ data
- jailbreak-style roleplay
- requests to reveal system prompts or hidden tools
- unsafe instructions relevant to your domain
These cases may use custom G-Eval rubrics or deterministic detectors. Keep them in PR smoke if the feature is user-facing and high risk. Do not bury all safety cases in a monthly report nobody reads.
Synthetic Data vs Production-Derived Cases
DeepEval suites often start synthetic because that is easy. Synthetic cases are useful for:
- bootstrap coverage
- rare safety scenarios
- controlled document contexts
They are weak when:
- real users use slang, typos, and multi-intent questions
- production retrieval corpora differ from toy contexts
- tool-argument quirks only appear in real workflows
A healthy mix:
| Source | Share early | Share mature |
|---|---|---|
| Synthetic targeted cases | High | Medium |
| Anonymized production traces | Low | High |
| Incident regressions | Medium | Medium |
| Red-team adversarial prompts | Medium | Medium |
Whenever a production failure escapes, add a minimized case to the suite within the same sprint if possible.
Local Dev Loop vs CI Loop
Developers need a fast local loop:
- change prompt or chain
- run a focused tag
- inspect three failures
- iterate
CI needs a trustworthy gate:
- smoke tags on PR
- full suite nightly
- published comparison against main
- owners notified on critical regressions
If local runs take as long as CI, people stop running them. Offer:
make eval-smokemake eval-tag TAG=billingmake eval-full
DeepEval is most effective when the commands are obvious and documented in the repo README.
Handling Non-Determinism Without Giving Up
LLM tests will not be as stable as pure function unit tests. That is acceptable if managed.
Tactics:
- lower temperature for eval runs when product allows
- average multiple runs only for research, not usually for PR gates
- keep critical assertions deterministic where possible
- quarantine known flaky cases with owners and expiry dates
- require re-run evidence before blaming infrastructure
Do not disable a safety case because it is inconvenient. Fix the product or improve the case design.
Team Roles Around DeepEval
Suggested ownership:
- Feature engineer: keeps cases for their feature green
- QA / eval owner: suite structure, thresholds, triage rituals
- Platform: CI secrets, cost budgets, report publishing
- Product: severity calls when tone metrics conflict with brevity goals
- Security / trust: adversarial and privacy cases
Without ownership, suites rot. With ownership, DeepEval becomes part of Definition of Done for AI changes.
Example Failure Triage Notes
Case: billing-refund-window-02
Metric: faithfulness 0.42 (threshold 0.70)
Actual: claimed 30-day refunds for monthly plans
Context: only annual plans have 14-day refunds; monthly non-refundable after use
Root cause: generator overgeneralized from adjacent annual policy chunk
Action: tighten prompt grounding; add explicit monthly non-refundable sentence to KB; keep case
Owner: payments-ai
Write triage notes like bug reports. Future you will need them.
Final Takeaways
DeepEval helps you bring software engineering discipline to LLM quality. The winning pattern is not “add a metric library.” The winning pattern is:
- define risks
- encode them as cases and metrics
- threshold them in CI
- debug failures like product defects
- keep humans in the calibration loop
- grow the dataset from production pain
- assign owners so the suite cannot silently rot
Use faithfulness for grounded answers, relevancy for on-topic behavior, G-Eval for custom rubrics, and deterministic tests for hard constraints. Start small, tag everything, and treat eval failures with the same seriousness as failing unit tests, adjusted for probabilistic scoring.
If you want deliberate practice comparing outputs against rubrics, join QABattle and train the evaluation habit in the battle arena before you wire the same judgment style into automated suites.
FAQ
Questions testers ask
What is DeepEval used for?
DeepEval is used to evaluate and unit test LLM application outputs with metrics such as faithfulness, relevancy, and G-Eval style rubric scoring. Teams integrate it into pytest-style suites so prompt, retrieval, and model changes get automated quality feedback in development and CI.
How do you write unit tests for LLM outputs with DeepEval?
Write a normal test that runs your LLM app or a captured output, wrap the interaction in a DeepEval test case with input, actual output, and optional expected output or context, then assert metric scores pass your thresholds. Keep datasets versioned and separate flaky exploratory checks from release gates.
How does DeepEval compare to Ragas?
DeepEval focuses on developer-centric unit testing ergonomics for LLM apps, including broad metric coverage and pytest-style workflows. Ragas focuses deeply on RAG pipeline evaluation metrics such as faithfulness, context precision, and context recall. Many teams use both: Ragas for retrieval quality research and DeepEval for app-level test suites.
What is the G-Eval metric in DeepEval?
G-Eval is an LLM-as-judge style metric that scores outputs against a natural language criteria rubric. You define what good looks like, the judge model evaluates the output, and DeepEval returns a score you can threshold in tests. It is flexible for custom quality dimensions beyond fixed string metrics.
Can DeepEval test RAG faithfulness?
Yes. DeepEval includes faithfulness-style evaluation where the output is checked against retrieval context to catch unsupported claims. Provide the retrieved context in the test case, choose the faithfulness metric, and fail the test when groundedness drops below your threshold.
Should DeepEval tests run on every pull request?
Run a small high-signal smoke eval on every pull request and larger suites nightly or pre-release. LLM metrics can be slower and costlier than classic unit tests, so split critical deterministic checks from broader graded evaluations to keep CI fast and useful.
RELATED GUIDES
Continue the route
LLM Evaluation Metrics: A Practical Guide
A practical guide to LLM evaluation metrics: faithfulness, answer relevancy, BLEU vs semantic scores, LLM-as-judge, and offline vs online eval.
RAGAS: Evaluating RAG Pipelines
Learn Ragas for RAG evaluation: faithfulness, context precision, contextual recall, dataset design, and how to measure retrieval-augmented generation quality.
Building an LLM Eval Dataset: Golden Sets and Rubrics
Learn building an LLM eval dataset with golden sets, rubrics, synthetic data, human labels, edge cases, sizing rules, and versioning for reliable evals.
How to Test Prompts: Prompt Regression Testing
Learn prompt regression testing with golden datasets, versioned prompts, CI checks, scoring strategies, and non-deterministic LLM regression tactics.