How to mock Microsoft.VisualStudio.Services.WebApi.VssConnection - c#

I've created an application that uses the MS GitHttpClient class to read commits in an AzureDevOps project. I would like to make a unit test of the logic, so I need to mock the VssConnection and GitHttpClient. Neither of the two classes implements any interface.
I can mock the GitHttpClient and make it return commit refs when calling GitHttpClient.GetCommitsAsync(...) but when I try to mock VssConnection.GetClient<GitHttpClient>() I get the following exception
Test method mycli.Tests.Unit.Services.GitServiceTests.TestVssConnectionMock threw exception:
System.NotSupportedException: Unsupported expression: conn => conn.GetClient<GitHttpClient>()
Non-overridable members (here: VssConnection.GetClient) may not be used in setup / verification expressions.
Here is my test class. The first test TestVssConnection fails with the above exception. The second test TestGitHttpClientMock passes.
[TestClass]
public class GitServiceTests
{
[TestMethod]
public async Task TestVssConnectionMock()
{
var vssConnectionMock = new Mock<VssConnection>(new Uri("http://fake"), new VssCredentials());
var gitHttpClientMock = new Mock<GitHttpClient>(new Uri("http://fake"), new VssCredentials());
gitHttpClientMock.Setup(client => client.GetCommitsAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<GitQueryCommitsCriteria>(), null, null, null, It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<GitCommitRef> { new GitCommitRef { Comment = "abc" } }));
vssConnectionMock.Setup(conn => conn.GetClient<GitHttpClient>()).Returns(gitHttpClientMock.Object);
// EXCEPTION THROWN ABOVE ^
var gitHttpClient = vssConnectionMock.Object.GetClient<GitHttpClient>();
var commits = await gitHttpClient.GetCommitsAsync("", "", new GitQueryCommitsCriteria());
Assert.IsTrue(commits.Count == 1);
}
[TestMethod]
public async Task TestGitHttpClientMock()
{
var gitHttpClientMock = new Mock<GitHttpClient>(new Uri("http://fake"), new VssCredentials());
gitHttpClientMock.Setup(client => client.GetCommitsAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<GitQueryCommitsCriteria>(), null, null, null, It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<GitCommitRef> { new GitCommitRef { Comment = "abc" } }));
var commits = await gitHttpClientMock.Object.GetCommitsAsync("", "", new GitQueryCommitsCriteria());
Assert.IsTrue(commits.Count == 1);
}
}
My question is, how do I mock VssConnection.GetClient<GitHttpClient>() so it returns my mock of GitHttpClient?
Is the workaround to make a wrapper of VssConnection? And if so, how is that best done?
I am using .NET 6, MsTest and MoQ.

So far my own solution is to use the decorator pattern to wrap the VssConnection with an interface like this:
//Using decorator pattern to wrap VssConnection so it can be mocked. VssConnection is not mock-able out of the box.
public interface IVssConnection : IDisposable
{
public T GetClient<T>() where T : VssHttpClientBase => this.GetClientAsync<T>().SyncResult<T>();
public Task<T> GetClientAsync<T>(CancellationToken cancellationToken = default(CancellationToken)) where T : VssHttpClientBase;
}
public class VssConnectionWrapper : VssConnection, IVssConnection
{
public VssConnectionWrapper(Uri baseUrl, VssCredentials credentials) : base(baseUrl, credentials)
{
}
}
This way I can test the VssConnection like this:
[TestMethod]
public async Task TestVssConnectionMock()
{
var vssConnectionMock = new Mock<IVssConnection>();
var gitHttpClientMock = new Mock<GitHttpClient>(new Uri("http://fake"), new VssCredentials());
gitHttpClientMock.Setup(client => client.GetCommitsAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<GitQueryCommitsCriteria>(), null, null, null, It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<GitCommitRef> { new GitCommitRef { Comment = "abc" } }));
vssConnectionMock.Setup(conn => conn.GetClient<GitHttpClient>()).Returns(gitHttpClientMock.Object);
var gitHttpClient = vssConnectionMock.Object.GetClient<GitHttpClient>();
var commits = await gitHttpClient.GetCommitsAsync("", "", new GitQueryCommitsCriteria());
Assert.IsTrue(commits.Count == 1);
}
Only chenge is
var vssConnectionMock = new Mock<VssConnection>(new Uri("http://fake"), new VssCredentials());
// REPLACED WITH:
var vssConnectionMock = new Mock<IVssConnection>();
BUT if anyone have a solution where I can just use Moq without having to create a decorator then please let me know :-)

