为集成添加标准测试
在创建自定义类(无论是自己使用还是发布到 LangChain 集成中)时,添加标准测试以确保其按预期工作非常重要。本指南将展示如何为每种集成类型添加标准测试。
设置
首先,让我们安装 2 个依赖项:
langchain-core将定义我们要导入以定义自定义工具的接口。langchain-tests将提供我们要使用的标准测试,以及运行它们所需的 pytest 插件。建议固定到最新版本:
由于 langchain-tests 新版本中添加的测试可能会破坏您的 CI/CD 管道,因此我们建议固定 langchain-tests 的版本以避免意外更改。
- Poetry
- Pip
如果您已按照上一指南进行操作,应该已经安装了这些依赖项!
poetry add langchain-core
poetry add --group test langchain-tests==<latest_version>
poetry install --with test
pip install -U langchain-core langchain-tests
# 以可编辑模式安装当前包
pip install --editable .
添加和配置标准测试
langchain-tests 包中有 2 个命名空间:
- 单元测试(
langchain_tests.unit_tests):设计用于独立测试组件,无需访问外部服务。 - 集成测试(
langchain_tests.integration_tests):设计用于测试组件,允许访问外部服务(特别是组件旨在交互的外部服务)。
这两种测试都实现为pytest 基于类的测试套件。
通过继承每种标准测试类型的基类(如下所示),您可以获得该类型的所有标准测试,并且可以覆盖测试套件用于配置测试的属性。
为了以与本指南相同的方式运行测试,我们建议在两个测试子目录下的测试文件中继承这些类:
tests/unit_tests用于单元测试tests/integration_tests用于集成测试
实现标准测试
在以下选项卡中,我们展示了如何为每种组件类型实现标准测试:
- Chat models
- Vector stores
- Embeddings
- Tools
- Retrievers
要为聊天模型配置标准测试,我们继承 ChatModelUnitTests 和 ChatModelIntegrationTests。在每个子类上,我们覆盖以下 @property 方法来指定要测试的聊天模型及其配置:
| Property | Description |
|---|---|
chat_model_class | 要测试的聊天模型类 |
chat_model_params | 要传递给聊天模型构造函数的参数 |
此外,聊天模型标准测试会测试一系列行为,从最基本的要求(生成查询响应)到多模态支持和工具调用等可选功能。为了使测试运行成功:
- 如果模型旨在支持某项功能,则应通过测试;
- 如果模型不支持某项功能,则应跳过测试。
“可选”功能的测试通过可以覆盖测试模型子类上的一组属性来控制。
您可以在 API 参考中找到所有可配置功能的列表: 单元测试 和 集成测试。
例如,要为图像输入启用集成测试,我们可以在集成测试类中实现:
@property
def supports_image_inputs(self) -> bool:
return True
有关运行哪些测试、如何跳过每个测试以及每个测试的故障排除技巧的详细信息,请参阅 API 参考。请参阅详细信息:
单元测试示例:
"""Test chat model integration."""
from typing import Type
from langchain_parrot_link.chat_models import ChatParrotLink
from langchain_tests.unit_tests import ChatModelUnitTests
class TestChatParrotLinkUnit(ChatModelUnitTests):
@property
def chat_model_class(self) -> Type[ChatParrotLink]:
return ChatParrotLink
@property
def chat_model_params(self) -> dict:
# These should be parameters used to initialize your integration for testing
return {
"model": "bird-brain-001",
"temperature": 0,
"parrot_buffer_length": 50,
}
集成测试示例:
"""Test ChatParrotLink chat model."""
from typing import Type
from langchain_parrot_link.chat_models import ChatParrotLink
from langchain_tests.integration_tests import ChatModelIntegrationTests
class TestChatParrotLinkIntegration(ChatModelIntegrationTests):
@property
def chat_model_class(self) -> Type[ChatParrotLink]:
return ChatParrotLink
@property
def chat_model_params(self) -> dict:
# These should be parameters used to initialize your integration for testing
return {
"model": "bird-brain-001",
"temperature": 0,
"parrot_buffer_length": 50,
}
以下是如何为典型的向量商店配置标准测试(使用 ParrotVectorStore 作为占位符):
目前,向量存储测试没有可以配置的可选功能。
from typing import Generator
import pytest
from langchain_parrot_link.vectorstores import ParrotVectorStore
from langchain_core.vectorstores import VectorStore
from langchain_tests.integration_tests import VectorStoreIntegrationTests
class TestParrotVectorStore(VectorStoreIntegrationTests):
@pytest.fixture()
def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore
"""Get an empty vectorstore for unit tests."""
store = ParrotVectorStore(self.get_embeddings())
# note: store should be EMPTY at this point
# if you need to delete data, you may do so here
try:
yield store
finally:
# cleanup operations, or deleting data
pass
配置测试包括实现用于设置空向量存储和在测试运行结束后清理向量存储的 pytest 夹具。
| Fixture | Description |
|---|---|
vectorstore | 一个生成器,用于为单元测试生成一个空向量存储。向量存储将在测试运行结束后清理。 |
例如,以下是 Chroma 集成的 VectorStoreIntegrationTests 类:
from typing import Generator
import pytest
from langchain_core.vectorstores import VectorStore
from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests
from langchain_chroma import Chroma
class TestChromaStandard(VectorStoreIntegrationTests):
@pytest.fixture()
def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore
"""为单元测试获取一个空的 vectorstore。"""
store = Chroma(embedding_function=self.get_embeddings())
try:
yield store
finally:
store.delete_collection()
pass
请注意,在第一次 yield 之前,我们使用嵌入模型对象实例化了向量存储。这是一个预定义的“虚假”嵌入模型,用于为文档生成简短的任意向量。您可以根据需要使用不同的嵌入对象。
在 finally 块中,我们调用任何特定于集成的逻辑来将向量存储置于干净状态。此逻辑在每个测试之间执行(例如,即使测试失败)。
有关运行哪些测试以及每个测试的故障排除技巧的详细信息,请参阅API 参考。
要为嵌入模型配置标准测试,我们继承 EmbeddingsUnitTests 和 EmbeddingsIntegrationTests。在每个子类上,我们覆盖以下 @property 方法来指定要测试的嵌入模型及其配置:
| Property | Description |
|---|---|
embeddings_class | 要测试的嵌入模型类 |
embedding_model_params | 要传递给嵌入模型构造函数的参数 |
有关运行哪些测试、如何跳过每个测试以及每个测试的故障排除技巧的详细信息,请参阅 API 参考。请参阅详细信息:
单元测试示例:
"""Test embedding model integration."""
from typing import Type
from langchain_parrot_link.embeddings import ParrotLinkEmbeddings
from langchain_tests.unit_tests import EmbeddingsUnitTests
class TestParrotLinkEmbeddingsUnit(EmbeddingsUnitTests):
@property
def embeddings_class(self) -> Type[ParrotLinkEmbeddings]:
return ParrotLinkEmbeddings
@property
def embedding_model_params(self) -> dict:
return {"model": "nest-embed-001"}
集成测试示例:
from typing import Type
from langchain_parrot_link.embeddings import ParrotLinkEmbeddings
from langchain_tests.integration_tests import EmbeddingsIntegrationTests
class TestParrotLinkEmbeddingsIntegration(EmbeddingsIntegrationTests):
@property
def embeddings_class(self) -> Type[ParrotLinkEmbeddings]:
return ParrotLinkEmbeddings
@property
def embedding_model_params(self) -> dict:
return {"model": "nest-embed-001"}
"""Test ParrotLink embeddings."""
from typing import Type
from langchain_parrot_link.embeddings import ParrotLinkEmbeddings
from langchain_tests.integration_tests import EmbeddingsIntegrationTests
class TestParrotLinkEmbeddingsIntegration(EmbeddingsIntegrationTests):
@property
def embeddings_class(self) -> Type[ParrotLinkEmbeddings]:
return ParrotLinkEmbeddings
@property
def embedding_model_params(self) -> dict:
return {"model": "nest-embed-001"}
要为工具配置标准测试,我 们继承 ToolsUnitTests 和 ToolsIntegrationTests。在每个子类上,我们覆盖以下 @property 方法来指定要测试的工具及其配置:
| Property | Description |
|---|---|
tool_constructor | 要测试的工具的构造函数,或已实例化的工具。 |
tool_constructor_params | 要传递给工具的参数(可选)。 |
tool_invoke_params_example | 要传递给工具 invoke 方法的参数示例。 |
如果您正在测试一个工具类并将类(如 MyTool)传递给 tool_constructor,您可以通过 tool_constructor_params 传递构造函数参数。
如果您正在测试一个已实例化的工具,您可以将该已实例化的工具传递给 tool_constructor,并且不需要覆盖 tool_constructor_params。
有关运行哪些测试、如何跳过每个测试以及每个测试的故障排除技巧的详细信息,请参阅 API 参考。请参阅详细信息:
from typing import Type
from langchain_parrot_link.tools import ParrotTool
from langchain_tests.unit_tests import ToolsUnitTests
class TestParrotMultiplyToolUnit(ToolsUnitTests):
@property
def tool_constructor(self) -> Type[ParrotTool]:
return ParrotTool
@property
def tool_constructor_params(self) -> dict:
# if your tool constructor instead required initialization arguments like
# `def __init__(self, some_arg: int):`, you would return those here
# as a dictionary, e.g.: `return {'some_arg': 42}`
return {}
@property
def tool_invoke_params_example(self) -> dict:
"""
Returns a dictionary representing the "args" of an example tool call.
This should NOT be a ToolCall dict - i.e. it should not
have {"name", "id", "args"} keys.
"""
return {"a": 2, "b": 3}
from typing import Type
from langchain_parrot_link.tools import ParrotTool
from langchain_tests.integration_tests import ToolsIntegrationTests
class TestParrotMultiplyToolIntegration(ToolsIntegrationTests):
@property
def tool_constructor(self) -> Type[ParrotTool]:
return ParrotTool
@property
def tool_constructor_params(self) -> dict:
# if your tool constructor instead required initialization arguments like
# `def __init__(self, some_arg: int):`, you would return those here
# as a dictionary, e.g.: `return {'some_arg': 42}`
return {}
@property
def tool_invoke_params_example(self) -> dict:
"""
Returns a dictionary representing the "args" of an example tool call.
This should NOT be a ToolCall dict - i.e. it should not
have {"name", "id", "args"} keys.
"""
return {"a": 2, "b": 3}
要为检索器配置标准测试,我们继承 RetrieversUnitTests 和 RetrieversIntegrationTests。在每个子类上,我们覆盖以下 @property 方法
| Property | Description |
|---|---|
retriever_constructor | 要测试的检索器的类 |
retriever_constructor_params | 要传递给检索器构造函数的参数 |
retriever_query_example | 要传递给检索器 invoke 方法的查询示例 |
有关运行哪些测试以及每个测试的故障排除技巧的详细信息,请参阅API 参考。
from typing import Type
from langchain_parrot_link.retrievers import ParrotRetriever
from langchain_tests.integration_tests import (
RetrieversIntegrationTests,
)
class TestParrotRetriever(RetrieversIntegrationTests):
@property
def retriever_constructor(self) -> Type[ParrotRetriever]:
"""Get an empty vectorstore for unit tests."""
return ParrotRetriever
@property
def retriever_constructor_params(self) -> dict:
return {"k": 2}
@property
def retriever_query_example(self) -> str:
"""
Returns a str representing the "query" of an example retriever call.
"""
return "example query"
运行测试
您可以使用项目根目录下的以下命令运行这些测试:
- Poetry
- Pip
# 在没有网络访问的情况下运行单元测试
poetry run pytest --disable-socket --allow-unix-socket --asyncio-mode=auto tests/unit_tests
# 运行集成测试
poetry run pytest --asyncio-mode=auto tests/integration_tests
# 在没有网络访问的情况下运行单元测试
pytest --disable-socket --allow-unix-socket --asyncio-mode=auto tests/unit_tests
# 运行集成测试
pytest --asyncio-mode=auto tests/integration_tests
测试套件信息和故障排除
有关可用的标准测试套件的完整列表,以及有关包含哪些测试和如何排除常见问题的信息,请参阅标准测试 API 参考。
您可以在 API 参考中列出的各个测试套件下找到故障排除指南。例如,这是 ChatModelIntegrationTests.test_usage_metadata 的故障排除指南。