diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec8a8515..6c4afbe4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# 1.20.0 (24 December 2025)
+- [#1399](https://github.com/wiremock/WireMock.Net/pull/1399) - Upgrade RamlToOpenApiConverter and YamlDotNet [feature] contributed by [StefH](https://github.com/StefH)
+- [#1400](https://github.com/wiremock/WireMock.Net/pull/1400) - Add WireMock.Net.NUnit project [feature] contributed by [StefH](https://github.com/StefH)
+- [#1405](https://github.com/wiremock/WireMock.Net/pull/1405) - Fix Testcontainers AddProtoDefinition [bug] contributed by [StefH](https://github.com/StefH)
+- [#1398](https://github.com/wiremock/WireMock.Net/issues/1398) - Upgrade YamlDotNet dependency [feature]
+- [#1404](https://github.com/wiremock/WireMock.Net/issues/1404) - An exception occurs when adding multiple proto definitions in the TestContainer. [bug]
+
# 1.19.0 (12 December 2025)
- [#1391](https://github.com/wiremock/WireMock.Net/pull/1391) - Update WireMockContainerBuilder (WithImage and WithCustomImage) [feature] contributed by [StefH](https://github.com/StefH)
- [#1392](https://github.com/wiremock/WireMock.Net/pull/1392) - WireMockContainerBuilder: allow all docker images named wiremock [feature] contributed by [StefH](https://github.com/StefH)
diff --git a/Directory.Build.props b/Directory.Build.props
index 6d59e440..daf48040 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,7 +4,7 @@
- 1.19.0
+ 1.20.0
WireMock.Net-Logo.png
https://github.com/wiremock/WireMock.Net
Apache-2.0
diff --git a/Generate-ReleaseNotes.cmd b/Generate-ReleaseNotes.cmd
index dee29e49..5a5e664b 100644
--- a/Generate-ReleaseNotes.cmd
+++ b/Generate-ReleaseNotes.cmd
@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes
-SET version=1.19.0
+SET version=1.20.0
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
diff --git a/PackageReleaseNotes.txt b/PackageReleaseNotes.txt
index 1cc929a6..d0d374b0 100644
--- a/PackageReleaseNotes.txt
+++ b/PackageReleaseNotes.txt
@@ -1,6 +1,8 @@
-# 1.19.0 (12 December 2025)
-- #1391 Update WireMockContainerBuilder (WithImage and WithCustomImage) [feature]
-- #1392 WireMockContainerBuilder: allow all docker images named wiremock [feature]
-- #1390 Unable to build WireMockContainerBuilder with custom image [feature]
+# 1.20.0 (24 December 2025)
+- #1399 Upgrade RamlToOpenApiConverter and YamlDotNet [feature]
+- #1400 Add WireMock.Net.NUnit project [feature]
+- #1405 Fix Testcontainers AddProtoDefinition [bug]
+- #1398 Upgrade YamlDotNet dependency [feature]
+- #1404 An exception occurs when adding multiple proto definitions in the TestContainer. [bug]
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
\ No newline at end of file
diff --git a/src/WireMock.Net.ProtoBuf/Matchers/ProtoBufMatcher.cs b/src/WireMock.Net.ProtoBuf/Matchers/ProtoBufMatcher.cs
index bbba63f3..7f79d896 100644
--- a/src/WireMock.Net.ProtoBuf/Matchers/ProtoBufMatcher.cs
+++ b/src/WireMock.Net.ProtoBuf/Matchers/ProtoBufMatcher.cs
@@ -96,14 +96,14 @@ public class ProtoBufMatcher : IProtoBufMatcher
}
var protoDefinitions = ProtoDefinition().Texts;
-
+
var resolver = new WireMockProtoFileResolver(protoDefinitions);
var request = new ConvertToObjectRequest(protoDefinitions[0], MessageType, input)
.WithProtoFileResolver(resolver);
try
{
- return await ProtoBufToJsonConverter.ConvertAsync(request, cancellationToken).ConfigureAwait(false);
+ return await ProtoBufToJsonConverter.ConvertAsync(request, cancellationToken);
}
catch
{
diff --git a/src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj b/src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj
index 47710e3d..52d1e3b0 100644
--- a/src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj
+++ b/src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/src/WireMock.Net.Testcontainers/Extensions/HttpWaitStrategyExtensions.cs b/src/WireMock.Net.Testcontainers/Extensions/HttpWaitStrategyExtensions.cs
new file mode 100644
index 00000000..33aab29a
--- /dev/null
+++ b/src/WireMock.Net.Testcontainers/Extensions/HttpWaitStrategyExtensions.cs
@@ -0,0 +1,18 @@
+// Copyright © WireMock.Net
+
+using WireMock.Net.Testcontainers;
+
+namespace DotNet.Testcontainers.Configurations;
+
+internal static class HttpWaitStrategyExtensions
+{
+ internal static HttpWaitStrategy WithBasicAuthentication(this HttpWaitStrategy strategy, WireMockConfiguration configuration)
+ {
+ if (configuration.HasBasicAuthentication)
+ {
+ return strategy.WithBasicAuthentication(configuration.Username, configuration.Password);
+ }
+
+ return strategy;
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.Testcontainers/Utils/CombineUtils.cs b/src/WireMock.Net.Testcontainers/Utils/CombineUtils.cs
new file mode 100644
index 00000000..caab4c75
--- /dev/null
+++ b/src/WireMock.Net.Testcontainers/Utils/CombineUtils.cs
@@ -0,0 +1,20 @@
+// Copyright © WireMock.Net
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace WireMock.Net.Testcontainers.Utils;
+
+internal static class CombineUtils
+{
+ internal static List Combine(List oldValue, List newValue)
+ {
+ return oldValue.Union(newValue).ToList();
+ }
+
+ internal static Dictionary Combine(Dictionary oldValue, Dictionary newValue)
+ where TKey : notnull
+ {
+ return oldValue.Union(newValue).ToDictionary(item => item.Key, item => item.Value);
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.Testcontainers/WireMockConfiguration.cs b/src/WireMock.Net.Testcontainers/WireMockConfiguration.cs
index f7260670..dded4876 100644
--- a/src/WireMock.Net.Testcontainers/WireMockConfiguration.cs
+++ b/src/WireMock.Net.Testcontainers/WireMockConfiguration.cs
@@ -1,12 +1,12 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
-using System.Linq;
using Docker.DotNet.Models;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations;
using JetBrains.Annotations;
using Stef.Validation;
+using WireMock.Net.Testcontainers.Utils;
namespace WireMock.Net.Testcontainers;
@@ -77,8 +77,8 @@ public sealed class WireMockConfiguration : ContainerConfiguration
StaticMappingsPath = BuildConfiguration.Combine(oldValue.StaticMappingsPath, newValue.StaticMappingsPath);
WatchStaticMappings = BuildConfiguration.Combine(oldValue.WatchStaticMappings, newValue.WatchStaticMappings);
WatchStaticMappingsInSubdirectories = BuildConfiguration.Combine(oldValue.WatchStaticMappingsInSubdirectories, newValue.WatchStaticMappingsInSubdirectories);
- AdditionalUrls = Combine(oldValue.AdditionalUrls, newValue.AdditionalUrls);
- ProtoDefinitions = Combine(oldValue.ProtoDefinitions, newValue.ProtoDefinitions);
+ AdditionalUrls = CombineUtils.Combine(oldValue.AdditionalUrls, newValue.AdditionalUrls);
+ ProtoDefinitions = CombineUtils.Combine(oldValue.ProtoDefinitions, newValue.ProtoDefinitions);
}
///
@@ -130,16 +130,4 @@ public sealed class WireMockConfiguration : ContainerConfiguration
return this;
}
-
- private static List Combine(List oldValue, List newValue)
- {
- return oldValue.Concat(newValue).ToList();
- }
-
- private static Dictionary Combine(Dictionary oldValue, Dictionary newValue)
- {
- return newValue
- .Concat(oldValue.Where(item => !newValue.Keys.Contains(item.Key)))
- .ToDictionary(item => item.Key, item => item.Value);
- }
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Testcontainers/WireMockContainer.cs b/src/WireMock.Net.Testcontainers/WireMockContainer.cs
index 002a0469..f34cc1b3 100644
--- a/src/WireMock.Net.Testcontainers/WireMockContainer.cs
+++ b/src/WireMock.Net.Testcontainers/WireMockContainer.cs
@@ -228,6 +228,7 @@ public sealed class WireMockContainer : DockerContainer
foreach (var kvp in _configuration.ProtoDefinitions)
{
Logger.LogInformation("Adding ProtoDefinition {Id}", kvp.Key);
+
foreach (var protoDefinition in kvp.Value)
{
try
diff --git a/src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs b/src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs
index 71c0ad69..37241598 100644
--- a/src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs
+++ b/src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs
@@ -2,6 +2,8 @@
using System;
using System.Linq;
+using System.Net;
+using System.Net.Http;
using System.Runtime.InteropServices;
using Docker.DotNet.Models;
using DotNet.Testcontainers.Builders;
@@ -250,6 +252,23 @@ public sealed class WireMockContainerBuilder : ContainerBuilder httpWaitStrategy
+ .ForPort(WireMockContainer.ContainerPort)
+ .WithMethod(HttpMethod.Get)
+ .WithBasicAuthentication(DockerResourceConfiguration)
+ .ForPath("/__admin/health")
+ .ForStatusCode(HttpStatusCode.OK)
+ .ForResponseMessageMatching(async httpResponseMessage =>
+ {
+ var content = await httpResponseMessage.Content.ReadAsStringAsync();
+ return content?.Contains("Healthy") == true;
+ })
+ )
+ );
+
return new WireMockContainer(builder.DockerResourceConfiguration);
}
@@ -262,7 +281,9 @@ public sealed class WireMockContainerBuilder : ContainerBuilder waitStrategy.WithTimeout(TimeSpan.FromSeconds(30)))
+ );
}
///
diff --git a/test/WireMock.Net.Tests/Testcontainers/CombineUtilsTests.cs b/test/WireMock.Net.Tests/Testcontainers/CombineUtilsTests.cs
new file mode 100644
index 00000000..95f55774
--- /dev/null
+++ b/test/WireMock.Net.Tests/Testcontainers/CombineUtilsTests.cs
@@ -0,0 +1,161 @@
+// Copyright © WireMock.Net
+
+using System.Collections.Generic;
+using FluentAssertions;
+using WireMock.Net.Testcontainers.Utils;
+using Xunit;
+
+namespace WireMock.Net.Tests.Testcontainers;
+
+public class CombineUtilsTests
+{
+ [Fact]
+ public void Combine_Lists_WithBothEmpty_ReturnsEmptyList()
+ {
+ // Arrange
+ var oldValue = new List();
+ var newValue = new List();
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Combine_Lists_WithEmptyOldValue_ReturnsNewValue()
+ {
+ // Arrange
+ var oldValue = new List();
+ var newValue = new List { "item1", "item2" };
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().Equal("item1", "item2");
+ }
+
+ [Fact]
+ public void Combine_Lists_WithEmptyNewValue_ReturnsOldValue()
+ {
+ // Arrange
+ var oldValue = new List { "item1", "item2" };
+ var newValue = new List();
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().Equal("item1", "item2");
+ }
+
+ [Fact]
+ public void Combine_Lists_WithBothPopulated_ReturnsConcatenatedList()
+ {
+ // Arrange
+ var oldValue = new List { 1, 2, 3 };
+ var newValue = new List { 4, 5, 6 };
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().Equal(1, 2, 3, 4, 5, 6);
+ }
+
+ [Fact]
+ public void Combine_Lists_WithDuplicates_RemovesDuplicates()
+ {
+ // Arrange
+ var oldValue = new List { "a", "b", "c" };
+ var newValue = new List { "b", "c", "d" };
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().Equal("a", "b", "c", "d");
+ }
+
+ [Fact]
+ public void Combine_Dictionaries_WithBothEmpty_ReturnsEmptyDictionary()
+ {
+ // Arrange
+ var oldValue = new Dictionary();
+ var newValue = new Dictionary();
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Combine_Dictionaries_WithEmptyOldValue_ReturnsNewValue()
+ {
+ // Arrange
+ var oldValue = new Dictionary();
+ var newValue = new Dictionary
+ {
+ { "key1", 1 },
+ { "key2", 2 }
+ };
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().HaveCount(2);
+ result["key1"].Should().Be(1);
+ result["key2"].Should().Be(2);
+ }
+
+ [Fact]
+ public void Combine_Dictionaries_WithEmptyNewValue_ReturnsOldValue()
+ {
+ // Arrange
+ var oldValue = new Dictionary
+ {
+ { "key1", 1 },
+ { "key2", 2 }
+ };
+ var newValue = new Dictionary();
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().HaveCount(2);
+ result["key1"].Should().Be(1);
+ result["key2"].Should().Be(2);
+ }
+
+ [Fact]
+ public void Combine_Dictionaries_WithNoOverlappingKeys_ReturnsMergedDictionary()
+ {
+ // Arrange
+ var oldValue = new Dictionary
+ {
+ { "key1", "value1" },
+ { "key2", "value2" }
+ };
+ var newValue = new Dictionary
+ {
+ { "key3", "value3" },
+ { "key4", "value4" }
+ };
+
+ // Act
+ var result = CombineUtils.Combine(oldValue, newValue);
+
+ // Assert
+ result.Should().HaveCount(4);
+ result["key1"].Should().Be("value1");
+ result["key2"].Should().Be("value2");
+ result["key3"].Should().Be("value3");
+ result["key4"].Should().Be("value4");
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Testcontainers/TestcontainersTestsGrpc.cs b/test/WireMock.Net.Tests/Testcontainers/TestcontainersTestsGrpc.cs
index c05125e0..1c578f78 100644
--- a/test/WireMock.Net.Tests/Testcontainers/TestcontainersTestsGrpc.cs
+++ b/test/WireMock.Net.Tests/Testcontainers/TestcontainersTestsGrpc.cs
@@ -19,7 +19,7 @@ using Xunit.Abstractions;
namespace WireMock.Net.Tests.Testcontainers;
-[Collection("Grpc")]
+//[Collection("Grpc")]
public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
{
[Fact]
@@ -37,7 +37,7 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
.WithCommand("--Urls", $"http://*:80 grpc://*:{port}")
.WithPortBinding(port, true)
.Build();
-
+
try
{
await wireMockContainer.StartAsync();
@@ -171,6 +171,35 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
await StopAsync(wireMockContainer);
}
+ private async Task When_GrpcClient_Calls_SayHelloAsync(WireMockContainer wireMockContainer)
+ {
+ var address = wireMockContainer.GetPublicUrls().First(x => x.Key != 80).Value;
+ var channel = GrpcChannel.ForAddress(address);
+
+ var client = new Greeter.GreeterClient(channel);
+
+ try
+ {
+ return await client.SayHelloAsync(new HelloRequest { Name = "stef" });
+ }
+ catch (Exception ex)
+ {
+ testOutputHelper.WriteLine("Exception during GrpcClient Call to {0}. Exception = {1}.", address, ex);
+
+ testOutputHelper.WriteLine("Dumping WireMock.Net logs:");
+ var (stdOut, stdError) = await wireMockContainer.GetLogsAsync(DateTime.MinValue);
+ testOutputHelper.WriteLine("Out :\r\n{0}", stdOut);
+ testOutputHelper.WriteLine("Error:\r\n{0}", stdError);
+
+ testOutputHelper.WriteLine("Dumping WireMock.Net mappings:");
+ using var httpClient = wireMockContainer.CreateClient();
+ using var response = await httpClient.GetAsync("/__admin/mappings");
+ var mappings = await response.Content.ReadAsStringAsync();
+ testOutputHelper.WriteLine("Mappings:\r\n{0}", mappings);
+ throw;
+ }
+ }
+
private async Task StopAsync(WireMockContainer wireMockContainer)
{
try
@@ -240,18 +269,6 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
var result = await httpClient.PostAsync("/__admin/mappings", new StringContent(mappingsJson, Encoding.UTF8, WireMockConstants.ContentTypeJson));
result.EnsureSuccessStatusCode();
-
- await Task.Delay(5000);
- }
-
- private static async Task When_GrpcClient_Calls_SayHelloAsync(WireMockContainer wireMockContainer)
- {
- var address = wireMockContainer.GetPublicUrls().First(x => x.Key != 80).Value;
- var channel = GrpcChannel.ForAddress(address);
-
- var client = new Greeter.GreeterClient(channel);
-
- return await client.SayHelloAsync(new HelloRequest { Name = "stef" });
}
private static void Then_ReplyMessage_Should_BeCorrect(HelloReply reply)
diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
index de6ca818..b53868b9 100644
--- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
+++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
@@ -30,6 +30,10 @@
+
+
+
+