DeepEval: Unit Testing and Evaluation for LLM Applications

# DeepEval: Unit Testing dan Evaluasi untuk Aplikasi LLM Membangun aplikasi berbasis Large Language Model (LLM) seperti chatbot RAG, asisten AI, atau pipeline generatif bukan hanya soal membuat model...

By Ruby Abdullah · · tutorial
DeepEvalLLM TestingEvaluationUnit TestPython

DeepEval: Unit Testing and Evaluation for LLM Applications

Building applications powered by Large Language Models (LLMs) such as RAG chatbots, AI assistants, or generative pipelines isn't just about making the model work. The real challenge is ensuring output quality consistently. How do you know your chatbot isn't hallucinating? How do you measure answer relevancy to user questions? How do you detect bias or toxic content in responses?

DeepEval is an open-source framework designed specifically to answer all these questions. DeepEval allows you to write unit tests for LLM applications just like you write unit tests for traditional software, complete with proven evaluation metrics and pytest integration.

What Is DeepEval?

DeepEval is a Python framework for testing and evaluating LLM applications. The framework provides:

  • Unit Testing for LLMs: Write test cases that validate LLM output with measurable metrics
  • 14+ Built-in Metrics: Hallucination, Answer Relevancy, Faithfulness, Bias, Toxicity, and more
  • Pytest Integration: Run LLM evaluations like regular test suites
  • Custom Metrics: Create evaluation metrics tailored to your business needs
  • Conversational Metrics: Evaluate multi-turn conversation quality
  • Benchmarks: Test your model against standard benchmarks like MMLU and HellaSwag
  • Confident AI Dashboard: Monitoring and analytics platform for evaluation results
  • CI/CD Ready: Integrate evaluations into your deployment pipeline

Installation

Basic Installation

pip install deepeval

Installation with Additional Dependencies

# For all features including benchmarks

pip install deepeval[benchmark]

For Confident AI integration

pip install deepeval[dashboard]

Initial Setup

After installation, login to Confident AI (optional but recommended for tracking):

deepeval login

Or set the API key manually:

export DEEPEVALAPIKEY="your-api-key-here"

Make sure you also have an API key for the LLM used as evaluator:

export OPENAIAPIKEY="your-openai-api-key"

Core Concept: LLMTestCase

LLMTestCase is the fundamental unit in DeepEval. Each test case represents a single interaction with an LLM that you want to evaluate.
from deepeval.testcase import LLMTestCase

testcase = LLMTestCase(

input="What is the capital of Indonesia?",

actualoutput="The capital of Indonesia is Jakarta. However, the Indonesian government is building a new capital called Nusantara (IKN) in East Kalimantan.",

expectedoutput="Jakarta (with note about IKN Nusantara as the new capital)",

retrievalcontext=[

"Jakarta has been the capital of Indonesia since independence in 1945.",

"Nusantara (IKN) is Indonesia's capital relocation project to East Kalimantan, inaugurated in 2024."

]

)

Key LLMTestCase parameters:

| Parameter | Description | Required |

|-----------|-------------|----------|

| input | The question or prompt given to the LLM | Yes |

| actualoutput | The actual response from the LLM | Yes |

| expectedoutput | The expected response (ground truth) | Depends on metric |

| retrievalcontext | Context retrieved from knowledge base | Depends on metric |

| context | Ground truth context (ideal) | Depends on metric |

Built-in Evaluation Metrics

DeepEval provides various ready-to-use evaluation metrics. Here are the key metrics explained:

1. Hallucination Metric

Measures whether the LLM output contains information not supported by the given context.

from deepeval.metrics import HallucinationMetric

from deepeval.testcase import LLMTestCase

metric = HallucinationMetric(threshold=0.5)

testcase = LLMTestCase(

input="Tell me about PT ABC company",

actualoutput="PT ABC was founded in 1990 in Jakarta. The company has 5000 employees and operates in the technology sector.",

context=[

"PT ABC was founded in 1990 in Jakarta.",

"PT ABC operates in the information technology sector.",

"PT ABC has approximately 2000 employees."

]

)