I feel your pain. The lack of an interface for either the VssConnection class or the GitHttpClient class makes unit testing difficult.
I offer you this possible alternative which may or may NOT be any better than your existing approach:
Use the C# 'dynamic' duck typing keyword to pretend that C# is Python. :)
I did it like this:
First in your client code that you want to unit test, define the externally connecting pieces like the VssConnection and the GitHttpClient as 'dynamic' types.
public class TFSCustomLib
{
private dynamic gitClient;
public TFSCustomLib(dynamic connection)
{
gitClient = connection.GetClient<GitHttpClient>();
}
...
}
Then, in the unit test class itself define an IMock interface along with a MoqTfsConnection property and a TestMethod that uses that property like this:
public class TFSCustomLibTests
{
public interface IMock
{
public dynamic GetClient<GitHttpClient>();
public dynamic GetRepositoriesAsync();
}
private static IMock MoqTfsConnection
{
get
{
Mock<IMock> icc = new();
icc.Setup(x => x.GetClient<GitHttpClient>()).Returns(icc.Object);
icc.Setup(x => x.GetRepositoriesAsync()).Returns(
Task<List<GitRepository>>.FromResult(
new List<GitRepository>() {
new GitRepository() })); //TODO: Add any number of GitRepository instances configured for unit testing.
return icc.Object;
}
}
[TestMethod()]
public void TestTFSDependencyReporting()
{
TFSDependencyReporter testReporter = new(MoqTfsConnection);
Assert.AreEqual(1, testReporter.Reports.Count);
}
}
That should allow you to use Moq to mock out the VssConnection and GitHttpClient dependencies as needed.
Your milage may vary and your original approach may even be a better one. For me, having worked with Python for so long, I looked for a more pythonic approach. The 'dynamic' keyword fit the duck typed bill (pardon the pun.) The fact that C# eventually (after .Net 4.0 I think) provided the 'dynamic' keyword seems like maybe Python was/is ahead of the game in many ways.

Related

How to mock AsyncPolicyWrap or AsyncPolicy in .Net Core using FakeItEasy

I have a code like this (I have to test a repo, you'll see the code below)
public class SomeClass
{
public AsyncPolicyWrap PropName { get; }
public SomeClass(...)
{
PropName = Policy.WrapAsync(someRetry,someCircuitBreaker)
// here there are passed some methods that return someRetry - AsyncRetryPolicy
// and someCircuitBreaker - AsyncCircuitBreakerPolicy
}
}
then I have another repo class
public class SomeRepo : ISomeRepo
{
private readonly AsyncPolicy _somePolicy;
public SomeRepo(..., SomeClass someClass) : base(...)
{
_somePolicy = someClass.PropName;
}
public async Task<Result<SomeDTO>> GetDTO(Guid someId)
{
var someResponse = await _somePolicy.ExecuteAsync(() =>
HttpClient.GetAsync(serviceName, $"endpointUrl"));
...
}
}
2 pieces of code above can't be changed cause they are in prod and I as a junior dev just have to cover code with tests if possible
I have tried to write a test like this
[TestMethod]
public async Task DoStuff()
{
var repository = DefaultSome();
var result = await repository.GetDTO(new Guid());
result.ShouldNotBeNull(); // don't pay attention I'll change stuff which has to be asserted
}
private SomeRepo DefaultSome(Some some = null)
{
some = some ?? A.Fake<ISome>();
/// HERE I TRIED TO MOCK STUFF IN DIFFERENT WAYS AND I HAVE AN ERROR
var policyWrap = A.Dummy<AsyncPolicyWrap>();
//var test = Policy.WrapAsync(A.Fake<AsyncRetryPolicy>(), A.Fake<AsyncCircuitBreakerPolicy>());
//var test = Policy.WrapAsync(A.Fake<IAsyncPolicy>(), A.Fake<IAsyncPolicy>());
A.CallTo(() =>
policyWrap.ExecuteAsync(A<Func<Task<HttpResponseMessage>>>._))
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
var policy = A.Fake<RetryPolicies>();
A.CallTo(() =>
policy.PropName)
.Returns(policyWrap);
return new SomeRepo(some, ..., policy);
}
here is an error i get
I get similar for commented // var test = ... variats
Concrete vs Abstract
Whenever you need to mock something then rely on abstraction rather than concrete implementation.
AsyncPolicyWrap is a concrete class not an abstract like AsyncPolicy
Also as the exception says this class does not have a public parameterless constructor.
It has an internal ctor with 2 parameters:
internal AsyncPolicyWrap(AsyncPolicy outer, IAsyncPolicy inner)
: base(outer.ExceptionPredicates)
{
_outer = outer;
_inner = inner;
}
So, you should prefer AsyncPolicy abstract class or IAsyncPolicy interface.
With or without result
Please be aware that in Polly each Policy has two versions:
One which does not return any result
One which does return some result
Based on the SomeRepo's code your Policy should return an HttpResponseMessage.
So, you should use IAsyncPolicy<HttpResponseMessage> or AsyncPolicy<HttpResponseMessage> to indicate that your policy will return an HttpResponseMessage.
Mocking
Whenever you mock an IAsyncPolicy<HttpResponseMessage> then you don't have to recreate the combined policy (like you did in the comments). All you have to do is to define how should the ExecuteAsync behave.
Happy path:
var mockedPolicy = new Mock<IAsyncPolicy<HttpResponseMessage>>();
mockedPolicy
.Setup(policy => policy.ExecuteAsync(It.IsAny<Func<Task<HttpResponseMessage>>>()))
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK));
Unhappy path:
var mockedPolicy = new Mock<IAsyncPolicy<HttpResponseMessage>>();
mockedPolicy
.Setup(policy => policy.ExecuteAsync(It.IsAny<Func<Task<HttpResponseMessage>>>()))
.ThrowsAsync(new HttpRequestException("Something bad happened"));
I've used moq to mock the policy but the same concept can be applied for FakeItEasy.

