Testing
- Last UpdatedJul 28, 2026
- 4 minute read
- Engineering
- Integration Service 4.1
- Integrators
This section covers unit testing strategies and patterns for applications using the AVEVA Integration Service Data API SDK.
Test Setup
Test Project Structure

Required Test Packages
<!-- In your test .csproj -->
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.x" />
<PackageReference Include="xunit" Version="2.x" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.x" />
<PackageReference Include="Moq" Version="4.x" />
<PackageReference Include="AutoFixture" Version="4.x" />
<PackageReference Include="FluentAssertions" Version="6.x" />
</ItemGroup>
Mocking the SDK
Mock HttpClient
using Moq;
using Moq.Protected;
[Fact]
public async Task GetDataSources_WithValidHost_ReturnsDataSources()
{
// Arrange
var datasources = new List<DatasourceDto>
{
new DatasourceDto { Name = "Eng1", Type = "Engineering" }
};
var mockHandler = new Mock<HttpMessageHandler>();
mockHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(
JsonConvert.SerializeObject(datasources),
Encoding.UTF8,
"application/json")
});
var httpClient = new HttpClient(mockHandler.Object);
// Act
var client = DataApiClientFactory.CreateDataApiClient<EngineeringClient>(host);
var result = await client.GetDataSources("");
// Assert
Assert.NotNull(result);
Assert.Single(result);
Assert.Equal("Eng1", result.First().Name);
}
Mock HubConnectionManager
[Fact]
public async Task Subscribe_WithValidTopic_Succeeds()
{
// Arrange
var mockHubConnection = new Mock<IHubConnection>();
mockHubConnection.Setup(x => x.ConnectionId).Returns("test-connection-id");
mockHubConnection.Setup(x => x.State).Returns(HubConnectionState.Connected);
var mockHubManager = new Mock<IHubConnectionManager>();
mockHubManager
.Setup(x => x.CreateHubConnectionAsync(
It.IsAny<AuthenticationType>(),
It.IsAny<string>(),
It.IsAny<CancellationTokenSource>(),
It.IsAny<string>()))
.ReturnsAsync(mockHubConnection.Object);
// Act
var pubSubClient = new SignalRPubSubClient(mockHubManager.Object, host);
var result = await pubSubClient.Subscribe("TestTopic");
// Assert
Assert.NotNull(result);
}
Base Test Class with Mock Helpers
public abstract class BaseDataApiClientTests
{
protected Mock<IIntegrationHttpClientFactory> mockHttpClientFactory;
protected Mock<IHubConnectionManager> mockHubConnectionManager;
protected Mock<HttpMessageHandler> handlerMock;
protected BaseDataApiClientTests()
{
handlerMock = new Mock<HttpMessageHandler>();
var httpClient = new HttpClient(handlerMock.Object);
mockHttpClientFactory = new Mock<IIntegrationHttpClientFactory>();
mockHttpClientFactory
.Setup(x => x.CreateClient(It.IsAny<string>()))
.Returns(httpClient);
mockHubConnectionManager = new Mock<IHubConnectionManager>();
}
protected void SetupHttpResponse<T>(T data) where T : class
{
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(
JsonConvert.SerializeObject(data),
Encoding.UTF8,
"application/json")
});
}
}
Unit Test Examples
Test Data Retrieval
[Fact]
public async Task GetDataSources_ReturnsAllDataSources()
{
// Arrange
var expectedDatasources = new List<DatasourceDto>
{
new DatasourceDto { Name = "DS1", Type = "Engineering" },
new DatasourceDto { Name = "DS2", Type = "Excel" }
};
SetupHttpResponse(expectedDatasources);
var client = new EngineeringClient(
mockHttpClientFactory.Object,
mockHubConnectionManager.Object,
host);
// Act
var result = await client.GetDataSources("");
// Assert
result.Should().HaveCount(2);
result.Should().Contain(x => x.Name == "DS1");
}
Test Exception Handling
[Fact]
public async Task GetDataSources_OnTimeout_ThrowsTimeoutException()
{
// Arrange
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ThrowsAsync(new TimeoutException("Connection timed out"));
var client = new EngineeringClient(
mockHttpClientFactory.Object,
mockHubConnectionManager.Object,
host);
// Act & Assert
await Assert.ThrowsAsync<TimeoutException>(
async () => await client.GetDataSources(""));
}
Test Pub/Sub Operations
[Fact]
public async Task Publish_WithValidMessage_PublishesSuccessfully()
{
// Arrange
var mockHubConnection = new Mock<IHubConnection>();
mockHubConnection
.Setup(x => x.InvokeAsync(It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(Task.CompletedTask);
var mockHubManager = new Mock<IHubConnectionManager>();
mockHubManager
.Setup(x => x.CreateHubConnectionAsync(
It.IsAny<AuthenticationType>(),
It.IsAny<string>(),
It.IsAny<CancellationTokenSource>(),
It.IsAny<string>()))
.ReturnsAsync(mockHubConnection.Object);
var pubSubClient = new SignalRPubSubClient(mockHubManager.Object, host);
var message = new Broadcast
{
PublisherConnectionId = "conn1",
CreatedTime = DateTime.UtcNow,
User = "testuser",
Context = "Test message"
};
// Act
var result = await pubSubClient.Publish(message, host, "TestTopic");
// Assert
Assert.NotNull(result);
}
Parameterized Tests
[Theory]
[InlineData("")]
[InlineData("Status eq 1")]
[InlineData("Modified gt datetime'2024-01-01'")]
public async Task GetTableData_WithVariousFilters_ReturnsData(string filter)
{
// Arrange
var expectedData = new List<TableData> { new TableData { Name = "Row1" } };
SetupHttpResponse(expectedData);
var client = new EngineeringClient(
mockHttpClientFactory.Object,
mockHubConnectionManager.Object,
host);
// Act
var result = await client.GetTableData("DS", "Table", filter);
// Assert
result.Should().NotBeEmpty();
}
Integration Tests
Test with Real DataAPI
public class DataApiIntegrationTests : IAsyncLifetime
{
private readonly string _host;
private EngineeringClient _client;
public DataApiIntegrationTests()
{
_host = "https://your-test-ais-server/api/v1/datasources";
}
public async Task InitializeAsync()
{
_client = DataApiClientFactory.CreateDataApiClient<EngineeringClient>(_host);
}
public Task DisposeAsync() => Task.CompletedTask;
[Fact]
[Trait("Category", "Integration")]
public async Task RetrievesDataFromRealAPI()
{
var dataSources = await _client.GetDataSources("");
Assert.NotNull(dataSources);
}
}
Test Configuration Loading
[Fact]
public void CreateClient_WithConfigFile_LoadsConfigurationCorrectly()
{
var host = ConfigurationManager.AppSettings["host"];
var authType = AuthenticationType.NTLM;
var client = DataApiClientFactory.CreateDataApiClient<EngineeringClient>(host, authType);
Assert.NotNull(client);
}
Test Data
Creating Test Fixtures
public class DataSourceFixture
{
public static DatasourceDto CreateDatasource(
string name = "TestDS",
string type = "Engineering") => new DatasourceDto
{
Name = name,
Type = type,
Description = $"Test data source: {name}"
};
public static IEnumerable<DatasourceDto> CreateMultipleDatasources(int count = 3)
{
for (int i = 1; i <= count; i++)
yield return CreateDatasource($"DS{i}", "Engineering");
}
}
Using AutoFixture
[Fact]
public void CreateRandomDataSources()
{
var fixture = new Fixture();
var datasources = fixture.CreateMany<DatasourceDto>(5);
Assert.Equal(5, datasources.Count());
}
Running Tests
.NET CLI
# Run all tests
dotnet test
# Run specific test class
dotnet test --filter "ClassName=EngineeringClientTest"
# Run specific test method
dotnet test --filter "MethodName=GetDataSources_ReturnsData"
# Verbose output
dotnet test --verbosity normal
# Release configuration
dotnet test -c Release
Visual Studio Test Explorer
-
Open Test → Test Explorer (Ctrl+E, T)
-
Click Run All Tests or select individual tests
-
View pass/fail results in the panel
Continuous Integration (GitHub Actions)
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- run: dotnet test --verbosity normal
Code Coverage
Generate Coverage Report
# One-time tool install
dotnet tool install -g dotnet-coverage
# Collect coverage
dotnet-coverage collect -f cobertura -o coverage.cobertura.xml dotnet test
# Generate HTML report (requires ReportGenerator)
dotnet tool install -g reportgenerator
reportgenerator -reports:coverage.cobertura.xml -targetdir:coveragereport
Coverage Targets
-
Unit test coverage: 80%+
-
Critical data paths: 95%+
-
Infrastructure/boilerplate: lower coverage acceptable
Recommended Test Structure
public class EngineeringClientTests
{
private readonly Mock<IIntegrationHttpClientFactory> _mockHttpClientFactory;
private readonly Mock<IHubConnectionManager> _mockHubManager;
private readonly EngineeringClient _client;
public EngineeringClientTests()
{
_mockHttpClientFactory = new Mock<IIntegrationHttpClientFactory>();
_mockHubManager = new Mock<IHubConnectionManager>();
_client = new EngineeringClient(
_mockHttpClientFactory.Object,
_mockHubManager.Object,
"http://test");
}
#region GetDataSources Tests
[Fact]
public async Task GetDataSources_WithEmptyFilter_ReturnsAllDataSources()
{
// Arrange – setup mock response
// Act
var result = await _client.GetDataSources("");
// Assert
Assert.NotNull(result);
}
[Fact]
public async Task GetDataSources_OnConnectionError_ThrowsHubConnectionInitializationException()
{
// Arrange – configure mock to throw
// Act & Assert
await Assert.ThrowsAsync<HubConnectionInitializationException>(
() => _client.GetDataSources(""));
}
#endregion
#region GetTableData Tests
[Theory]
[InlineData("")]
[InlineData("filter")]
public async Task GetTableData_WithVariousFilters_ReturnsExpectedData(string filter)
{
// Test implementation
}
#endregion
}