diff --git a/src/HotChocolate/Core/HotChocolate.Core.sln b/src/HotChocolate/Core/HotChocolate.Core.sln
index ec3f6f79706..91d726854d1 100644
--- a/src/HotChocolate/Core/HotChocolate.Core.sln
+++ b/src/HotChocolate/Core/HotChocolate.Core.sln
@@ -63,6 +63,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Validation.Ben
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Utilities.Introspection", "..\Utilities\src\Utilities.Introspection\HotChocolate.Utilities.Introspection.csproj", "{39592692-847D-49F0-97DE-3F2F21677319}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Tests.Utilities", "test\Utilities\HotChocolate.Tests.Utilities.csproj", "{E2574A41-D9BF-4071-84D0-DB0F6D1148EE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -385,6 +387,18 @@ Global
{39592692-847D-49F0-97DE-3F2F21677319}.Release|x64.Build.0 = Release|Any CPU
{39592692-847D-49F0-97DE-3F2F21677319}.Release|x86.ActiveCfg = Release|Any CPU
{39592692-847D-49F0-97DE-3F2F21677319}.Release|x86.Build.0 = Release|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Debug|x64.Build.0 = Debug|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Debug|x86.Build.0 = Debug|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Release|x64.ActiveCfg = Release|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Release|x64.Build.0 = Release|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Release|x86.ActiveCfg = Release|Any CPU
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -416,6 +430,7 @@ Global
{C08D0A27-6F28-4845-91E7-5C1A0C926939} = {7462D089-D350-44D6-8131-896D949A65B7}
{44517954-6231-4EE3-9F50-298C0EBFCDF5} = {A1F4B2A9-E0EA-4D8C-B76C-38F034C2E8DC}
{39592692-847D-49F0-97DE-3F2F21677319} = {7637D30E-7339-4D4E-9424-87CF2394D234}
+ {E2574A41-D9BF-4071-84D0-DB0F6D1148EE} = {7462D089-D350-44D6-8131-896D949A65B7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E4D94C77-6657-4630-9D42-0A9AC5153A1B}
diff --git a/src/HotChocolate/Core/test/Utilities/HotChocolate.Tests.Utilities.csproj b/src/HotChocolate/Core/test/Utilities/HotChocolate.Tests.Utilities.csproj
new file mode 100644
index 00000000000..67acc402c67
--- /dev/null
+++ b/src/HotChocolate/Core/test/Utilities/HotChocolate.Tests.Utilities.csproj
@@ -0,0 +1,17 @@
+
+
+
+ enable
+
+
+
+ HotChocolate.Types.Tests
+ HotChocolate
+
+
+
+
+
+
+
+
diff --git a/src/HotChocolate/Core/test/Utilities/SnapshotExtensions.cs b/src/HotChocolate/Core/test/Utilities/SnapshotExtensions.cs
new file mode 100644
index 00000000000..4ce1870be3f
--- /dev/null
+++ b/src/HotChocolate/Core/test/Utilities/SnapshotExtensions.cs
@@ -0,0 +1,24 @@
+using System.Threading.Tasks;
+using HotChocolate.Execution;
+using Snapshooter.Xunit;
+
+namespace HotChocolate.Tests
+{
+ public static class SnapshotExtensions
+ {
+ public static IExecutionResult MatchSnapshot(
+ this IExecutionResult result)
+ {
+ result.ToJson(true).MatchSnapshot();
+ return result;
+ }
+
+ public static async Task MatchSnapshotAsync(
+ this Task task)
+ {
+ IExecutionResult result = await task;
+ result.ToJson(true).MatchSnapshot();
+ return result;
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Utilities/TestConfiguration.cs b/src/HotChocolate/Core/test/Utilities/TestConfiguration.cs
new file mode 100644
index 00000000000..e9e22339db3
--- /dev/null
+++ b/src/HotChocolate/Core/test/Utilities/TestConfiguration.cs
@@ -0,0 +1,12 @@
+using System;
+using HotChocolate.Execution;
+
+namespace HotChocolate.Tests
+{
+ public class TestConfiguration
+ {
+ public ISchema? Schema { get; set; }
+ public IQueryExecutor? Executor { get; set; }
+ public IServiceProvider? Service { get; set; }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Utilities/TestHelper.cs b/src/HotChocolate/Core/test/Utilities/TestHelper.cs
new file mode 100644
index 00000000000..5a07a5b3d25
--- /dev/null
+++ b/src/HotChocolate/Core/test/Utilities/TestHelper.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using HotChocolate.Execution;
+using HotChocolate.StarWars;
+using Xunit;
+
+namespace HotChocolate.Tests
+{
+ public static class TestHelper
+ {
+ public static Task ExpectValid(string query)
+ {
+ return ExpectValid(new TestConfiguration(), query);
+ }
+
+ public static Task ExpectValid(ISchema schema, string query)
+ {
+ return ExpectValid(new TestConfiguration { Schema = schema }, query);
+ }
+
+ public static async Task ExpectValid(
+ TestConfiguration? configuration,
+ string query)
+ {
+ // arrange
+ configuration = new TestConfiguration();
+ configuration.Schema ??= SchemaBuilder.New().AddStarWarsTypes().Create();
+ configuration.Executor ??= configuration.Schema.MakeExecutable();
+ configuration.Service ??=
+ new ServiceCollection().AddStarWarsRepositories().BuildServiceProvider();
+
+ IReadOnlyQueryRequest request =
+ QueryRequestBuilder.New()
+ .SetQuery(query)
+ .SetServices(configuration.Service)
+ .Create();
+
+ // act
+ IExecutionResult result = await configuration.Executor.ExecuteAsync(request, default);
+
+ // assert
+ Assert.Null(result.Errors);
+ return result;
+ }
+
+ public static Task ExpectError(
+ string query,
+ params Action[] elementInspectors)
+ {
+ return ExpectError(new TestConfiguration(), query, elementInspectors);
+ }
+
+ public static Task ExpectError(
+ ISchema schema,
+ string query,
+ params Action[] elementInspectors)
+ {
+ return ExpectError(new TestConfiguration { Schema = schema }, query, elementInspectors);
+ }
+
+ public static async Task ExpectError(
+ TestConfiguration? configuration,
+ string query,
+ params Action[] elementInspectors)
+ {
+ // arrange
+ configuration = new TestConfiguration();
+ configuration.Schema ??= SchemaBuilder.New().AddStarWarsTypes().Create();
+ configuration.Executor ??= configuration.Schema.MakeExecutable();
+ configuration.Service ??=
+ new ServiceCollection().AddStarWarsRepositories().BuildServiceProvider();
+
+ IReadOnlyQueryRequest request =
+ QueryRequestBuilder.New()
+ .SetQuery(query)
+ .SetServices(configuration.Service)
+ .Create();
+
+ // act
+ IExecutionResult result = await configuration.Executor.ExecuteAsync(request, default);
+
+ // assert
+ Assert.NotNull(result.Errors);
+
+ if (elementInspectors.Length > 0)
+ {
+ Assert.Collection(result.Errors, elementInspectors);
+ }
+
+ result.MatchSnapshot();
+ }
+ }
+}