metric.measure(testcase)

print(f"Score: {metric.score}") # 0.0 - 1.0 (lower is better)

print(f"Reason: {metric.reason}") # Explanation of why the score was given

In the example above, the LLM stated "5000 employees" while the context mentions "2000 employees", resulting in a hallucination.

2. Answer Relevancy Metric

Measures how relevant the LLM's answer is to the given question.

from deepeval.metrics import AnswerRelevancyMetric

metric = AnswerRelevancyMetric(threshold=0.7)

testcase = LLMTestCase(

input="How do I install Python on Ubuntu?",

actualoutput="To install Python on Ubuntu, run: sudo apt update && sudo apt install python3. You can also use pyenv to manage multiple Python versions.",

retrievalcontext=[

"Python can be installed on Ubuntu using the apt package manager.",

"Pyenv is a tool for managing multiple Python versions."

]

)

metric.measure(testcase)

print(f"Score: {metric.score}")

print(f"Reason: {metric.reason}")

3. Faithfulness Metric

Measures whether the LLM output is faithful to the retrieved context. Unlike the hallucination metric, faithfulness focuses more on whether claims in the output are supported by retrieval context.

from deepeval.metrics import FaithfulnessMetric

metric = FaithfulnessMetric(threshold=0.7)

testcase = LLMTestCase(

input="What are the benefits of regular exercise?",

actualoutput="Regular exercise can improve heart health, reduce diabetes risk, and boost mood.",

retrievalcontext=[

"Research shows regular exercise improves cardiovascular health.",

"Physical activity helps regulate blood sugar levels and reduces risk of type 2 diabetes.",

"Exercise releases endorphins that can improve mood."

]

)

metric.measure(testcase)

print(f"Score: {metric.score}")

4. Bias Metric

Detects whether the LLM output contains bias related to gender, race, religion, or other groups.

from deepeval.metrics import BiasMetric

metric = BiasMetric(threshold=0.5)

testcase = LLMTestCase(

input="Who is suitable to become a software engineer?",

actualoutput="Anyone can become a software engineer, regardless of gender, age, or background. What matters most is willingness to learn, problem-solving ability, and dedication to continuous growth."

)

metric.measure(testcase)

print(f"Score: {metric.score}") # Low score = not biased (good)

5. Toxicity Metric

Detects toxic, offensive, or harmful content in LLM output.

from deepeval.metrics import ToxicityMetric

metric = ToxicityMetric(threshold=0.5)

testcase = LLMTestCase(

input="Give feedback on this code",

actualoutput="Your code is fairly good, but there are some areas for improvement: 1) Use consistent naming conventions, 2) Add error handling, 3) Separate business logic from controllers."

)

metric.measure(testcase)

print(f"Score: {metric.score}") # Low score = not toxic (good)

Running Tests with Pytest

One of DeepEval's key strengths is its integration with pytest. You can run LLM evaluations as a test suite.

File Structure

project/

├── tests/

│ ├── testchatbot.py

│ ├── testragpipeline.py

│ └── conftest.py

├── src/

│ └── chatbot.py

└── pyproject.toml

Writing Test Files

# tests/testchatbot.py

import pytest

from deepeval import asserttest

from deepeval.testcase import LLMTestCase

from deepeval.metrics import (

AnswerRelevancyMetric,

FaithfulnessMetric,

HallucinationMetric,

)

Import your chatbot function

from src.chatbot import getchatbotresponse

def generatetestcase(question: str) -> LLMTestCase:

"""Helper to create test case from chatbot."""

response = getchatbotresponse(question)

return LLMTestCase(

input=question,

actualoutput=response["answer"],

retrievalcontext=response["sources"],

context=response.get("groundtruthcontext", [])

)

def testanswerrelevancy():

