Writing Custom Hooks in QaaS for Advanced Testing¶
When the built-in Plugins and packaged hooks do not provide sufficient functionality for your test requirements, custom hooks can be implemented in a C# project to extend QaaS.Framework. This guide demonstrates how to create and integrate custom QaaS.Common.Generators, QaaS.Common.Assertions, and QaaS.Common.Probes style hooks to test a specific condition:
The application receives a JSON array as input and sends a JSON array with the same number of items as output.
1. Creating a Custom Generator: JsonArrayGenerator¶
A generator produces test data for DataSources and implements the IGenerator interface from the QaaS.Framework.SDK package. We extend BaseGenerator<T> with a configuration record that includes validation and default values.
Generator Configuration Record¶
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Nodes;
using QaaS.Framework.SDK.DataSourceObjects;
using QaaS.Framework.SDK.Hooks.Generator;
using QaaS.Framework.SDK.Session.DataObjects;
using QaaS.Framework.SDK.Session.SessionDataObjects;
namespace DummyAppTests;
/// <summary>
/// Configuration for JsonArrayGenerator with required and optional settings.
/// </summary>
public record JsonArrayGeneratorConfiguration
{
[Required]
public uint? Count { get; set; }
public uint NumberOfItemsPerArray { get; set; } = 5;
}
Generator Implementation¶
/// <summary>
/// Generates a specified number of JSON arrays, each containing a configurable number of items.
/// </summary>
public class JsonArrayGenerator : BaseGenerator<JsonArrayGeneratorConfiguration>
{
public override IEnumerable<Data<object>> Generate(
IImmutableList<SessionData> sessionDataList,
IImmutableList<DataSource> dataSourceList)
{
for (var generatedDataIndex = 0; generatedDataIndex < Configuration.Count; generatedDataIndex++)
{
var jsonArray = new JsonArray();
for (var jsonArrayItemIndex = 0; jsonArrayItemIndex < Configuration.NumberOfItemsPerArray; jsonArrayItemIndex++)
jsonArray.Add("SomeItem");
yield return new Data<object>
{
Body = jsonArray
};
}
}
}
File System Structure After Addition¶
DummyAppTests/
|-- DummyAppTests.csproj
|-- Program.cs
|-- JsonArrayGenerator.cs
|-- LengthAssertion.cs
|-- PrintCurrentTimeProbe.cs
|-- test.qaas.yaml
|-- Variables/
| |-- local.yaml
| `-- k8s.yaml
`-- TestData/
2. Configuring the Generator in DataSources¶
Define a DataSource in test.qaas.yaml that uses the custom generator with specific configuration.
DataSources:
- Name: 10Samples
Generator: JsonArrayGenerator
GeneratorConfiguration:
Count: 10
NumberOfItemsPerArray: 5
This creates 10 JSON arrays, each with 5 items.
3. Using the DataSource in a New Session¶
Create a new Session that uses the 10Samples data source. Since the data is JSON, a Serializer must be configured.
Sessions:
- Name: RabbitMqExchangeWith10Samples
Publishers:
- Name: Publisher
DataSourceNames: [10Samples]
Policies:
- LoadBalance:
Rate: 50
RabbitMq:
Host: 127.0.0.1
Username: admin
Password: admin
RoutingKey: /
Port: 5672
ExchangeName: input
Serialize:
Serializer: Json
Consumers:
- Name: Consumer
TimeoutMs: 5000
RabbitMq:
Host: 127.0.0.1
Username: admin
Password: admin
RoutingKey: /
Port: 5672
ExchangeName: output
Deserialize:
Deserializer: Json
4. Adding Common Assertions¶
Apply standard QaaS.Common.Assertions to the new session for hermeticity and delay.
Assertions:
- Name: HermeticByInputOutputPercentage
Assertion: HermeticByInputOutputPercentage
SessionNames: [RabbitMqExchangeWith10Samples]
AssertionConfiguration:
OutputNames: [Consumer]
InputNames: [Publisher]
ExpectedPercentage: 100
- Name: DelayByChunks
Assertion: DelayByChunks
SessionNames: [RabbitMqExchangeWith10Samples]
AssertionConfiguration:
Output:
Name: Consumer
ChunkSize: 1
Input:
Name: Publisher
ChunkSize: 1
MaximumDelayMs: 5000
5. Creating a Custom Assertion: LengthAssertion¶
To verify that output JSON arrays have the expected length, implement a custom assertion in the same style as QaaS.Common.Assertions.
Assertion Configuration Record¶
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Nodes;
using QaaS.Framework.SDK.DataSourceObjects;
using QaaS.Framework.SDK.Extensions;
using QaaS.Framework.SDK.Hooks.Assertion;
using QaaS.Framework.SDK.Session.SessionDataObjects;
namespace DummyAppTests;
/// <summary>
/// Configuration for LengthAssertion to validate output array length.
/// </summary>
public record LengthAssertionConfiguration
{
[Required]
public uint? ExpectedLength { get; set; }
[Required]
public string? OutputName { get; set; }
}
Assertion Implementation¶
/// <summary>
/// Asserts that all output JSON arrays have the expected number of items.
/// </summary>
public class LengthAssertion : BaseAssertion<LengthAssertionConfiguration>
{
public override bool Assert(
IImmutableList<SessionData> sessionDataList,
IImmutableList<DataSource> dataSourceList)
{
var output = sessionDataList.AsSingle()
.GetOutputByName(Configuration.OutputName!)
.CastCommunicationData<JsonArray>();
var countOfOutputsNotTheCorrectLength = output.Data.Count(item => item.Body?.Count != Configuration.ExpectedLength);
AssertionMessage = $"Number of outputs that were not the expected length is {countOfOutputsNotTheCorrectLength}";
return countOfOutputsNotTheCorrectLength == 0;
}
}
Assertion YAML Usage¶
Assertions:
- Name: LengthAssertion
Assertion: LengthAssertion
SessionNames: [RabbitMqExchangeWith10Samples]
AssertionConfiguration:
OutputName: Consumer
ExpectedLength: 5
6. Creating a Custom Probe: PrintCurrentTimeProbe¶
A probe executes custom logic during test execution. Here is a simple probe that logs the current UTC time.
Probe Implementation¶
using System.Collections.Immutable;
using QaaS.Framework.SDK.DataSourceObjects;
using QaaS.Framework.SDK.Hooks.Probe;
using QaaS.Framework.SDK.Session.SessionDataObjects;
namespace DummyAppTests;
/// <summary>
/// Prints the current UTC time to the console.
/// Probes can perform more complex operations using session data and configurations.
/// </summary>
public class PrintCurrentTimeProbe : BaseProbe<object>
{
public override void Run(
IImmutableList<SessionData> sessionDataList,
IImmutableList<DataSource> dataSourceList)
{
var currentTime = DateTime.UtcNow;
Console.WriteLine($"Current time is: {currentTime}");
}
}
Probe YAML Usage¶
Sessions:
- Name: RabbitMqExchangeWithFromFileSystemTestData
Probes:
- Probe: PrintCurrentTimeProbe
Name: GetCurrentTime
---
Final test.qaas.yaml Overview¶
MetaData:
Team: Smoke
System: DummyApp
DataSources:
- Name: FromFileSystemTestData
Generator: FromFileSystem
GeneratorConfiguration:
DataArrangeOrder: AsciiAsc
FileSystem:
Path: TestData
- Name: 10Samples
Generator: JsonArrayGenerator
GeneratorConfiguration:
Count: 10
NumberOfItemsPerArray: 5
Sessions:
- Name: RabbitMqExchangeWithFromFileSystemTestData
Probes:
- Probe: PrintCurrentTimeProbe
Name: GetCurrentTime
Publishers:
- Name: Publisher
DataSourceNames: [FromFileSystemTestData]
Policies:
- LoadBalance:
Rate: 50
RabbitMq:
Host: 127.0.0.1
Username: admin
Password: admin
RoutingKey: /
Port: 5672
ExchangeName: input
Consumers:
- Name: Consumer
TimeoutMs: 5000
RabbitMq:
Host: 127.0.0.1
Username: admin
Password: admin
RoutingKey: /
Port: 5672
ExchangeName: output
Deserialize:
Deserializer: Json
- Name: RabbitMqExchangeWith10Samples
Publishers:
- Name: Publisher
DataSourceNames: [10Samples]
Policies:
- LoadBalance:
Rate: 50
RabbitMq:
Host: 127.0.0.1
Username: admin
Password: admin
RoutingKey: /
Port: 5672
ExchangeName: input
Serialize:
Serializer: Json
Consumers:
- Name: Consumer
TimeoutMs: 5000
RabbitMq:
Host: 127.0.0.1
Username: admin
Password: admin
RoutingKey: /
Port: 5672
ExchangeName: output
Deserialize:
Deserializer: Json
Assertions:
- Name: HermeticByInputOutputPercentage
Assertion: HermeticByInputOutputPercentage
SessionNames: [RabbitMqExchangeWithFromFileSystemTestData]
AssertionConfiguration:
OutputNames: [Consumer]
InputNames: [Publisher]
ExpectedPercentage: 100
- Name: DelayByChunks
Assertion: DelayByChunks
SessionNames: [RabbitMqExchangeWithFromFileSystemTestData]
AssertionConfiguration:
Output:
Name: Consumer
ChunkSize: 1
Input:
Name: Publisher
ChunkSize: 1
MaximumDelayMs: 5000
- Name: HermeticByInputOutputPercentage
Assertion: HermeticByInputOutputPercentage
SessionNames: [RabbitMqExchangeWith10Samples]
AssertionConfiguration:
OutputNames: [Consumer]
InputNames: [Publisher]
ExpectedPercentage: 100
- Name: DelayByChunks
Assertion: DelayByChunks
SessionNames: [RabbitMqExchangeWith10Samples]
AssertionConfiguration:
Output:
Name: Consumer
ChunkSize: 1
Input:
Name: Publisher
ChunkSize: 1
MaximumDelayMs: 5000
- Name: LengthAssertion
Assertion: LengthAssertion
SessionNames: [RabbitMqExchangeWith10Samples]
AssertionConfiguration:
OutputName: Consumer
ExpectedLength: 5