diff --git a/docs/development/debugging.md b/docs/development/debugging.md
index 9d207b8..372bcc8 100644
--- a/docs/development/debugging.md
+++ b/docs/development/debugging.md
@@ -9,7 +9,7 @@ sidebar_position: 0
GraphQL will execute sibling fields asynchronously during normal operation. This includes multiple top-level controller action calls. However, during a debugging session, having multiple fields trying to resolve themselves can play havoc with your debug cursor. If you've ever encountered a situation where the yellow line in Visual Studio seemly jumps around to random lines of code then you've experienced this issue.
-At startup, it can help to disable asynchronous field resolution and instead force each field to execute in sequential order awaiting its completion before beginning the next one. Don't forget to disable this in production though, as awaiting fields individually will _**significantly**_ impact performance.
+At startup, it can help to disable asynchronous field resolution and instead force each field to execute in sequential order awaiting its completion before beginning the next one.
```csharp title="Configure Debug Mode"
services.AddGraphQL(options =>
@@ -18,3 +18,6 @@ services.AddGraphQL(options =>
options.ExecutionOptions.DebugMode = true;
});
```
+:::danger Performance Killer
+Don't forget to disable debug mode in production though. Awaiting fields individually will _**significantly**_ impact performance.
+:::
\ No newline at end of file
diff --git a/docs/development/unit-testing.md b/docs/development/unit-testing.md
index 0352d4a..6adefa7 100644
--- a/docs/development/unit-testing.md
+++ b/docs/development/unit-testing.md
@@ -5,21 +5,42 @@ sidebar_label: Unit Testing
sidebar_position: 1
---
-GraphQL ASP.NET has more than `3500 unit tests and 91% code coverage`. Much of this is powered by a test component designed to quickly build a configurable, fully mocked server instance to perform a query. It may be helpful to download the code and extend it for harnessing your own controllers.
+.NET 6+
+
+
-The `TestServerBuilder` can be found in the `graphql-aspnet-testframework` project of the primary repo and is dependent on `Moq`. As its part of the core library solution you'll want to remove the project reference to `graphql-aspnet` project and instead add a reference to the nuget package.
+:::info
+Your test projects must target .NET 6 or greater to use the test framework
+:::
+
+GraphQL ASP.NET has more than `3500 unit tests and 91% code coverage`. All the internal integration tests are powered by a framework designed to quickly build a configurable, fully mocked server instance to perform a query against the runtime. It may be helpful to use and extend the framework to test your own controllers.
This document explains how to perform some common test functions for your own controller methods.
+
+## Install the Framework
+
+Add a reference to the [Nuget Package](https://www.nuget.org/packages/GraphQL.AspNet.TestFramework) `GraphQL.AspNet.TestFramework` to your unit test project. The framework is just a class library and not dependent on any individual testing framework like NUnit or XUnit. However, it does mock some runtime only objects and, as a result, is dependent on [Moq](https://www.nuget.org/packages/Moq).
+
+```powershell title="Install the Test Framework"
+# Using the dotnet CLI
+> dotnet add package GraphQL.AspNet.TestFramework
+
+# Using Package Manager Console
+> Install-Package GraphQL.AspNet.TestFramework
+```
+
+
+
## Create a Test Server
-1. Create a new instance of the `TestServerBuilder`. The builder takes in a set of flags to perform some auto configurations for common scenarios such as exposing exceptions or altering the casing of graph type names.
+1. Create a new instance of the `TestServerBuilder`. The builder takes in an optional set of flags to perform some auto configurations for common scenarios such as exposing exceptions or altering the casing of graph type names.
2. Configure your test scenario
- Use `.User` to add any permissions to the mocked user account
- Use `.Authorization` to add any security policy definitions if you wish to test security
- Use `.AddGraphQL()` to mimic the functionality of schema configuration used when your application starts.
- - The `TestServerBuilder` implements `IServiceCollection`, add any additional mocked services as needed to ensure your controllers are wired up correctly by the runtime.
+ - `TestServerBuilder` implements `IServiceCollection`. Add any additional mocked services as needed to ensure your controllers are wired up correctly by the runtime.
3. Build the server instance using `.Build()`
@@ -27,21 +48,30 @@ This document explains how to perform some common test functions for your own co
[Test]
public async Task MyController_InvocationTest()
{
+ // Arrange
var builder = new TestServerBuilder();
builder.AddGraphQL(o => {
o.AddController();
});
+ // Act
var server = builder.Build();
- //...
+
+ // continued...
}
```
+:::tip Custom Schemas
+Use `TestServerBuild` to test against a custom defined schema instance.
+:::
+
## Execute a Query
-1. Mock the query execution context (the object that the runtime acts on) using `.CreateQueryContextBuilder()`
-2. Configure the text, variables etc. on the builder.
+Follow these steps to execute a query against the runtime. If your controller is registered to the test server and the appropriate field is requested in the query, it will be invoked.
+
+1. Create a builder to generate a mocked execution context (the object that the runtime acts on) using `.CreateQueryContextBuilder()`
+2. Configure the query text, variables etc. on the builder.
3. Build the context and submit it for processing:
- Use `server.ExecuteQuery()` to process the context. `context.Result` will be filled with the final `IQueryExecutionResult` which can be inspected for resultant data fields and error messages.
- Use `server.RenderResult()` to generate the json string a client would recieve if they performed the query.
@@ -50,13 +80,20 @@ public async Task MyController_InvocationTest()
```csharp title="Executing a Test Query"
[Test]
public async Task MyController_InvocationTest()
-{
- // ...
+{
+ // Arrange
+ var builder = new TestServerBuilder();
+ builder.AddGraphQL(o => {
+ o.AddController();
+ });
+
var server = builder.Build();
var contextBuilder = server.CreateQueryContextBuilder();
contextBuilder.AddQueryText("query { controller { actionMethod { property1 } } }");
var context = contextBuilder.Build();
+
+ // Act
var result = await server.RenderResult(context);
/* result contains the string for:
@@ -71,5 +108,83 @@ public async Task MyController_InvocationTest()
}
*/
}
+```
+
+## Other Test Scenarios
+
+### Throwing Exceptions
+If you need to test that your controller throws an appropriate exception you can inspect the response object (instead of rendering a result). The exception will be attached to an error message generated during the query execution.
+
+```csharp title="Testing for Thrown Exceptions"
+[Test]
+public async Task MyController_InvocationTest()
+{
+ // Arrange
+ var builder = new TestServerBuilder();
+ builder.AddGraphQL(o => {
+ o.AddController();
+ });
+
+ var server = builder.Build();
+ var contextBuilder = server.CreateQueryContextBuilder();
+ contextBuilder.AddQueryText("query { controller { actionMethod { property1 } } }");
+
+ var context = contextBuilder.Build();
+
+ // Act
+ // Use ExecuteQuery instead of RenderResult to obtain the response object
+ // highlight-next-line
+ var result = await server.ExecuteQuery(queryBuilder);
+
+ // Assert
+ // ensure a message was captured
+ Assert.IsNotNull(result);
+ Assert.AreEqual(1, result.Messages.Count);
+ Assert.IsInstanceOf(typeof(MyException), result.Messages[0].Exception);
+}
+```
+
+:::tip
+Use `server.ExecuteQuery` to obtain a reference to the response object. This allows you to interrogate the message data before its rendered as a json document.
+:::
+
+
+### Authn & Authz
+
+Test authentication and authorization scenarios by configuring both the policies the server should support and the claims or roles the user will present during the test.
+
+```csharp
+[Test]
+public async Task WhenUserHasPolicy_ThenAllowExecution()
+{
+ // Arrange
+ var builder = new TestServerBuilder();
+ builder.AddGraphQL(o => {
+ o.AddController();
+ });
+ // configure the policies the server will recognize
+ // and the claims the user context will have.
+ // This specific test assumes that the controller method
+ // defines an authorization requirement of "policy1".
+ // highlight-start
+ builder.Authorization.AddClaimPolicy("policy1", "myClaimType", "myClaimValue");
+ builder.UserContext.AddUserClaim("myClaimType", "myClaimValue")
+ // highlight-end
+
+ var server = builder.Build();
+ var contextBuilder = server.CreateQueryContextBuilder();
+ contextBuilder.AddQueryText("query { controller { actionMethod { property1 } } }");
+
+ var context = contextBuilder.Build();
+
+ // Act
+ var result = await server.RenderResult(queryBuilder);
+
+ // Assert
+ // ....
+}
```
+
+The user context is always injected when you run a query on the test server. By default it is an anonymous user and credentials are applied when you add a claim or policy to the context during setup.
+
diff --git a/docs/server-extensions/multipart-requests.md b/docs/server-extensions/multipart-requests.md
index 89ebba3..508259b 100644
--- a/docs/server-extensions/multipart-requests.md
+++ b/docs/server-extensions/multipart-requests.md
@@ -448,7 +448,7 @@ A bitwise flag enumeration allowing the inclusion of different types of values f
mpOptions.RequestMode = MultipartRequestMode.Default;
```
-A bitwise flag enumeration that controls which actions the multi-part request extension. By default, both batch queries and file uploads are enabled.
+A bitwise flag enumeration that controls which parts of the multi-part request extension are enabled. By default, both batch queries and file uploads are enabled.
| Option | Description |
| ------------- | ----------------- |
@@ -508,7 +508,7 @@ mpOptions.RequireMultipartRequestHttpProcessor = true;
| ------------- | ----------------- |
| `true` | `true`, `false` |
-Determines if, when starting up the application, the extension will check that the required http processor is registered. When set to true, if the required processor is not registered and configuration exception will be thrown and the server will fail to start. This can be helpful when registering multiple extensions to ensure that a valid processor is registered such that multipart form requests will be handled correctly.
+Determines if, when starting up the application, the extension will check that the required http processor is registered. When set to true, if the required processor is not registered a configuration exception will be thrown and the server will fail to start. This can be helpful when registering multiple extensions to ensure that a valid processor is registered such that multipart-form requests will be handled correctly.
## Demo Project
See the [demo projects](../reference/demo-projects.md) for a sample project utilizing [jaydenseric's apollo-upload-client](https://github.com/jaydenseric/apollo-upload-client) as a front end for performing file uploads against this extension.
\ No newline at end of file