testcase = generatetestcase("What is the company refund policy?")

metric = AnswerRelevancyMetric(threshold=0.7)

asserttest(testcase, [metric])

def testfaithfulness():

testcase = generatetestcase("How do I apply for leave?")

metric = FaithfulnessMetric(threshold=0.7)

asserttest(testcase, [metric])

def testnohallucination():

testcase = generatetestcase("What is the minimum employee salary?")

metric = HallucinationMetric(threshold=0.5)

asserttest(testcase, [metric])

def testmultiplemetrics():

"""Test with multiple metrics at once."""

testcase = generatetestcase("What are the employee health benefits?")

metrics = [

AnswerRelevancyMetric(threshold=0.7),

FaithfulnessMetric(threshold=0.7),

HallucinationMetric(threshold=0.5),

]

asserttest(testcase, metrics)

Running Tests

# Run all tests

deepeval test run tests/

Run specific test file

deepeval test run tests/testchatbot.py

Run with verbose output

deepeval test run tests/testchatbot.py -v

Run and send results to Confident AI

deepeval test run tests/testchatbot.py --confident

You can also use pytest directly:

pytest tests/testchatbot.py -v

Custom Metrics

When built-in metrics aren't enough, you can create custom metrics tailored to your business needs.

Custom Metric with LLM as Judge

from deepeval.metrics import GEval

from deepeval.testcase import LLMTestCase, LLMTestCaseParams

Create custom metric to measure answer completeness

completenessmetric = GEval(

name="Completeness",

criteria="Evaluate whether the response comprehensively addresses all aspects of the user's question. A complete response should cover all key points without leaving important information out.",

evaluationparams=[

LLMTestCaseParams.INPUT,

LLMTestCaseParams.ACTUALOUTPUT,

],

threshold=0.7,

)

testcase = LLMTestCase(

input="Explain the steps to create a REST API with FastAPI",

actualoutput="To create a REST API with FastAPI: 1) Install FastAPI and uvicorn, 2) Create main.py file, 3) Define routes with decorators, 4) Run with uvicorn."

)

completenessmetric.measure(testcase)

print(f"Completeness Score: {completenessmetric.score}")

print(f"Reason: {completenessmetric.reason}")

Custom Metric with Custom Logic

from deepeval.metrics import BaseMetric

from deepeval.testcase import LLMTestCase

class ResponseLengthMetric(BaseMetric):

"""Metric to ensure response has appropriate length."""

def init(self, minwords: int = 50, maxwords: int = 500, threshold: float = 0.5):

self.threshold = threshold

self.minwords = minwords

self.maxwords = maxwords

def measure(self, testcase: LLMTestCase) -> float:

wordcount = len(testcase.actualoutput.split())

if self.minwords <= wordcount <= self.maxwords:

self.score = 1.0

self.reason = f"Response length ({wordcount} words) is within acceptable range."

elif wordcount < self.minwords:

self.score = wordcount / self.minwords

self.reason = f"Response too short ({wordcount} words). Minimum: {self.minwords}."

else:

self.score = self.maxwords / wordcount

self.reason = f"Response too long ({wordcount} words). Maximum: {self.maxwords}."

self.success = self.score >= self.threshold

return self.score

def issuccessful(self) -> bool:

return self.success

@property

def name(self):

return "Response Length"

Conversational Metrics

For chatbot applications, DeepEval provides specialized metrics that evaluate multi-turn conversation quality.

from deepeval.testcase import ConversationalTestCase, LLMTestCase

from deepeval.metrics import ConversationRelevancyMetric

Simulate multi-turn conversation

conversation = ConversationalTestCase(

turns=[

LLMTestCase(

input="Hello, I'd like to ask about your products",

actualoutput="Hello! Of course, I'd be happy to help. Which product would you like to know about?"

),

LLMTestCase(

input="How much is the Enterprise plan?",

actualoutput="Our Enterprise plan starts at $3,000 per month. This package includes unlimited user access, 24/7 dedicated support, custom integration, and 99.9% SLA."

),

LLMTestCase(

input="Is there a free trial?",

actualoutput="Yes, we offer a 14-day free trial for the Enterprise plan. You can sign up on our website without a credit card."

),

]

)

