Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocking Migration to NSubstitute #241

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/EvoSC.Common/Config/Stores/TomlConfigStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
{
if (!File.Exists(path))
{
string directory = Path.GetDirectoryName(path);

Check warning on line 22 in src/EvoSC.Common/Config/Stores/TomlConfigStore.cs

View workflow job for this annotation

GitHub Actions / build_and_test

Converting null literal or possible null value to non-nullable type.

if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);

Check warning on line 26 in src/EvoSC.Common/Config/Stores/TomlConfigStore.cs

View workflow job for this annotation

GitHub Actions / build_and_test

Possible null reference argument for parameter 'path' in 'DirectoryInfo Directory.CreateDirectory(string path)'.
}

_document = CreateDefaultConfig();
Expand Down Expand Up @@ -115,7 +115,7 @@
var index = int.Parse(key[(indexStart + 1)..^1], CultureInfo.InvariantCulture);
var value = _document.GetValue(key[..indexStart]) as TomlArray;

return value?.Skip(index)?.FirstOrDefault()?.StringValue;
return value?.Skip(index).FirstOrDefault()?.StringValue;
}

var keyValue = _document.GetValue(key);
Expand Down
26 changes: 13 additions & 13 deletions src/EvoSC.Testing/Controllers/ControllerContextMock.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
using EvoSC.Common.Interfaces.Controllers;
using EvoSC.Common.Interfaces.Services;
using EvoSC.Common.Interfaces.Util.Auditing;
using Moq;
using NSubstitute;

namespace EvoSC.Testing.Controllers;

public class ControllerContextMock<TContext> where TContext : class, IControllerContext
{
private Mock<IContextService> _contextService;
private Mock<TContext> _context;
private Mock<IAuditService> _auditService;
private Mock<IAuditEventBuilder> _auditEventBuilder;
private IContextService _contextService;
private TContext _context;
private IAuditService _auditService;
private IAuditEventBuilder _auditEventBuilder;

/// <summary>
/// The context mock.
/// </summary>
public Mock<TContext> Context => _context;
public TContext Context => _context;

/// <summary>
/// The context service mock.
/// </summary>
public Mock<IContextService> ContextService => _contextService;
public IContextService ContextService => _contextService;

/// <summary>
/// The audit service mock.
/// </summary>
public Mock<IAuditService> AuditService => _auditService;
public IAuditService AuditService => _auditService;

/// <summary>
/// The audit event builder mock.
/// </summary>
public Mock<IAuditEventBuilder> AuditEventBuilder => _auditEventBuilder;
public IAuditEventBuilder AuditEventBuilder => _auditEventBuilder;

public ControllerContextMock()
{
_auditService = new Mock<IAuditService>();
_auditService = Substitute.For<IAuditService>();
_auditEventBuilder = Mocking.NewAuditEventBuilderMock();

_context = new Mock<TContext>();
_context.Setup(c => c.AuditEvent).Returns(_auditEventBuilder.Object);
_context = Substitute.For<TContext>();
_context.AuditEvent.Returns(_auditEventBuilder);

_contextService = Mocking.NewContextServiceMock(Context.Object, null);
_contextService = Mocking.NewContextServiceMock(Context, null);
}
}
8 changes: 4 additions & 4 deletions src/EvoSC.Testing/Controllers/ManialinkControllerTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
using EvoSC.Common.Interfaces.Models;
using EvoSC.Manialinks.Interfaces;
using EvoSC.Manialinks.Interfaces.Models;
using Moq;
using NSubstitute;

namespace EvoSC.Testing.Controllers;

public class ManialinkControllerTestBase<TController> : ControllerMock<TController, IManialinkInteractionContext>
where TController : class, IController
{
private Mock<IManialinkManager> _mlManager = new();
private readonly IManialinkManager _mlManager = Substitute.For<IManialinkManager>();

/// <summary>
/// The manialink manager mock used for this mock.
/// </summary>
public Mock<IManialinkManager> ManialinkManager => _mlManager;
public IManialinkManager ManialinkManager => _mlManager;

/// <summary>
/// Initialize this controller mock.
Expand All @@ -25,6 +25,6 @@ public class ManialinkControllerTestBase<TController> : ControllerMock<TControll
protected void InitMock(IOnlinePlayer actor, IManialinkActionContext actionContext, params object[] services)
{
base.InitMock(services);
this.SetupMock(actor, actionContext, _mlManager.Object);
this.SetupMock(actor, actionContext, _mlManager);
}
}
3 changes: 2 additions & 1 deletion src/EvoSC.Testing/EvoSC.Testing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.15.0.81779">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
82 changes: 41 additions & 41 deletions src/EvoSC.Testing/Mocking.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
using EvoSC.Manialinks.Interfaces.Models;
using EvoSC.Testing.Controllers;
using GbxRemoteNet.Interfaces;
using Moq;
using NSubstitute;

namespace EvoSC.Testing;