How to wrap Messenger and IMessenger from Microsoft Toolkti Mvvm for UnitTesting

I want to migrate from MVVM Light to Microsoft Toolkit MVVM and I have a problem with my unit tests.
I don't know how properly wrap IMessengerExtensions and IMessenger to use it in unit testing.
Example of test I have in my project:
public void LoadingFinishedTest()
{
var messengerMock = new Mock<IMessenger>();
messengerMock.Setup(mock => mock.Send(It.Is<IsLoadingMessage>()));
var testedViewModelMock = new Mock<SomeViewModel>(messengerMock.Object);
testedViewModelMock.Object.LoadingFinished();
messengerMock.Verify(mock => mock.Send(It.Is<IsLoadingMessage>(), Times.Once);
}
And of course if I do not wrap anything and just try to run test I get:
System.NotSupportedException : Type to mock must be an interface, a delegate, or a non-sealed, non-static class.
Assuming SomeViewModel is het subject under test, an actual instance of this should be used to exercise the test case
public void LoadingFinishedTest() {
// Arrange
var messengerMock = new Mock<IMessenger>();
messengerMock.Setup(mock => mock.Send(It.Is<IsLoadingMessage>()));
var subject = new SomeViewModel(messengerMock.Object);
// Act
subject.Object.LoadingFinished();
// Assert
messengerMock.Verify(mock => mock.Send(It.Is<IsLoadingMessage>(), Times.Once);
}
The verification can also be configured during setup
For example
public void LoadingFinishedTest() {
// Arrange
var messengerMock = new Mock<IMessenger>();
messengerMock
.Setup(mock => mock.Send(It.Is<IsLoadingMessage>()))
.Verifiable(); //<-- NOTE THIS
var subject = new SomeViewModel(messengerMock.Object);
// Act
subject.Object.LoadingFinished();
// Assert
messengerMock.Verify(); //<-- verifying expected behavior that was setup
}
I also ran into problems with this. For me the issue was that IMessenger.Send<TMessage> is an extension method. Unfortunately, as I've learned, Moq cannot mock extension methods because they are static.
My solution was to create an IMessengerWrapper which can be mocked:
// Mockable interface
public interface IMessengerWrapper
{
TMessage Send<TMessage>(TMessage message)
where TMessage : class;
}
// Real implementation for actual code
public class MessengerWrapper : IMessengerWrapper
{
private IMessenger _messenger;
public MessengerWrapper(IMessenger messenger)
{
_messenger = messenger;
}
public TMessage Send<TMessage>(TMessage message)
where TMessage : class
{
return _messenger.Send(message);
}
}
Which can then be used in place of IMessenger in your unit tests, e.g.:
public void LoadingFinishedTest()
{
var messengerMock = new Mock<IMessengerWrapper>();
// Using It.IsAny here because It.Is requires a predicate
messengerMock.Setup(mock => mock.Send(It.IsAny<IsLoadingMessage>()));
var testedViewModelMock = new Mock<SomeViewModel>(messengerMock.Object);
testedViewModelMock.Object.LoadingFinished();
messengerMock.Verify(mock => mock.Send(It.IsAny<IsLoadingMessage>(), Times.Once);
}
Sources:
How do I use Moq to mock an extension method?
Mocking Static Methods for Unit Testing

How to properly moq ExecuteQuery method within DataContext?

I'm having a difficult time trying to understand how to appropriately return mocked data from a simulated database call in a unit test.
Here's an example method I want to unit test (GetBuildings):
public class BuildingService : IBuildingService {
public IQueryable<Building> GetBuildings(int propertyId)
{
IQueryable<Building> buildings;
// Execution path for potential exception thrown
// if (...) throw new SpecialException();
// Another execution path...
// if (...) ...
using (var context = DataContext.Instance())
{
var Params = new List<SqlParameter>
{
new SqlParameter("#PropertyId", propertyId)
};
// I need to return mocked data here...
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
So GetBuildings calls a stored procedure.
So I need to mock the DataContext, that of which I can override and set a testable instance. So what happens here is, in the above example DataContext.Instance() does return the mocked object.
[TestFixture]
public class BuildingServiceTests
{
private Mock<IDataContext> _mockDataContext;
[SetUp]
public void Setup() {
_mockDataContext = new Mock<IDataContext>();
}
[TearDown]
public void TearDown() {
...
}
[Test]
public void SomeTestName() {
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
DataContext.SetTestableInstance(_mockDataContext.Object);
var builings = BuildingService.GetBuildings(1, 1);
// Assert...
}
Please ignore some of the parameters, like propertyId. I've stripped those out and simplified this all. I simply can't get the ExecuteQuery method to return any data.
All other simple peta-poco type methods I can mock without issue (i.e. Get, Insert, Delete).
Update
DataContext.Instance returns the active instance of the DataContext class, if exists, and if not exists, returns a new one. So the method of test under question returns the mocked instance.
Do not mock DataContext. Because mocking DataContext will produce tests tightly coupled to the implementation details of DataContext. And you will be forced to change tests for every change in the code even behavior will remain same.
Instead introduce a "DataService" interface and mock it in the tests for BuildingService.
public interface IDataService
{
IEnumerable<Building> GetBuildings(int propertyId)
}
Then, you can tests implementation of IDataService agains real database as part of integration tests or tests it agains database in memory.
If you can test with "InMemory" database (EF Core or Sqlite) - then even better -> write tests for BuildingService against actual implementation of DataContext.
In tests you should mock only external resources (web service, file system or database) or only resources which makes tests slow.
Not mocking other dependencies will save you time and give freedom while you refactoring your codebase.
After update:
Based on the updated question, where BuildingService have some execution path - you can still testing BuildingService and abstract data related logic to the IDataService.
For example below is BuildingService class
public class BuildingService
{
private readonly IDataService _dataService;
public BuildingService(IDataService dataService)
{
_dataService = dataService;
}
public IEnumerable<Building> GetBuildings(int propertyId)
{
if (propertyId < 0)
{
throw new ArgumentException("Negative id not allowed");
}
if (propertyId == 0)
{
return Enumerable.Empty<Building>();
}
return _myDataService.GetBuildingsOfProperty(int propertyId);
}
}
In tests you will create a mock for IDataService and pass it to the constructor of BuildingService
var fakeDataService = new Mock<IDataContext>();
var serviceUnderTest = new BuildingService(fakeDataService);
Then you will have tests for:
"Should throw exception when property Id is negative"
"Should return empty collection when property Id equals zero"
"Should return collection of expected buildings when valid property Id is given"
For last test case you will mock IDataService to return expected building only when correct propertyId is given to _dataService.GetBuildingsOfProperty method
In order for the mock to return data is needs to be set up to behave as expected given a provided input.
currently in the method under test it is being called like this
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
Yet in the test the mock context is being setup like
_mockDataContext.Setup(r =>
r.ExecuteQuery<Building>(CommandType.StoredProcedure, "someSproc"))
.Returns(new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable());
Note what the mock is told to expect as parameters.
The mock will only behave as expected when provided with those parameters. Otherwise it will return null.
Consider the following example of how the test can be exercised based on the code provided in the original question.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
DataContext.SetTestableInstance(_mockDataContext.Object);
var subject = new BuildingService();
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}
That said, the current design of the system under test is tightly coupled to a static dependency which is a code smell and makes the current design follow some bad practices.
The static DataContext which is currently being used as a factory should be refactored as such,
public interface IDataContextFactory {
IDataContext CreateInstance();
}
and explicitly injected into dependent classes instead of calling the static factory method
public class BuildingService : IBuildingService {
private readonly IDataContextFactory factory;
public BuildingService(IDataContextFactory factory) {
this.factory = factory
}
public IQueryable<Building> GetBuildings(int propertyId) {
IQueryable<Building> buildings;
using (var context = factory.CreateInstance()) {
var Params = new List<SqlParameter> {
new SqlParameter("#PropertyId", propertyId)
};
buildings = context
.ExecuteQuery<Building>(System.Data.CommandType.StoredProcedure, "dbo.Building_List", Params.ToArray<object>())
.AsQueryable();
}
return buildings;
}
}
This will allow for a proper mock to be created in injected into the subject under test without using a static workaround hack.
[Test]
public void SomeTestName() {
//Arrange
var expected = new List<Building>() { new Building() { BuildingId = 1, Title = "1" }}.AsQueryable();
_mockDataContext
.Setup(_ => _.ExecuteQuery<Building>(CommandType.StoredProcedure, It.IsAny<string>(), It.IsAny<object[]>()))
.Returns(expected);
var factoryMock = new Mock<IDataContextFactory>();
factoryMock
.Setup(_ => _.CreateInstance())
.Returns(_mockDataContext.Object);
var subject = new BuildingService(factoryMock.Object);
//Act
var actual = subject.GetBuildings(1);
// Assert...
CollectionAssert.AreEquivalent(expected, actual);
}

Mock lazy interface with Moq

I want mock lazy interface but I got object reference not set to an instance of an object exception.
‌Here is class under test:
public class ProductServiceService : IProductServiceService
{
private readonly Lazy<IProductServiceRepository> _repository;
private readonly Lazy<IProductPackageRepository> _productPackageRepository;
public ProductServiceService(
Lazy<IProductServiceRepository> repository,
Lazy<IProductPackageRepository> productPackageRepository)
{
_repository = repository;
_productPackageRepository = productPackageRepository;
}
public async Task<OperationResult> ValidateServiceAsync(ProductServiceEntity service)
{
var errors = new List<ValidationResult>();
if (!await _productPackageRepository.Value.AnyAsync(p => p.Id == service.PackageId))
errors.Add(new ValidationResult(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));
.
.
.
return errors.Any()
? OperationResult.Failed(errors.ToArray())
: OperationResult.Success();
}
}
and here is test class
[Fact, Trait("Category", "Product")]
public async Task Create_Service_With_Null_Financial_ContactPerson_Should_Fail()
{
// Arrange
var entity = ObjectFactory.Service.CreateService(packageId: 1);
var fakeProductServiceRepository = new Mock<Lazy<IProductServiceRepository>>();
var repo= new Mock<IProductPackageRepository>();
repo.Setup(repository => repository.AnyAsync(It.IsAny<Expression<Func<ProductPackageEntity, bool>>>()));
var fakeProductPackageRepository = new Lazy<IProductPackageRepository>(() => repo.Object);
var sut = new ProductServiceService(fakeProductServiceRepository.Object, fakeProductPackageRepository);
// Act
var result = await sut.AddServiceAsync(service);
// Assert
Assert.False(result.Succeeded);
Assert.Contains(result.ErrorMessages, error => error.Contains(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));
}
fakeProductPackageRepository always is null. I followed this blog post but still I'm getting null reference exception.
How to mock lazy initialization of objects in C# unit tests using Moq
Update:
here is a screen that indicates fakeProductPackageRepository is null.
Here is a refactored version of your example:
[Fact, Trait("Category", "Product")]
public async Task Create_Service_With_Null_Financial_ContactPerson_Should_Fail() {
// Arrange
var entity = ObjectFactory.Service.CreateService(packageId = 1);
var productServiceRepositoryMock = new Mock<IProductServiceRepository>();
var productPackageRepositoryMock = new Mock<IProductPackageRepository>();
productPackageRepositoryMock
.Setup(repository => repository.AnyAsync(It.IsAny<Expression<Func<ProductPackageEntity, bool>>>()))
.ReturnsAsync(false);
//Make use of the Lazy<T>(Func<T>()) constructor to return the mock instances
var lazyProductPackageRepository = new Lazy<IProductPackageRepository>(() => productPackageRepositoryMock.Object);
var lazyProductServiceRepository = new Lazy<IProductServiceRepository>(() => productServiceRepositoryMock.Object);
var sut = new ProductServiceService(lazyProductServiceRepository, lazyProductPackageRepository);
// Act
var result = await sut.AddServiceAsync(service);
// Assert
Assert.False(result.Succeeded);
Assert.Contains(result.ErrorMessages, error => error.Contains(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));
}
UPDATE
The following Minimal, Complete, and Verifiable example of your stated issue passes when tested.
[TestClass]
public class MockLazyOfTWithMoqTest {
[TestMethod]
public async Task Method_Under_Test_Should_Return_True() {
// Arrange
var productServiceRepositoryMock = new Mock<IProductServiceRepository>();
var productPackageRepositoryMock = new Mock<IProductPackageRepository>();
productPackageRepositoryMock
.Setup(repository => repository.AnyAsync())
.ReturnsAsync(false);
//Make use of the Lazy<T>(Func<T>()) constructor to return the mock instances
var lazyProductPackageRepository = new Lazy<IProductPackageRepository>(() => productPackageRepositoryMock.Object);
var lazyProductServiceRepository = new Lazy<IProductServiceRepository>(() => productServiceRepositoryMock.Object);
var sut = new ProductServiceService(lazyProductServiceRepository, lazyProductPackageRepository);
// Act
var result = await sut.MethodUnderTest();
// Assert
Assert.IsTrue(result);
}
public interface IProductServiceService { }
public interface IProductServiceRepository { }
public interface IProductPackageRepository { Task<bool> AnyAsync();}
public class ProductServiceService : IProductServiceService {
private readonly Lazy<IProductServiceRepository> _repository;
private readonly Lazy<IProductPackageRepository> _productPackageRepository;
public ProductServiceService(
Lazy<IProductServiceRepository> repository,
Lazy<IProductPackageRepository> productPackageRepository) {
_repository = repository;
_productPackageRepository = productPackageRepository;
}
public async Task<bool> MethodUnderTest() {
var errors = new List<ValidationResult>();
if (!await _productPackageRepository.Value.AnyAsync())
errors.Add(new ValidationResult("error"));
return errors.Any();
}
}
}
A Lazy<> as a parameter is somewhat unexpected, though not illegal (obviously). Remember that a Lazy<> wrapped around a service is really just deferred execution of a Factory method. Why not just pass the factories to the constructor? You could still wrap the call to the factory in a Lazy<> inside your implementation class, but then you can just fake / mock your factory in your tests and pass that to your sut.
Or, perhaps the reason that you're passing around a Lazy<> is because you're really dealing with a singleton. In that case, I'd still create a factory and take dependencies on the IFactory<>. Then, the factory implementation can include the Lazy<> inside of it.
Often, I solve the singleton requirement (without the lazy loading) via setting a custom object scope for the dependency in my IoC container. For instance, StructureMap makes it easy to set certain dependencies as singleton or per-request-scope in a web application.
I rarely need to assert that I've done a lazy initialization on some service inside of a system-under-test. I might need to verify that I've only initialized a service once per some scope, but that's still easily tested by faking the factory interface.
The thing is that you are creating a Mock of Lazy as fakeProductServiceRepository and later on are returning that instance where just a Mock is needed.
You should change
var fakeProductServiceRepository = new Mock<Lazy<IProductServiceRepository>>();
to
var fakeProductServiceRepository = new Mock<IProductServiceRepository>();

How to use Moq to mock up the StackExchange.Redis ConnectionMultiplexer class?

I am working to mock up behaviors related to the StackExchange.Redis library, but can't figure out how to properly mock the sealed classes it uses. A specific example is in my calling code I'm doing something like this:
var cachable = command as IRedisCacheable;
if (_cache.Multiplexer.IsConnected == false)
{
_logger.Debug("Not using the cache because the connection is not available");
cacheAvailable = false;
}
else if (cachable == null)
{
The key line in there is _cache.Multiplexer.IsConnected where I'm checking to make sure I have a valid connection before using the cache. So in my tests I want to mock up this behavior with something like this:
_mockCache = new Mock<IDatabase>();
_mockCache.Setup(cache => cache.Multiplexer.IsConnected).Returns(false);
However, while that code compiles just fine, I get this error when running the test:
I have also tried mocking the multiplexer class itself, and providing that to my mocked cache, but I run into the fact the multiplexer class is sealed:
_mockCache = new Mock<IDatabase>();
var mockMultiplexer = new Mock<ConnectionMultiplexer>();
mockMultiplexer.Setup(c => c.IsConnected).Returns(false);
_mockCache.Setup(cache => cache.Multiplexer).Returns(mockMultiplexer.Object);
...but that results in this error:
Ultimately I want to control whether that property is true or false in my tests, so is there a correct way to mock up something like this?
Use the interface IConnectionMultiplexer instead of the concrete class ConnectionMultiplexer in your own class.
public interface ICacheable
{
void DoYourJob();
}
public sealed class RedisCacheHandler : ICacheable
{
private readonly IConnectionMultiplexer multiplexer;
public RedisCacheHandler(IConnectionMultiplexer multiplexer)
{
this.multiplexer = multiplexer;
}
public void DoYourJob()
{
var database = multiplexer.GetDatabase(1);
// your code
}
}
Then you could easily mock and test it:
// Arrange
var mockMultiplexer = new Mock<IConnectionMultiplexer>();
mockMultiplexer.Setup(_ => _.IsConnected).Returns(false);
var mockDatabase = new Mock<IDatabase>();
mockMultiplexer
.Setup(_ => _.GetDatabase(It.IsAny<int>(), It.IsAny<object>()))
.Returns(mockDatabase.Object);
var cacheHandler = new RedisCacheHandler(mockMultiplexer.Object);
// Act
cacheHandler.DoYourJob();
// Assert
// your tests
The best approach in my opinion is to wrap all of your Redis interaction in your own class and interface. Something like CacheHandler : ICacheHandler and ICacheHandler. All of your code would only ever speak to ICacheHandler.
This way, you eliminate a hard dependency on Redis (you can swap out the implementation of ICacheHandler as you please). You can also mock all interaction with your caching layer because it's programmed against the interface.
You should not test StackExchange.Redis directly - it is not code you've written.
Not included in the above answer is the more detailed Setup of the mockDatabase instance. I struggled a little bit finding a working example of something as simple as mocking the IDatabase StringGet method (e.g., handling of optional parameters, using RedisKey vs string, using RedisValue vs string, etc.), so thought I would share. Here is what worked for me.
This test setup:
var expected = "blah";
RedisValue expectedValue = expected;
mockDatabase.Setup(db => db.StringGet(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>()))
.Returns(expectedValue);
To affect what is returned by this tested method call:
var redisValue = _connectionMultiplexer.GetDatabase().StringGet(key);
I have solved this problem by using a connection provider class to create the instance of the ConnectionMultiplexer. The connection provider class can simply be injected into your cache service. The benefit of this approach is that the connection provider is the only code not tested (basically a single line of someone else's code) and your cache service can be tested by mocking the injected interfaces as normal.
In the code below my cache service can be tested and only the connection provider class needs to be excluded from code coverage.
public interface IElastiCacheService
{
Task<string> GetAsync(string key);
Task SetAsync(string key, string value, TimeSpan expiry);
}
public class ElastiCacheService : IElastiCacheService
{
private readonly ElastiCacheConfig _config;
private readonly IConnectionMultiplexer _connection = null;
public ElastiCacheService(
IOptions<ElastiCacheConfig> options,
IElastiCacheConnectionProvider connectionProvider)
{
_config = options.Value;
_connection = connectionProvider.GetConnection(_config.FullAddress);
}
public async Task<string> GetAsync(string key)
{
var value = await _connection.GetDatabase().StringGetAsync(key, CommandFlags.PreferReplica);
return value.IsNullOrEmpty ? null : value.ToString();
}
public Task SetAsync(string key, string value, TimeSpan expiry) =>
_connection.GetDatabase().StringSetAsync(key, value, expiry);
}
public interface IElastiCacheConnectionProvider
{
IConnectionMultiplexer GetConnection(string endPoint);
}
[ExcludeFromCodeCoverage]
public class ElastiCacheConnectionProvider : IElastiCacheConnectionProvider
{
public IConnectionMultiplexer GetConnection(string endPoint) =>
ConnectionMultiplexer.Connect(endPoint);
}

Categories

Resources