metric = ConversationRelevancyMetric(threshold=0.7)

metric.measure(conversation)

print(f"Conversation Relevancy: {metric.score}")

Benchmarks: MMLU and HellaSwag

DeepEval allows you to test models against standard benchmarks to measure general capabilities.

MMLU (Massive Multitask Language Understanding)

MMLU tests model understanding across 57 subjects, from STEM to humanities.

from deepeval.benchmarks import MMLU

from deepeval.benchmarks.tasks import MMLUTask

Evaluate on specific subjects

benchmark = MMLU(

tasks=[

MMLUTask.COMPUTERSCIENCE,

MMLUTask.MATHEMATICS,

MMLUTask.PHYSICS,

],

nshots=5 # few-shot examples

)

Run benchmark on your model

benchmark.evaluate(model=yourmodel)

HellaSwag

HellaSwag tests model ability in commonsense reasoning by completing scenarios.

from deepeval.benchmarks import HellaSwag

benchmark = HellaSwag(

nshots=10

)

Run benchmark

benchmark.evaluate(model=yourmodel)

Confident AI Dashboard

Confident AI is the companion platform for DeepEval that provides a visual dashboard for monitoring and analytics.

Setting Up Confident AI

# Login to Confident AI

deepeval login

Run tests and send results to dashboard

deepeval test run tests/ --confident

Key Dashboard Features

  • Test Run History: View all test run results with time trends
  • Metric Breakdown: Detailed per-metric analysis with score distributions
  • Failure Analysis: Identify failed test cases and their reasons
  • Comparison: Compare performance across models or prompt versions
  • Dataset Management: Manage golden datasets for consistent evaluation
  • Collaboration: Share results with team through shared workspaces

Sending Results Programmatically

import deepeval

from deepeval.testcase import LLMTestCase

from deepeval.metrics import AnswerRelevancyMetric

from deepeval.dataset import EvaluationDataset

Create evaluation dataset

dataset = EvaluationDataset(

testcases=[

LLMTestCase(

input="What is machine learning?",

actualoutput="Machine learning is a branch of AI that enables computers to learn from data without being explicitly programmed.",

retrievalcontext=["Machine learning is a subset of artificial intelligence."]

),

# ... more test cases

]

)

Evaluate and send to Confident AI

results = deepeval.evaluate(

testcases=dataset.testcases,

metrics=[AnswerRelevancyMetric(threshold=0.7)],

)

CI/CD Integration

Integrate DeepEval into your CI/CD pipeline to ensure LLM quality is maintained with every change.

GitHub Actions

# .github/workflows/llm-tests.yml

name: LLM Evaluation

on:

push:

branches: [main]

pullrequest:

branches: [main]

jobs:

evaluate:

runs-on: ubuntu-latest

steps:

  • uses: actions/checkout@v4
  • name: Setup Python
uses: actions/setup-python@v5

with:

python-version: "3.11"

  • name: Install dependencies
run: |

pip install -r requirements.txt

pip install deepeval

  • name: Run LLM evaluations
env:

OPENAIAPIKEY: ${{ secrets.OPENAIAPIKEY }}

DEEPEVALAPIKEY: ${{ secrets.DEEPEVALAPIKEY }}

run: |

deepeval test run tests/ --confident

  • name: Check evaluation results
if: failure()

run: echo "LLM evaluation failed. Check Confident AI dashboard for details."

GitLab CI

# .gitlab-ci.yml

llm-evaluation:

stage: test

image: python:3.11

script:

  • pip install -r requirements.txt
  • pip install deepeval
  • deepeval test run tests/ --confident
variables:

OPENAIAPIKEY: $OPENAIAPIKEY

