Integration Testing
What is Integration Testing?
Integration testing is a type of software testing where individual modules or components of a system are combined and tested as a group. The purpose of integration testing is to verify that different modules work together correctly and meet the specified requirements when integrated.
Why is Integration Testing Important?
- Detects Interface Issues – Identifies problems in the interaction between different software modules.
- Ensures Data Flow – Verifies that data is correctly transmitted between modules.
- Validates System Behavior – Ensures that the integrated components function as expected.
- Reduces Bugs in Later Stages – Detecting issues early prevents major problems during system testing and deployment.
Best Practices for Integration Testing
- Use Test Data Closely Resembling Real Scenarios – Helps in identifying real-world issues.
- Automate Where Possible – Reduces manual effort and increases test efficiency.
- Prioritize Critical Modules – Test modules that have the highest impact first.
- Keep Track of Test Cases and Results – Maintain proper documentation for troubleshooting.
- Mock External Dependencies – Use mocks and stubs to simulate unavailable components.
gRPC Service Integration Testing
gRPC Service designed in a way that it consists of Server and Client that sharing the same gRPC Protobuf file with generated classes/binaries.
Main idea is to use Client
to run Integration Tests against running gRPC Server in isolation.
All external dependencies like Database connectivity needs to be running in isolation for the test run. For this purposes we are using TestContainers to run ephemeral DB container for test execution.
Hence we are using DB Migration tools like Liquibase/Entities Framework the DB shape is easily re-creatable.
REST/GraphQL Domain Gateway Integration Testing
Similar concept is used for REST/GraphQL Domain Gateway testing. REST/GraphQL Server will be running with Mock/Stubbed gRPC Service. HTTP/GraphQL Client will be used to make real requests against running Server.
Integration tests setup for gRPC .NET Service
ApplicationFixture
for Server and Client
Creating Server running on random port with temporary database (CockroachDB Test Container)
public class ApplicationFixture: IDisposable
{
private readonly SampleServiceServer _server;
private readonly SampleServiceClient _client;
public ApplicationFixture()
{
_server = new SampleServiceServer()
.WithTempDb()
.WithRandomPorts()
.Start();
_client = SampleServiceClient.Of(_server.getGrpcUrl());
}
public SampleServiceClient GetClient() => _client;
public SampleServiceServer GetServer() => _server;
public void Dispose()
{
_server.Stop();
}
}
[CollectionDefinition("ApplicationCollection")]
public class ApplicationCollection : ICollectionFixture<ApplicationFixture>
{
// This class has no code; it's just a marker for the test collection
}
Using Client to make requests to Server
[Collection("ApplicationCollection")]
public class SampleServiceGrpcIt(ITestOutputHelper testOutputHelper, ApplicationFixture applicationFixture)
{
private readonly ApplicationFixture _applicationFixture = applicationFixture;
private readonly SampleServiceClient _client = applicationFixture.GetClient();
[Fact]
public async Task Test_CreateSample()
{
//Arrange
//Act
var createRequest = new SampleDto { Name = Guid.NewGuid().ToString() };
var response = await _client.CreateSample(createRequest);
//Assert
var dto = response.Sample;
Assert.NotNull(dto.Id);
Assert.Equal(createRequest.Name, dto.Name);
}
}
Integration tests setup for Java gRPC Service
Spring Integration Tests Configuration
Create Server running on random ports with temporary database (CockroachDB Test Container)
@Configuration
public class IntegrationTestsConfig {
@Bean(initMethod = "start", destroyMethod = "stop")
public SampleServiceServer sampleServiceServer() {
return new SampleServiceServer()
.withRandomPorts()
.withTempDb();
}
@Bean
public SampleServiceClient sampleServiceClient(SampleServiceServer server) {
return SampleServiceClient.of("localhost", server.getGrpcPort());
}
}
Base Integration Test
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { IntegrationTestsConfig.class })
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class SampleServiceBaseIT {
@Autowired
protected SampleServiceServer server;
@Autowired
protected SampleServiceClient client;
}
Actual Integration Test
class SampleServiceGrpcTest extends SampleServiceBaseIT {
@Test
void test_createSample() {
CreateSampleResponse response = client.createSample(SampleDto.newBuilder().setName("Sample").build());
assertThat(response.getSample()
.getId()
.getValue()).isNotNull();
}
}