Expand All @@ -38,8 +38,8 @@ public static ControllerContextMock<TContext> NewControllerContextMock<TContext>
public static ControllerContextMock<IPlayerInteractionContext> SetupMock(
this ControllerContextMock<IPlayerInteractionContext> mock, IOnlinePlayer actor)
{
mock.Context.Setup(c => c.Player).Returns(actor);
mock.Context.Object.AuditEvent.CausedBy(actor);
mock.Context.Player.Returns(actor);
mock.Context.AuditEvent.CausedBy(actor);

return mock;
}
Expand All @@ -62,8 +62,8 @@ public static ControllerContextMock<IPlayerInteractionContext>
public static ControllerContextMock<ICommandInteractionContext> SetupMock(
this ControllerContextMock<ICommandInteractionContext> mock, IOnlinePlayer actor)
{
mock.Context.Setup(c => c.Player).Returns(actor);
mock.Context.Object.AuditEvent.CausedBy(actor);
mock.Context.Player.Returns(actor);
mock.Context.AuditEvent.CausedBy(actor);

return mock;
}
Expand All @@ -89,10 +89,10 @@ public static ControllerContextMock<IManialinkInteractionContext> SetupMock(
this ControllerContextMock<IManialinkInteractionContext> mock, IOnlinePlayer actor,
IManialinkActionContext actionContext, IManialinkManager mlManager)
{
mock.Context.Setup(c => c.Player).Returns(actor);
mock.Context.Setup(c => c.ManialinkAction).Returns(actionContext);
mock.Context.Setup(m => m.ManialinkManager).Returns(mlManager);
mock.Context.Object.AuditEvent.CausedBy(actor);
mock.Context.Player.Returns(actor);
mock.Context.ManialinkAction.Returns(actionContext);
mock.Context.ManialinkManager.Returns(mlManager);
mock.Context.AuditEvent.CausedBy(actor);

return mock;
}
Expand Down Expand Up @@ -122,15 +122,14 @@ public static TController NewControllerMock<TController, TContext>(ControllerCon
where TController : class, IController
where TContext : class, IControllerContext
{
var ctorArgs = services.Select(s => s.GetType().IsAssignableTo(typeof(Mock)) ? ((Mock)s).Object : s).ToArray();
var controller = Activator.CreateInstance(typeof(TController), ctorArgs) as TController;
var controller = Activator.CreateInstance(typeof(TController), services) as TController;

if (controller == null)
{
throw new InvalidOperationException($"Failed to create instance of controller {typeof(TController)}");
}

controller.SetContext(contextMock.Context.Object);
controller.SetContext(contextMock.Context);

return controller;
}
Expand Down Expand Up @@ -159,16 +158,17 @@ public static (TController Controller, ControllerContextMock<TContext> ContextMo
/// <param name="context">The context which the context service will use.</param>
/// <param name="actor">The actor that triggered the action.</param>
/// <returns></returns>
public static Mock<IContextService> NewContextServiceMock(IControllerContext context, IOnlinePlayer? actor)
public static IContextService NewContextServiceMock(IControllerContext context, IOnlinePlayer? actor)
{
var mock = new Mock<IContextService>();
var auditEvent = context.AuditEvent;
var mock = Substitute.For<IContextService>();

mock.Setup(s => s.Audit()).Returns(context.AuditEvent);
mock.Setup(s => s.GetContext()).Returns(context);
mock.Audit().Returns(auditEvent);
mock.GetContext().Returns(context);

if (actor != null)
{
mock.Object.Audit().CausedBy(actor);
mock.Audit().CausedBy(actor);
}

return mock;
Expand All @@ -181,25 +181,25 @@ public static Mock<IContextService> NewContextServiceMock(IControllerContext con
/// <returns></returns>
public static Locale NewLocaleMock(IContextService contextService)
{
var config = new Mock<IEvoScBaseConfig>();
config.Setup(m => m.Locale.DefaultLanguage).Returns("en");
var localeManager = new Mock<ILocalizationManager>();
localeManager.Setup(m => m.GetString(It.IsAny<CultureInfo>(), It.IsAny<string>(), It.IsAny<object[]>()))
var config = Substitute.For<IEvoScBaseConfig>();
config.Locale.DefaultLanguage.Returns("en");
var localeManager = Substitute.For<ILocalizationManager>();
localeManager.GetString(Arg.Any<CultureInfo>(), Arg.Any<string>(), Arg.Any<object[]>())
.Returns("Test_Locale_String");

var locale = new LocaleResource(localeManager.Object, contextService, config.Object);
var locale = new LocaleResource(localeManager, contextService, config);
return locale;
}

/// <summary>
/// Create a new mock of the server client and it's GBXRemoteClient.
/// </summary>
/// <returns></returns>
public static (Mock<IServerClient> Client, Mock<IGbxRemoteClient> Remote) NewServerClientMock()
public static (IServerClient Client, IGbxRemoteClient Remote) NewServerClientMock()
{
var remote = new Mock<IGbxRemoteClient>();
var client = new Mock<IServerClient>();
client.Setup(m => m.Remote).Returns(remote.Object);
var remote = Substitute.For<IGbxRemoteClient>();
var client = Substitute.For<IServerClient>();
client.Remote.Returns(remote);

return (client, remote);
}
Expand All @@ -209,22 +209,22 @@ public static (Mock<IServerClient> Client, Mock<IGbxRemoteClient> Remote) NewSer
/// simply returns itself. Is used to verify the methods which are called for checking if auditing occured.
/// </summary>
/// <returns></returns>
public static Mock<IAuditEventBuilder> NewAuditEventBuilderMock()
public static IAuditEventBuilder NewAuditEventBuilderMock()
{
var builder = new Mock<IAuditEventBuilder>();

builder.Setup(m => m.CausedBy(It.IsAny<IPlayer>())).Returns(builder.Object);
builder.Setup(m => m.Comment(It.IsAny<string>())).Returns(builder.Object);
builder.Setup(m => m.HavingProperties(It.IsAny<object>())).Returns(builder.Object);
builder.Setup(m => m.Cancel()).Returns(builder.Object);
builder.Setup(m => m.Cancel(It.IsAny<bool>())).Returns(builder.Object);
builder.Setup(m => m.Error()).Returns(builder.Object);
builder.Setup(m => m.Info()).Returns(builder.Object);
builder.Setup(m => m.Success()).Returns(builder.Object);
builder.Setup(m => m.UnCancel()).Returns(builder.Object);
builder.Setup(m => m.WithStatus(It.IsAny<AuditEventStatus>())).Returns(builder.Object);
builder.Setup(m => m.WithEventName(It.IsAny<string>())).Returns(builder.Object);
builder.Setup(m => m.WithEventName(It.IsAny<Enum>())).Returns(builder.Object);
var builder = Substitute.For<IAuditEventBuilder>();

builder.CausedBy(Arg.Any<IPlayer>()).Returns(builder);
builder.Comment(Arg.Any<string>()).Returns(builder);
builder.HavingProperties(Arg.Any<object>()).Returns(builder);
builder.Cancel().Returns(builder);
builder.Cancel(Arg.Any<bool>()).Returns(builder);
builder.Error().Returns(builder);
builder.Info().Returns(builder);
builder.Success().Returns(builder);
builder.UnCancel().Returns(builder);
builder.WithStatus(Arg.Any<AuditEventStatus>()).Returns(builder);
builder.WithEventName(Arg.Any<string>()).Returns(builder);
builder.WithEventName(Arg.Any<Enum>()).Returns(builder);

return builder;
}
Expand Down
42 changes: 19 additions & 23 deletions src/EvoSC.Testing/Verifications.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Microsoft.Extensions.Logging;
using Moq;
using NSubstitute;
using NSubstitute.ReceivedExtensions;

namespace EvoSC.Testing;

public static class Verifications
{

/// <summary>
/// Helper method for verifying that a logging call was made.
/// </summary>
Expand All @@ -14,37 +16,31 @@ public static class Verifications
/// <param name="msg">Message which was logged.</param>
/// <param name="times">How many times this log was called.</param>
/// <typeparam name="T">The type which this logger is assigned to.</typeparam>
public static void Verify<T>(this Mock<ILogger<T>> loggerMock, LogLevel logLevel, Exception? exception, string? msg,
Times times)
public static void Verify<T>(this ILogger<T> loggerMock, LogLevel logLevel, Exception? exception, string? msg, Quantity times)
{
if (exception == null)
{
loggerMock.Verify(m => m.Log(
loggerMock.Received(times).Log(
logLevel,
0,
It.Is<It.IsAnyType>((o, type) =>
msg == null || (
o.ToString()!.StartsWith(msg, StringComparison.Ordinal)
&&type.Name.Equals("FormattedLogValues", StringComparison.Ordinal)
)),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()
), times);
Arg.Any<EventId>(),
Arg.Is<object>(o => msg == null || (
o.ToString()!.StartsWith(msg, StringComparison.Ordinal)
&& o.GetType().Name.Equals("FormattedLogValues", StringComparison.Ordinal)
)),
Arg.Any<Exception>(),
Arg.Any<Func<object, Exception?, string>>());
}
else
{
// duplicate code is required due to the way moq works with it's expression system
loggerMock.Verify(m => m.Log(
loggerMock.Received(times).Log(
logLevel,
0,
It.Is<It.IsAnyType>((o, type) =>
msg == null || (
o.ToString()!.StartsWith(msg, StringComparison.Ordinal)
&&type.Name.Equals("FormattedLogValues", StringComparison.Ordinal)
)),
Arg.Any<EventId>(),
Arg.Is<object>(o => msg == null || (
o.ToString()!.StartsWith(msg, StringComparison.Ordinal)
&& o.GetType().Name.Equals("FormattedLogValues", StringComparison.Ordinal)
)),
exception,
It.IsAny<Func<It.IsAnyType, Exception, string>>()
), times);
Arg.Any<Func<object, Exception?, string>>());
}
}
}
2 changes: 1 addition & 1 deletion tests/EvoSC.Commands.Tests/CommandParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public CommandParserTests()
_cmdManager.AddCommand(cmd);
}

ValueReaderManager GetFullValueReader()
private ValueReaderManager GetFullValueReader()
{
var valueReader = new ValueReaderManager();
valueReader.AddReader(new FloatReader());
Expand Down
Loading
Loading