DEEPEVALAPIKEY: $DEEPEVALAPIKEY

only:

  • main
  • mergerequests

Practical Example: Test Suite for a RAG Chatbot

Let's build a complete test suite for a RAG chatbot that answers questions about company policies.

Project Structure

rag-chatbot/

├── src/

│ ├── chatbot.py

│ ├── retriever.py

│ └── config.py

├── tests/

│ ├── conftest.py

│ ├── testhallucination.py

│ ├── testrelevancy.py

│ └── testcomprehensive.py

├── data/

│ └── testgoldendataset.json

└── requirements.txt

Golden Dataset

[

{

"input": "How long is the probation period for new employees?",

"expectedoutput": "The probation period for new employees is 3 months.",

"context": [

"Every new employee undergoes a 3-month probation period.",

"During probation, employees receive weekly evaluations from their supervisor."

]

},

{

"input": "What is the procedure for annual leave?",

"expectedoutput": "Annual leave is submitted through the HR system at least 3 days in advance and must be approved by the direct supervisor.",

"context": [

"Annual leave requests are made through the online HR system.",

"Employees must submit leave requests at least 3 working days in advance.",

"Leave approval is given by the employee's direct supervisor."

]

},

{

"input": "Does the company provide health insurance?",

"expectedoutput": "Yes, the company provides health insurance for permanent employees and their immediate family.",

"context": [

"The company provides health insurance for all permanent employees.",

"Insurance covers the employee, spouse, and up to 3 children.",

"Probationary employees do not yet receive insurance benefits."

]

}

]

Conftest for Shared Fixtures

# tests/conftest.py

import json

import pytest

from deepeval.testcase import LLMTestCase

def loadgoldendataset():

with open("data/testgoldendataset.json", "r") as f:

return json.load(f)

def simulatechatbotresponse(question: str, context: list[str]) -> str:

"""

Simulate RAG chatbot response.

In real implementation, this would call your chatbot.

"""

from src.chatbot import getresponse

return getresponse(question)

@pytest.fixture

def goldendataset():

return loadgoldendataset()

@pytest.fixture

def testcases(goldendataset):

cases = []

for item in goldendataset:

response = simulatechatbotresponse(item["input"], item["context"])

cases.append(

LLMTestCase(

input=item["input"],

actualoutput=response,

expectedoutput=item["expectedoutput"],

context=item["context"],

retrievalcontext=item["context"],

)

)

return cases

Hallucination Tests

# tests/testhallucination.py

import pytest

from deepeval import asserttest

from deepeval.metrics import HallucinationMetric

class TestHallucination:

"""Test suite for detecting chatbot hallucinations."""

@pytest.fixture

def metric(self):

return HallucinationMetric(

threshold=0.5,

model="gpt-4o",

)

def testprobationnohallucination(self, testcases, metric):

"""Ensure probation answer doesn't hallucinate."""

testcase = testcases[0]

asserttest(testcase, [metric])

def testleavenohallucination(self, testcases, metric):

"""Ensure leave policy answer doesn't hallucinate."""

testcase = testcases[1]

asserttest(testcase, [metric])

def testinsurancenohallucination(self, testcases, metric):

"""Ensure insurance answer doesn't hallucinate."""

testcase = testcases[2]

asserttest(testcase, [metric])

Relevancy Tests

# tests/testrelevancy.py

import pytest

from deepeval import asserttest

from deepeval.metrics import AnswerRelevancyMetric, FaithfulnessMetric

class TestRelevancy:

"""Test suite for measuring chatbot answer relevancy."""

@pytest.fixture

def relevancymetric(self):

return AnswerRelevancyMetric(threshold=0.7)

@pytest.fixture

def faithfulnessmetric(self):

return FaithfulnessMetric(threshold=0.7)

def testanswersarerelevant(self, testcases, relevancymetric):

"""All answers must be relevant to the question."""

for testcase in testcases:

asserttest(testcase, [relevancymetric])

def testanswersarefaithful(self, testcases, faithfulnessmetric):

"""All answers must be faithful to the context."""

for testcase in testcases:

asserttest(testcase, [faithfulnessmetric])

Comprehensive Tests

# tests/testcomprehensive.py

import pytest

from deepeval import asserttest

from deepeval.metrics import (

AnswerRelevancyMetric,

FaithfulnessMetric,

HallucinationMetric,

BiasMetric,

ToxicityMetric,

)

from deepeval.metrics import GEval

from deepeval.testcase import LLMTestCaseParams

class TestComprehensive:

"""Comprehensive test suite with all metrics."""

@pytest.fixture

def allmetrics(self):

return [

AnswerRelevancyMetric(threshold=0.7),

FaithfulnessMetric(threshold=0.7),

HallucinationMetric(threshold=0.5),

BiasMetric(threshold=0.5),

ToxicityMetric(threshold=0.5),

GEval(

name="Helpfulness",

criteria="Evaluate whether the response is helpful and actionable for the user.",

evaluationparams=[

LLMTestCaseParams.INPUT,

LLMTestCaseParams.ACTUALOUTPUT,

],

threshold=0.7,

),

]

def testfullevaluation(self, testcases, allmetrics):

"""Full evaluation of each test case with all metrics."""

for testcase in testcases:

asserttest(testcase, allmetrics)

Running the Test Suite

# Run all tests

deepeval test run tests/ -v

Run only hallucination tests

deepeval test run tests/test_hallucination.py -v

Run and send to Confident AI

deepeval test run tests/ --confident

Run with pytest

pytest tests/ -v --tb=short

Tips and Best Practices

  • Start with the most critical metrics: For RAG chatbots, prioritize Hallucination and Faithfulness before other metrics.
  • Create representative golden datasets: Collect real user questions and create accurate ground truths.
  • Set realistic thresholds: Don't be too strict initially. Start with a 0.5 threshold and gradually increase as you improve.
  • Use CI/CD for regression: Ensure every change to prompts or models doesn't degrade quality.
  • Monitor trends on the dashboard: Use Confident AI to view quality trends over time.
  • Combine metrics: A single metric isn't enough. Use a combination of metrics for comprehensive evaluation.
  • Test edge cases: Add test cases for difficult scenarios like ambiguous questions, out-of-domain questions, or adversarial inputs.
  • Conclusion

    DeepEval brings the software testing paradigm to the LLM world, enabling development teams to ensure AI output quality systematically and measurably. With a rich set of built-in metrics, seamless pytest integration, and the Confident AI dashboard for monitoring, DeepEval becomes an essential tool for every team building LLM applications in production.

    Start by identifying the most relevant metrics for your use case, create golden datasets from real user interactions, and integrate evaluations into your CI/CD pipeline. With this approach, you can build LLM applications that don't just work, but work with consistent and measurable quality.

    Related Articles

    Inspect AI: The LLM Evaluation Framework from the UK AI Safety Institute

    Inspect AI: Framework Evaluasi LLM dari UK AI Safety Institute yang Wajib Kamu Coba Temen-temen, kalau kamu udah mulai s...

    Complete Braintrust Tutorial: Evaluate, Test, and Improve Your LLM Applications

    Tutorial Lengkap Braintrust: Evaluasi, Testing, dan Improve Aplikasi LLM Halo temen-temen, di tutorial kali ini aku mau ...

    RAGAS: Evaluation Framework for RAG Pipelines

    RAGAS: Framework Evaluasi untuk Pipeline RAG Pendahuluan Retrieval-Augmented Generation (RAG) telah menjadi arsitektur s...

    MergeKit Tutorial: Merging LLM Models to Create Custom AI Models

    Tutorial MergeKit: Menggabungkan Model LLM untuk Membuat Model AI Kustom Model bahasa besar (LLM) open-source terus berk...