Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce integration testing / spec-first libs with initial support for
xunit
- Loading branch information
1 parent
4caddac
commit ed89d69
Showing
45 changed files
with
3,370 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
src/Swashbuckle.AspNetCore.ApiTesting.Xunit/ApiTestFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Mvc.Testing; | ||
using Microsoft.OpenApi.Models; | ||
using Xunit; | ||
|
||
namespace Swashbuckle.AspNetCore.ApiTesting.Xunit | ||
{ | ||
[Collection("ApiTests")] | ||
public class ApiTestFixture<TEntryPoint> : | ||
IClassFixture<WebApplicationFactory<TEntryPoint>> where TEntryPoint : class | ||
{ | ||
private readonly ApiTestRunnerBase _apiTestRunner; | ||
private readonly WebApplicationFactory<TEntryPoint> _webAppFactory; | ||
private readonly string _documentName; | ||
|
||
public ApiTestFixture( | ||
ApiTestRunnerBase apiTestRunner, | ||
WebApplicationFactory<TEntryPoint> webAppFactory, | ||
string documentName) | ||
{ | ||
_apiTestRunner = apiTestRunner; | ||
_webAppFactory = webAppFactory; | ||
_documentName = documentName; | ||
} | ||
|
||
public void Describe(string pathTemplate, OperationType operationType, OpenApiOperation operationSpec) | ||
{ | ||
_apiTestRunner.ConfigureOperation(_documentName, pathTemplate, operationType, operationSpec); | ||
} | ||
|
||
public async Task TestAsync(string operationId, string expectedStatusCode, HttpRequestMessage request) | ||
{ | ||
await _apiTestRunner.TestAsync( | ||
_documentName, | ||
operationId, | ||
expectedStatusCode, | ||
request, | ||
_webAppFactory.CreateClient()); | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/Swashbuckle.AspNetCore.ApiTesting.Xunit/Swashbuckle.AspNetCore.ApiTesting.Xunit.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<Description>Xunit add-on for Swagger/OpenAPI-driven integration testing API's built on ASP.NET Core</Description> | ||
<TargetFramework>netcoreapp2.1</TargetFramework> | ||
<NoWarn>$(NoWarn);1591</NoWarn> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<AssemblyName>Swashbuckle.AspNetCore.ApiTesting.Xunit</AssemblyName> | ||
<PackageId>Swashbuckle.AspNetCore.ApiTesting.Xunit</PackageId> | ||
<PackageTags>swagger;openapi;test-first;spec-first;testing;aspnetcore;xunit</PackageTags> | ||
<PackageProjectUrl>https://github.com/domaindrivendev/Swashbuckle.AspNetCore</PackageProjectUrl> | ||
<PackageLicenseUrl>https://raw.githubusercontent.com/domaindrivendev/Swashbuckle.AspNetCore/master/LICENSE</PackageLicenseUrl> | ||
<RepositoryType>git</RepositoryType> | ||
<RepositoryUrl>https://github.com/domaindrivendev/Swashbuckle.AspNetCore.git</RepositoryUrl> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Swashbuckle.AspNetCore.ApiTesting\Swashbuckle.AspNetCore.ApiTesting.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="xunit" Version="2.2.0" /> | ||
</ItemGroup> | ||
</Project> |
88 changes: 88 additions & 0 deletions
88
src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using System; | ||
using System.IO; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Microsoft.OpenApi.Models; | ||
using Microsoft.OpenApi.Writers; | ||
|
||
namespace Swashbuckle.AspNetCore.ApiTesting | ||
{ | ||
public abstract class ApiTestRunnerBase : IDisposable | ||
{ | ||
private readonly ApiTestRunnerOptions _options; | ||
private readonly RequestValidator _requestValidator; | ||
private readonly ResponseValidator _responseValidator; | ||
|
||
protected ApiTestRunnerBase() | ||
{ | ||
_options = new ApiTestRunnerOptions(); | ||
_requestValidator = new RequestValidator(_options.ContentValidators); | ||
_responseValidator = new ResponseValidator(_options.ContentValidators); | ||
} | ||
|
||
public void Configure(Action<ApiTestRunnerOptions> setupAction) | ||
{ | ||
setupAction(_options); | ||
} | ||
|
||
public void ConfigureOperation( | ||
string documentName, | ||
string pathTemplate, | ||
OperationType operationType, | ||
OpenApiOperation operation) | ||
{ | ||
var openApiDocument = _options.GetOpenApiDocument(documentName); | ||
|
||
if (openApiDocument.Paths == null) | ||
openApiDocument.Paths = new OpenApiPaths(); | ||
|
||
if (!openApiDocument.Paths.TryGetValue(pathTemplate, out OpenApiPathItem pathItem)) | ||
{ | ||
pathItem = new OpenApiPathItem(); | ||
openApiDocument.Paths.Add(pathTemplate, pathItem); | ||
} | ||
|
||
pathItem.AddOperation(operationType, operation); | ||
} | ||
|
||
public async Task TestAsync( | ||
string documentName, | ||
string operationId, | ||
string expectedStatusCode, | ||
HttpRequestMessage request, | ||
HttpClient httpClient) | ||
{ | ||
var openApiDocument = _options.GetOpenApiDocument(documentName); | ||
if (!openApiDocument.TryFindOperationById(operationId, out string pathTemplate, out OperationType operationType)) | ||
throw new InvalidOperationException($"Operation with id '{operationId}' not found in OpenAPI document '{documentName}'"); | ||
|
||
if (expectedStatusCode.StartsWith("2")) | ||
_requestValidator.Validate(request, openApiDocument, pathTemplate, operationType); | ||
|
||
var response = await httpClient.SendAsync(request); | ||
|
||
_responseValidator.Validate(response, openApiDocument, pathTemplate, operationType, expectedStatusCode); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
if (!_options.GenerateOpenApiFiles) return; | ||
|
||
if (_options.FileOutputRoot == null) | ||
throw new Exception("GenerateOpenApiFiles set but FileOutputRoot is null"); | ||
|
||
foreach (var entry in _options.OpenApiDocs) | ||
{ | ||
var outputDir = Path.Combine(_options.FileOutputRoot, entry.Key); | ||
Directory.CreateDirectory(outputDir); | ||
|
||
using (var streamWriter = new StreamWriter(Path.Combine(outputDir, "openapi.json"))) | ||
{ | ||
var openApiJsonWriter = new OpenApiJsonWriter(streamWriter); | ||
entry.Value.SerializeAsV3(openApiJsonWriter); | ||
streamWriter.Close(); | ||
} | ||
} | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using System.Collections.Generic; | ||
using Microsoft.OpenApi.Models; | ||
|
||
namespace Swashbuckle.AspNetCore.ApiTesting | ||
{ | ||
public class ApiTestRunnerOptions | ||
{ | ||
public ApiTestRunnerOptions() | ||
{ | ||
OpenApiDocs = new Dictionary<string, OpenApiDocument>(); | ||
ContentValidators = new List<IContentValidator> { new JsonContentValidator() }; | ||
GenerateOpenApiFiles = false; | ||
FileOutputRoot = null; | ||
} | ||
|
||
public Dictionary<string, OpenApiDocument> OpenApiDocs { get; } | ||
|
||
public List<IContentValidator> ContentValidators { get; } | ||
|
||
public bool GenerateOpenApiFiles { get; set; } | ||
|
||
public string FileOutputRoot { get; set; } | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptionsExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using Microsoft.OpenApi.Models; | ||
using Microsoft.OpenApi.Readers; | ||
using System; | ||
using System.IO; | ||
|
||
namespace Swashbuckle.AspNetCore.ApiTesting | ||
{ | ||
public static class ApiTestRunnerOptionsExtensions | ||
{ | ||
public static void AddOpenApiFile(this ApiTestRunnerOptions options, string documentName, string filePath) | ||
{ | ||
using (var fileStream = File.OpenRead(filePath)) | ||
{ | ||
var openApiDocument = new OpenApiStreamReader().Read(fileStream, out OpenApiDiagnostic diagnostic); | ||
options.OpenApiDocs.Add(documentName, openApiDocument); | ||
} | ||
} | ||
|
||
public static OpenApiDocument GetOpenApiDocument(this ApiTestRunnerOptions options, string documentName) | ||
{ | ||
if (!options.OpenApiDocs.TryGetValue(documentName, out OpenApiDocument document)) | ||
throw new InvalidOperationException($"Document with name '{documentName}' not found"); | ||
|
||
return document; | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/Swashbuckle.AspNetCore.ApiTesting/HttpHeadersExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
using System.Collections.Specialized; | ||
using System.Net.Http.Headers; | ||
|
||
namespace Swashbuckle.AspNetCore.ApiTesting | ||
{ | ||
public static class HttpHeadersExtensions | ||
{ | ||
internal static NameValueCollection ToNameValueCollection(this HttpHeaders httpHeaders) | ||
{ | ||
var headerNameValues = new NameValueCollection(); | ||
foreach (var entry in httpHeaders) | ||
{ | ||
headerNameValues.Add(entry.Key, string.Join(',', entry.Value)); | ||
} | ||
return headerNameValues; | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/Swashbuckle.AspNetCore.ApiTesting/IContentValidator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System; | ||
using System.Net.Http; | ||
using Microsoft.OpenApi.Models; | ||
|
||
namespace Swashbuckle.AspNetCore.ApiTesting | ||
{ | ||
public interface IContentValidator | ||
{ | ||
bool CanValidate(string mediaType); | ||
|
||
void Validate(OpenApiMediaType mediaTypeSpec, OpenApiDocument openApiDocument, HttpContent content); | ||
} | ||
|
||
public class ContentDoesNotMatchSpecException : Exception | ||
{ | ||
public ContentDoesNotMatchSpecException(string message) | ||
: base(message) | ||
{ } | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/Swashbuckle.AspNetCore.ApiTesting/JsonContentValidator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Net.Http; | ||
using Microsoft.OpenApi.Models; | ||
using Newtonsoft.Json.Linq; | ||
|
||
namespace Swashbuckle.AspNetCore.ApiTesting | ||
{ | ||
public class JsonContentValidator : IContentValidator | ||
{ | ||
private readonly JsonValidator _jsonValidator; | ||
|
||
public JsonContentValidator() | ||
{ | ||
_jsonValidator = new JsonValidator(); | ||
} | ||
|
||
public bool CanValidate(string mediaType) | ||
{ | ||
return mediaType.Contains("json"); | ||
} | ||
|
||
public void Validate(OpenApiMediaType mediaTypeSpec, OpenApiDocument openApiDocument, HttpContent content) | ||
{ | ||
if (mediaTypeSpec?.Schema == null) return; | ||
|
||
var instance = JToken.Parse(content.ReadAsStringAsync().Result); | ||
if (!_jsonValidator.Validate(mediaTypeSpec.Schema, openApiDocument, instance, out IEnumerable<string> errorMessages)) | ||
throw new ContentDoesNotMatchSpecException(string.Join(Environment.NewLine, errorMessages)); | ||
} | ||
} | ||
} |
Oops, something went wrong.