Skip to content

Commit

Permalink
test: replace Moq with NSubstitute (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
skwasjer authored Nov 18, 2023
1 parent 038adaa commit 66c66da
Show file tree
Hide file tree
Showing 22 changed files with 182 additions and 252 deletions.
4 changes: 2 additions & 2 deletions test/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@

<ItemGroup>
<Using Include="FluentAssertions" />
<Using Include="Moq" />
<Using Include="NSubstitute" />
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="xunit" Version="2.6.1" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void Given_that_adapter_is_not_registered_when_getting_adapter_it_should_
public void Given_that_adapter_is_registered_when_getting_adapter_it_should_return_same_instance()
{
using var msg = new HttpRequestMessage();
IJsonAdapter adapter = Mock.Of<IJsonAdapter>();
IJsonAdapter adapter = Substitute.For<IJsonAdapter>();
var items = new Dictionary<Type, object> { { typeof(IJsonAdapter), adapter } };
var sut = new MockHttpRequestContext(msg, items);
sut.Services.Should().NotBeEmpty();
Expand Down
78 changes: 31 additions & 47 deletions test/MockHttp.Json.Tests/JsonContentMatcherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ namespace MockHttp.Json;

public sealed class JsonContentMatcherTests : IDisposable
{
private readonly Mock<IJsonAdapter> _adapterMock;
private readonly Mock<IEqualityComparer<string>> _equalityComparerMock;
private readonly Dictionary<Type, object> _services;
private readonly IJsonAdapter _adapterMock;
private readonly IEqualityComparer<string> _equalityComparerMock;
private readonly Dictionary<Type, object> _services = new();
private readonly HttpRequestMessage _requestMessage;
private readonly MockHttpRequestContext _requestContext;

public JsonContentMatcherTests()
{
_adapterMock = new Mock<IJsonAdapter>();
_adapterMock = Substitute.For<IJsonAdapter>();

_equalityComparerMock = new Mock<IEqualityComparer<string>>();
_equalityComparerMock = Substitute.For<IEqualityComparer<string>>();
_equalityComparerMock
.Setup(m => m.Equals(It.IsAny<string?>()!, It.IsAny<string?>()!))
.Equals(Arg.Any<string?>(), Arg.Any<string?>())
.Returns(true);

_services = new Dictionary<Type, object>();
_requestMessage = new HttpRequestMessage();
_requestContext = new MockHttpRequestContext(_requestMessage, _services);
}
Expand All @@ -30,25 +29,21 @@ public async Task Given_that_adapter_is_provided_to_ctor_when_matching_it_should
var jsonContentAsObject = new { PropertyName = "value" };
const string serializedJson = "{\"modifiedPropertyName\":\"modifiedValue\"}";
_adapterMock
.Setup(m => m.Serialize(jsonContentAsObject))
.Returns(serializedJson)
.Verifiable();
.Serialize(Arg.Any<object?>())
.Returns(serializedJson);

var globalAdapterMock = new Mock<IJsonAdapter>();
_services.Add(typeof(IJsonAdapter), globalAdapterMock.Object);
IJsonAdapter globalAdapterMock = Substitute.For<IJsonAdapter>();
_services.Add(typeof(IJsonAdapter), globalAdapterMock);

var sut = new JsonContentMatcher(jsonContentAsObject, _adapterMock.Object, _equalityComparerMock.Object);
var sut = new JsonContentMatcher(jsonContentAsObject, _adapterMock, _equalityComparerMock);

// Act
await sut.IsMatchAsync(_requestContext);

// Assert
_adapterMock.Verify();
globalAdapterMock.Verify(m => m.Serialize(It.IsAny<object?>()), Times.Never);
_equalityComparerMock
.Verify(m => m.Equals(It.IsAny<string>(), serializedJson),
Times.Once
);
_adapterMock.Received(1).Serialize(jsonContentAsObject);
globalAdapterMock.DidNotReceiveWithAnyArgs().Serialize(Arg.Any<object?>());
_ = _equalityComparerMock.Received(1).Equals(Arg.Any<string?>(), serializedJson);
}

[Fact]
Expand All @@ -57,74 +52,66 @@ public async Task Given_that_adapter_is_not_provided_to_ctor_when_matching_it_sh
var jsonContentAsObject = new { PropertyName = "value" };
const string serializedJson = "{\"modifiedPropertyName\":\"modifiedValue\"}";

var globalAdapterMock = new Mock<IJsonAdapter>();
IJsonAdapter globalAdapterMock = Substitute.For<IJsonAdapter>();
globalAdapterMock
.Setup(m => m.Serialize(jsonContentAsObject))
.Returns(serializedJson)
.Verifiable();
.Serialize(Arg.Any<object?>())
.Returns(serializedJson);

var sut = new JsonContentMatcher(jsonContentAsObject, null, _equalityComparerMock.Object);
var sut = new JsonContentMatcher(jsonContentAsObject, null, _equalityComparerMock);

// Act
_services.Add(typeof(IJsonAdapter), globalAdapterMock.Object);
_services.Add(typeof(IJsonAdapter), globalAdapterMock);
await sut.IsMatchAsync(_requestContext);

// Assert
globalAdapterMock.Verify();
_equalityComparerMock
.Verify(m => m.Equals(It.IsAny<string>(), serializedJson),
Times.Once
);
globalAdapterMock.Received(1).Serialize(jsonContentAsObject);
_ = _equalityComparerMock.Received(1).Equals(Arg.Any<string?>(), serializedJson);
}

[Fact]
public async Task Given_that_adapter_is_not_provided_to_ctor_and_no_global_adapter_is_available_when_matching_it_should_use_the_default()
{
var jsonContentAsObject = new { PropertyName = "value" };
const string serializedJson = "{\"PropertyName\":\"value\"}";
var sut = new JsonContentMatcher(jsonContentAsObject, null, _equalityComparerMock.Object);
var sut = new JsonContentMatcher(jsonContentAsObject, null, _equalityComparerMock);

// Act
_services.Should().NotContainKey(typeof(IJsonAdapter));
await sut.IsMatchAsync(_requestContext);

// Assert
_equalityComparerMock
.Verify(m => m.Equals(It.IsAny<string>(), serializedJson),
Times.Once
);
_ = _equalityComparerMock.Received(1).Equals(Arg.Any<string?>(), serializedJson);
}

[Fact]
public async Task Given_that_adapter_is_not_provided_to_ctor_when_matching_it_should_use_the_adapter()
{
const string jsonContentAsObject = "text";
var sut = new JsonContentMatcher(jsonContentAsObject, _adapterMock.Object, _equalityComparerMock.Object);
var sut = new JsonContentMatcher(jsonContentAsObject, _adapterMock, _equalityComparerMock);

// Act
await sut.IsMatchAsync(_requestContext);

// Assert
_adapterMock.Verify(m => m.Serialize(jsonContentAsObject), Times.Once);
_adapterMock.Received(1).Serialize(jsonContentAsObject);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task When_matching_it_should_return_the_results_of_the_comparer(bool equals)
{
var sut = new JsonContentMatcher("something to compare with", _adapterMock.Object, _equalityComparerMock.Object);
var sut = new JsonContentMatcher("something to compare with", _adapterMock, _equalityComparerMock);

_equalityComparerMock
.Setup(m => m.Equals(It.IsAny<string?>()!, It.IsAny<string?>()!))
.Returns(equals)
.Verifiable();
.Equals(Arg.Any<string?>(), Arg.Any<string?>())
.Returns(equals);

// Act
bool actual = await sut.IsMatchAsync(_requestContext);

// Assert
_equalityComparerMock.Verify();
_ =_equalityComparerMock.Received(1).Equals(Arg.Any<string?>(), Arg.Any<string?>());
actual.Should().Be(equals);
}

Expand All @@ -137,17 +124,14 @@ HttpContent content
{
using (content)
{
var sut = new JsonContentMatcher("something to compare with", _adapterMock.Object, _equalityComparerMock.Object);
var sut = new JsonContentMatcher("something to compare with", _adapterMock, _equalityComparerMock);
_requestMessage.Content = content;

// Act
await sut.IsMatchAsync(_requestContext);

// Assert
_equalityComparerMock
.Verify(m => m.Equals(string.Empty, It.IsAny<string>()),
Times.Once
);
_ = _equalityComparerMock.Received(1).Equals(string.Empty, Arg.Any<string?>());
}
}

Expand Down
13 changes: 4 additions & 9 deletions test/MockHttp.Json.Tests/MockConfigurationExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@

public class MockConfigurationExtensionsTests
{
private readonly MockHttpHandler _sut;

public MockConfigurationExtensionsTests()
{
_sut = new MockHttpHandler();
}
private readonly MockHttpHandler _sut = new();

[Theory]
[MemberData(nameof(UseJsonAdapterNullTestCases))]
Expand All @@ -29,8 +24,8 @@ public void Given_that_arg_is_null_when_creating_instance_using_it_should_throw

public static IEnumerable<object?[]> UseJsonAdapterNullTestCases()
{
IMockConfiguration mockConfig = Mock.Of<IMockConfiguration>();
IJsonAdapter jsonAdapter = Mock.Of<IJsonAdapter>();
IMockConfiguration mockConfig = Substitute.For<IMockConfiguration>();
IJsonAdapter jsonAdapter = Substitute.For<IJsonAdapter>();

yield return new object?[] { null, jsonAdapter, nameof(mockConfig) };
yield return new object?[] { mockConfig, null, nameof(jsonAdapter) };
Expand All @@ -39,7 +34,7 @@ public void Given_that_arg_is_null_when_creating_instance_using_it_should_throw
[Fact]
public void When_using_it_should_register_adapter()
{
IJsonAdapter adapter = Mock.Of<IJsonAdapter>();
IJsonAdapter adapter = Substitute.For<IJsonAdapter>();

// Act
_sut.UseJsonAdapter(adapter);
Expand Down
17 changes: 8 additions & 9 deletions test/MockHttp.Tests/AnyRequestMatchingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,18 @@ public class AnyRequestMatchingTests

public AnyRequestMatchingTests()
{
static HttpRequestMatcher CreateMatcherMock(Func<bool> returns)
{
var matcherMock = new Mock<HttpRequestMatcher>();
matcherMock
.Setup(m => m.IsExclusive)
.Returns(returns);
return matcherMock.Object;
}

_sut = new AnyRequestMatching();

_matcher1 = CreateMatcherMock(() => _isExclusive1);
_matcher2 = CreateMatcherMock(() => _isExclusive2);
return;

static HttpRequestMatcher CreateMatcherMock(Func<bool> returns)
{
HttpRequestMatcher matcherMock = Substitute.For<HttpRequestMatcher>();
matcherMock.IsExclusive.Returns(_ => returns());
return matcherMock;
}
}

[Fact]
Expand Down
14 changes: 6 additions & 8 deletions test/MockHttp.Tests/Extensions/IRespondsExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,12 @@ public class WithStream : IRespondsExtensionsTests
[InlineData(false)]
public async Task When_responding_with_stream_it_should_return_response(bool isSeekable)
{
using var ms = new CanSeekMemoryStream(Encoding.UTF8.GetBytes("content"), isSeekable);
using var ms = new CanSeekMemoryStream("content"u8.ToArray(), isSeekable);
var request = new HttpRequestMessage();
var expectedContent = new ByteArrayContent(Encoding.UTF8.GetBytes("content"));
var expectedContent = new ByteArrayContent("content"u8.ToArray());

// Act
// ReSharper disable once AccessToDisposedClosure
_sut.Respond(with => with.Body(ms));
HttpResponseMessage actualResponse = await _httpCall.SendAsync(new MockHttpRequestContext(request), CancellationToken.None);

Expand Down Expand Up @@ -90,9 +91,8 @@ public void When_responding_with_null_stream_it_should_throw()
[Fact]
public void When_responding_with_not_readable_stream_it_should_throw()
{
var streamMock = new Mock<Stream>();
streamMock.Setup(m => m.CanRead).Returns(false);
Stream? streamContent = streamMock.Object;
Stream streamContent = Substitute.For<Stream>();
streamContent.CanRead.Returns(false);

// Act
Action act = () => _sut.Respond(with => with.Body(streamContent));
Expand Down Expand Up @@ -199,10 +199,8 @@ public void Given_null_argument_when_executing_method_it_should_throw(params obj

public static IEnumerable<object?[]> TestCases()
{
var streamMock = new Mock<Stream> { CallBase = true };
streamMock.SetReturnsDefault(true);
using var content = new StringContent("");
IResponds<IResponseResult> responds = Mock.Of<IResponds<IResponseResult>>();
IResponds<IResponseResult> responds = Substitute.For<IResponds<IResponseResult>>();

DelegateTestCase[] testCases =
{
Expand Down
4 changes: 2 additions & 2 deletions test/MockHttp.Tests/Extensions/ListExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ public void Given_that_list_contains_instances_of_type_when_getting_index_it_sho
{
var list = new List<IFoo?>
{
Mock.Of<IFoo>(),
Substitute.For<IFoo>(),
null,
new Foo(),
new Foo(),
Mock.Of<IFoo>(),
Substitute.For<IFoo>(),
new FooBar(),
new Foo(),
new FooBar()
Expand Down
21 changes: 12 additions & 9 deletions test/MockHttp.Tests/Extensions/RequestMatchingExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public async Task When_configuring_formData_should_match(string urlEncodedFormDa
var request = new HttpRequestMessage
{
RequestUri = new Uri("http://127.0.0.1"),
Content = new ByteArrayContent(Encoding.UTF8.GetBytes("key1=value1&key2=value2"))
Content = new ByteArrayContent("key1=value1&key2=value2"u8.ToArray())
{
Headers =
{
Expand All @@ -319,8 +319,8 @@ public async Task When_configuring_formData_should_match_multipart(string urlEnc
{
var content = new MultipartFormDataContent
{
{ new ByteArrayContent(Encoding.UTF8.GetBytes("value1")), "key1" },
{ new ByteArrayContent(Encoding.UTF8.GetBytes("éôxÄ")), "key2" },
{ new ByteArrayContent("value1"u8.ToArray()), "key1" },
{ new ByteArrayContent("éôxÄ"u8.ToArray()), "key2" },
{ new StringContent("file content 1", Encoding.UTF8, MediaTypes.PlainText), "file1", "file1.txt" }
};
var request = new HttpRequestMessage
Expand Down Expand Up @@ -679,8 +679,11 @@ public void Given_null_argument_when_executing_method_it_should_throw(params obj

public static IEnumerable<object?[]> TestCases()
{
var streamMock = new Mock<Stream> { CallBase = true };
streamMock.SetReturnsDefault(true);
Stream streamMock = Substitute.For<Stream>();
streamMock.CanRead.Returns(true);
streamMock.CanWrite.Returns(true);
streamMock.CanSeek.Returns(true);
streamMock.CanTimeout.Returns(true);
var uri = new Uri("http://0.0.0.0");
var instance = new RequestMatching();

Expand Down Expand Up @@ -830,11 +833,11 @@ public void Given_null_argument_when_executing_method_it_should_throw(params obj
DelegateTestCase.Create(
RequestMatchingExtensions.Body,
instance,
Encoding.UTF8.GetBytes("content")),
"content"u8.ToArray()),
DelegateTestCase.Create(
RequestMatchingExtensions.Body,
instance,
streamMock.Object),
streamMock),
DelegateTestCase.Create(
RequestMatchingExtensions.WithoutBody,
instance),
Expand All @@ -850,11 +853,11 @@ public void Given_null_argument_when_executing_method_it_should_throw(params obj
DelegateTestCase.Create(
RequestMatchingExtensions.PartialBody,
instance,
Encoding.UTF8.GetBytes("partial content")),
"partial content"u8.ToArray()),
DelegateTestCase.Create(
RequestMatchingExtensions.PartialBody,
instance,
streamMock.Object),
streamMock),
DelegateTestCase.Create<RequestMatching, Action<RequestMatching>, RequestMatching>(
RequestMatchingExtensions.Any,
instance,
Expand Down
Loading

0 comments on commit 66c66da

Please sign in to comment.