Class AiServiceInvocationAssert
- All Implemented Interfaces:
org.assertj.core.api.Assert<AiServiceInvocationAssert, AiServiceInvocation>, org.assertj.core.api.Descriptable<AiServiceInvocationAssert>, org.assertj.core.api.ExtensionPoints<AiServiceInvocationAssert, AiServiceInvocation>
Judge.
Capture the invocation with AiServiceCapture, which works with any AI Service method:
import static dev.langchain4j.test.LangChain4jAssertions.assertThat;
AiServiceCapture capture = AiServiceCapture.capture(() -> assistant.chat("Cancel my booking MS-777"));
assertThat(capture)
.hasToolsExecutedExactlyInAnyOrder("getBookingDetails", "cancelBooking")
.hasToolExecutedWith("cancelBooking", Map.of("bookingNumber", "MS-777"))
.hasNoFailedToolExecutions();
This works for every return type of the AI Service method, also Result<...>: capture the call.
hasToolExecuted(String), hasToolExecutedTimes(String, int),
hasToolsExecutedInOrder(String...), hasToolsExecutedExactlyInAnyOrder(String...) and
both hasToolExecutedWith(...) count only the tool executions that succeeded. A tool that threw an
exception counts as a failed execution, also when the AI Service sent the error to the model and then succeeded:
check it with
hasToolExecutionFailed(String) or hasNoFailedToolExecutions(). They fail if the AI Service did not
report whether its tool executions failed (see ToolExecutedEvent.isError()).
When the invocation started other AI Service invocations (for example an AI Service called by one of its tools, see
AiServiceInvocation.nestedInvocations()), say which ones to check first: includingNested() checks
the whole tree, topLevelOnly() only what the invocation did itself. Without it, the assertions on tools,
sources and model calls fail, because both readings are plausible and a wrong one could hide a
problem: a sub-agent that ran a forbidden tool, or called the model many times. An invocation without nested invocations needs
neither.
Like Mockito's verifyNoMoreInteractions, hasNoMoreToolExecutions() checks that every tool
execution was checked by a tool assertion before, also by an earlier assertThat(...) on the same capture:
assertThat(capture).hasToolExecutedWith("cancelBooking", Map.of("bookingNumber", "MS-777"));
assertThat(capture).hasToolExecuted("getBookingDetails").hasNoMoreToolExecutions();
When an assertion fails, the message names the AI Service method and lists all executed tools with their
arguments and results.
All assertions fail if the invocation was cancelled. If the invocation failed, they fail until
hasFailedWithMessageContaining(String) or hasFailedWith(Class) has checked the failure; after
it, they check what happened before the failure.
These assertions need the langchain4j module, where AI Services are defined. langchain4j-test
does not bring it in. If the compiler reports that a class of dev.langchain4j.service is not found, add it,
with the same version as your other LangChain4j modules (managed by langchain4j-bom if you import it):
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
-
Field Summary
Fields inherited from class org.assertj.core.api.AbstractAssert
actual, info, myself, objects, throwUnsupportedExceptionOnEquals -
Constructor Summary
ConstructorsConstructorDescriptionCreates assertions on the given AI Service invocation. -
Method Summary
Modifier and TypeMethodDescriptionorg.assertj.core.api.ListAssert<String> Returns assertions on the names of all executed tools, in the order of execution, for exampleexecutedToolNames().containsExactly("getBookingDetails", "cancelBooking").hasFailedWith(Class<? extends Throwable> type) Asserts that the invocation failed with an error of the given type, or with an error caused by one.Asserts that the invocation failed with an error whose message, or the message of one of its causes, contains the given text (case-sensitive).hasFinishReason(FinishReason finishReason) Asserts that the final response of this invocation finished for the given reason.hasModelCallCount(int expectedCount) Asserts that exactly the given number of model calls were made (seeAiServiceInvocation.modelCalls()): those of the AI Service to its own chat model and those of the components it runs, of every model type, also calls that failed.hasModelCallCount(Class<? extends ModelCall> type, int expectedCount) Asserts that exactly the given number of calls were made to models of the given type (seeAiServiceInvocation.modelCalls()), also calls that failed.Asserts that no tool execution failed (seeToolExecution.hasFailed()).Asserts that every tool execution was checked by one of the tool assertions above, called before on the same invocation, like Mockito'sverifyNoMoreInteractions: the assertions add up across separateassertThat(...)statements on the sameAiServiceCapture.Asserts that no sources were retrieved for retrieval-augmented generation (RAG), for example for a question that the retrieved knowledge should not answer.Asserts that no tool was executed, not even one that failed.hasOwnChatModelCallCount(int expectedCount) Asserts that the AI Service called its own chat model exactly the given number of times (seeAiServiceInvocation.ownChatModelCalls()), also calls that failed.hasOwnChatModelCallCountAtMost(int maxCount) Asserts that the AI Service called its own chat model at most the given number of times (seeAiServiceInvocation.ownChatModelCalls()), also calls that failed, for example to check that the AI Service does not call tools in a loop.Asserts that at least one retrieved source (seeAiServiceInvocation.sources()) contains the given text (case-sensitive).Asserts that the invocation succeeded: it neither failed nor was cancelled.hasToolExecuted(String toolName) Asserts that the tool with the given name was executed successfully at least once.hasToolExecutedTimes(String toolName, int times) Asserts that the tool with the given name was executed successfully exactly the given number of times.Asserts that the tool with the given name was executed successfully at least once with arguments that pass the given requirements.hasToolExecutedWith(String toolName, Map<String, ?> expectedArguments) Asserts that the tool with the given name was executed successfully at least once with arguments that contain all the given arguments.hasToolExecutionFailed(String toolName) Asserts that the tool with the given name was executed at least once and that this execution failed: the tool threw an exception, or its arguments were invalid.hasToolsExecutedExactlyInAnyOrder(String... toolNames) Asserts that exactly the given tools were executed successfully, in any order, and no other tools: a tool that is not given must not have been executed at all, not even in an execution that failed.hasToolsExecutedInOrder(String... toolNames) Asserts that the given tools were executed successfully in the given order.Makes the assertions that follow check the whole tree of invocations: this invocation and all the invocations it started, directly or not (seeAiServiceInvocation.nestedInvocations()).org.assertj.core.api.ListAssert<ToolExecution> Returns assertions on the tool executions, in the order of execution.Makes the assertions that follow check only what this invocation did itself, not the invocations it started (seeAiServiceInvocation.nestedInvocations()).Methods inherited from class org.assertj.core.api.AbstractObjectAssert
as, as, doesNotReturn, extracting, extracting, extracting, extracting, extracting, extracting, extractingForProxy, getComparatorsByType, hasAllNullFieldsOrProperties, hasAllNullFieldsOrPropertiesExcept, hasFieldOrProperty, hasFieldOrPropertyWithValue, hasNoNullFieldsOrProperties, hasNoNullFieldsOrPropertiesExcept, hasOnlyFields, isEqualToComparingFieldByField, isEqualToComparingFieldByFieldRecursively, isEqualToComparingOnlyGivenFields, isEqualToIgnoringGivenFields, isEqualToIgnoringNullFields, newObjectAssert, returns, usingComparatorForFields, usingComparatorForType, usingRecursiveAssertion, usingRecursiveAssertion, usingRecursiveComparison, usingRecursiveComparisonMethods inherited from class org.assertj.core.api.AbstractAssert
actual, areEqual, asInstanceOf, asList, assertionError, asString, describedAs, descriptionText, doesNotHave, doesNotHaveSameClassAs, doesNotHaveSameHashCodeAs, doesNotHaveToString, doesNotHaveToString, doesNotMatch, doesNotMatch, equals, extracting, extracting, failure, failureWithActualExpected, failWithActualExpectedAndMessage, failWithMessage, getWritableAssertionInfo, has, hashCode, hasSameClassAs, hasSameHashCodeAs, hasToString, hasToString, inBinary, inHexadecimal, is, isElementOfCustomAssert, isEqualTo, isExactlyInstanceOf, isIn, isIn, isInstanceOf, isInstanceOfAny, isInstanceOfSatisfying, isNot, isNotEqualTo, isNotExactlyInstanceOf, isNotIn, isNotIn, isNotInstanceOf, isNotInstanceOfAny, isNotNull, isNotOfAnyClassIn, isNotSameAs, isNull, isOfAnyClassIn, isSameAs, matches, matches, newListAssertInstance, overridingErrorMessage, overridingErrorMessage, satisfies, satisfies, satisfies, satisfiesAnyOf, satisfiesAnyOf, satisfiesAnyOfForProxy, satisfiesForProxy, setCustomRepresentation, setDescriptionConsumer, setPrintAssertionsDescription, throwAssertionError, usingComparator, usingComparator, usingDefaultComparator, usingEquals, usingEquals, withFailMessage, withFailMessage, withRepresentation, withThreadDumpOnErrorMethods inherited from class Object
clone, finalize, getClass, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface org.assertj.core.api.Descriptable
as, describedAs, describedAs
-
Constructor Details
-
AiServiceInvocationAssert
Creates assertions on the given AI Service invocation. PreferLangChain4jAssertions.assertThat(AiServiceInvocation).
-
-
Method Details
-
includingNested
Makes the assertions that follow check the whole tree of invocations: this invocation and all the invocations it started, directly or not (seeAiServiceInvocation.nestedInvocations()). Their tool executions, sources and model calls add up. -
topLevelOnly
Makes the assertions that follow check only what this invocation did itself, not the invocations it started (seeAiServiceInvocation.nestedInvocations()). -
hasNoToolExecutions
Asserts that no tool was executed, not even one that failed. -
hasToolExecuted
Asserts that the tool with the given name was executed successfully at least once. Marks all its successful executions as checked (seehasNoMoreToolExecutions()). -
hasToolExecutionFailed
Asserts that the tool with the given name was executed at least once and that this execution failed: the tool threw an exception, or its arguments were invalid. Use it to check how the AI Service handles a failing tool. Marks all failed executions of the tool as checked (seehasNoMoreToolExecutions()). -
hasToolExecutedTimes
Asserts that the tool with the given name was executed successfully exactly the given number of times.0checks that it was not executed successfully. Marks its successful executions as checked (seehasNoMoreToolExecutions()). -
hasToolsExecutedInOrder
Asserts that the given tools were executed successfully in the given order. Other tools may have been executed before, between or after them, for examplehasToolsExecutedInOrder("getBookingDetails", "cancelBooking")checks that the booking was looked up before it was cancelled. When the model asked for several tools in one response (parallel tool calls), their order is the order in which the model listed them: it does not show that one tool used the result of the other. To check that exactly these tools were executed, in this order, useexecutedToolNames().containsExactly(...)(this includes failed executions). Marks the executions that matched as checked (seehasNoMoreToolExecutions()).- Throws:
IllegalStateException- afterincludingNested(): the order of the tool executions of different invocations is not known
-
hasToolsExecutedExactlyInAnyOrder
Asserts that exactly the given tools were executed successfully, in any order, and no other tools: a tool that is not given must not have been executed at all, not even in an execution that failed. Failed executions of the given tools are not counted: check them withhasNoFailedToolExecutions(). A tool executed several times must be given as many times:hasToolsExecutedExactlyInAnyOrder("getWeather", "getWeather")passes only whengetWeatherwas executed twice and no other tool was executed. To also check the order, useexecutedToolNames().containsExactly(...)(this includes failed executions). Marks all successful executions as checked (seehasNoMoreToolExecutions()). -
hasToolExecutedWith
public AiServiceInvocationAssert hasToolExecutedWith(String toolName, Map<String, ?> expectedArguments) Asserts that the tool with the given name was executed successfully at least once with arguments that contain all the given arguments. Other arguments are ignored. Values are compared as JSON:Map.of("count", 3)matches an argument"count": 3as well as"count": 3.0. Nested objects and arrays must match exactly. A number or a boolean sent as a string matches too, as the tool receives it converted to the type of its parameter:Map.of("count", 3)matches"count": "3", andMap.of("urgent", true)matches"urgent": "true". Text must match exactly: when a real model can write a value in several ways, usehasToolExecutedWith(String, Consumer).The argument names are the names of the tool method's parameters. Compile with
-parametersor name the parameters with@P(name = ...), otherwise they arearg0,arg1and so on.Marks the matching executions as checked (see
hasNoMoreToolExecutions()). -
hasToolExecutedWith
public AiServiceInvocationAssert hasToolExecutedWith(String toolName, Consumer<Map<String, Object>> argumentsRequirements) Asserts that the tool with the given name was executed successfully at least once with arguments that pass the given requirements. Use it when a real model can write an argument in several ways, for example"Munich"or"München", so thathasToolExecutedWith(String, Map)would fail for a correct call. Accept only what your tool can really handle: a looser check can hide a call that the tool cannot process.
The requirements receive the arguments as the model sent them, parsed from JSON: a number is aassertThat(capture).hasToolExecutedWith("getWeather", arguments -> { assertThat(arguments.get("city")).isIn("Munich", "München"); Judge.from(judgeModel).assertThat(arguments.get("country").toString()).satisfies("refers to Germany"); });Number, a nested object aMapand an array aList. They run once for every successful execution of the tool, and an execution passes unless they throw anAssertionError. Any other exception is rethrown, for example when a judge cannot give a verdict.Marks the executions that passed as checked (see
hasNoMoreToolExecutions()). -
hasNoMoreToolExecutions
Asserts that every tool execution was checked by one of the tool assertions above, called before on the same invocation, like Mockito'sverifyNoMoreInteractions: the assertions add up across separateassertThat(...)statements on the sameAiServiceCapture. Use it to check that the AI Service executed no other tools than the expected ones, also with other arguments or failing. -
hasNoFailedToolExecutions
Asserts that no tool execution failed (seeToolExecution.hasFailed()). Fails if the AI Service did not report whether its tool executions failed (seeToolExecutedEvent.isError()). A tool that throws an exception, or anAssertionErrorin a test, counts as a failed tool execution when the AI Service sends the error to the model instead of failing. Whether it does depends on the kind of AI Service method and on its tool execution error handler (seeAiServices.toolExecutionErrorHandler(ToolExecutionErrorHandler)). -
hasRetrievedSourceContaining
Asserts that at least one retrieved source (seeAiServiceInvocation.sources()) contains the given text (case-sensitive). Fails if no sources were captured. -
hasNoRetrievedSources
Asserts that no sources were retrieved for retrieval-augmented generation (RAG), for example for a question that the retrieved knowledge should not answer. Fails if the AI Service has no retrieval augmentor or did not report what it retrieved (seeAiServiceInvocation.sources()). -
hasOwnChatModelCallCount
Asserts that the AI Service called its own chat model exactly the given number of times (seeAiServiceInvocation.ownChatModelCalls()), also calls that failed. For example, an AI Service that executes tools once calls its chat model twice: once for the tool calls and once more for the final answer. To also count the calls of the components the AI Service runs (for example an embedding or a moderation model), usehasModelCallCount(int). -
hasOwnChatModelCallCountAtMost
Asserts that the AI Service called its own chat model at most the given number of times (seeAiServiceInvocation.ownChatModelCalls()), also calls that failed, for example to check that the AI Service does not call tools in a loop. -
hasFinishReason
Asserts that the final response of this invocation finished for the given reason. For example,FinishReason.LENGTHmeans that the response was cut off because it reached the maximum number of output tokens, sohasFinishReason(FinishReason.STOP)checks that the response is complete. It is about the final answer of this invocation, so nested invocations are never checked. -
hasModelCallCount
Asserts that exactly the given number of model calls were made (seeAiServiceInvocation.modelCalls()): those of the AI Service to its own chat model and those of the components it runs, of every model type, also calls that failed. To count only the calls of the AI Service to its own chat model, usehasOwnChatModelCallCount(int). -
hasModelCallCount
public AiServiceInvocationAssert hasModelCallCount(Class<? extends ModelCall> type, int expectedCount) Asserts that exactly the given number of calls were made to models of the given type (seeAiServiceInvocation.modelCalls()), also calls that failed. For example,hasModelCallCount(EmbeddingModelCall.class, 1)checks that the query was embedded once.- Parameters:
type- the type of model call, for exampleChatModelCall.classorEmbeddingModelCall.class
-
hasSucceeded
Asserts that the invocation succeeded: it neither failed nor was cancelled. Other assertions check this too; this one is for when nothing else is checked, for example for a streaming method whose failure the test would otherwise not notice. Nested invocations are not checked: a failure of a nested invocation that the AI Service handled (for example a tool that sent the error to the model) does not make this invocation fail. -
hasFailedWithMessageContaining
Asserts that the invocation failed with an error whose message, or the message of one of its causes, contains the given text (case-sensitive). The assertions that follow it check what happened before the failure, for example which tools were executed. -
hasFailedWith
Asserts that the invocation failed with an error of the given type, or with an error caused by one. The assertions that follow it check what happened before the failure, for example which tools were executed. -
executedToolNames
Returns assertions on the names of all executed tools, in the order of execution, for exampleexecutedToolNames().containsExactly("getBookingDetails", "cancelBooking"). Unlike thehasTool...assertions, this includes the executions that failed: addhasNoFailedToolExecutions()if they must all have succeeded. AfterincludingNested(), the names of the nested invocations come after those of this invocation. -
toolExecutions
Returns assertions on the tool executions, in the order of execution. AfterincludingNested(), the executions of the nested invocations come after those of this invocation.- Throws:
AssertionError- if the AI Service did not report whether its tool executions failed (seeAiServiceInvocation.toolExecutions())
-