Unit testing a c# Method using Moq - c#

I'm new to unit testing and the language c sharp and I'm trying to unit test by mocking ISessionStorageService to test the HasGlobaladminaccess function but my setup does not seem to work as expected.
public class Context
{
public asyncTask<bool>HasGlobalAdminAccessAsync(Blazored.SessionStorage.ISessionStorageService sessionStorage)
{
return await HasAccessAsync(sessionStorage, Config.Constants.HAS_GLOBALE_ADMIN_ACCESS);
}
private async Task<bool> HasAccessAsync(Blazored.SessionStorage.ISessionStorageService sessionStorage, string sessionStorageKey)
{
ILogger _logger = Log.ForContext<ContextHelpers>();
try
{
_logger.Debug("HasAccessAsync({sessionStorageKey})", sessionStorageKey);
var access = await sessionStorage.GetItemAsync<bool>(sessionStorageKey);
_logger.Debug("HasAccessAsync({sessionStorageKey})::access={access}", sessionStorageKey, access);
return access;
}
catch (Exception ex)
{
_logger.Debug("HasAccessAsync({sessionStorageKey})::exception={ex}", sessionStorageKey, ex.Message);
return false;
}
}
}
My test method
private readonly Mock<ISessionStorageService> MockStorage = new Mock<ISessionStorageService>();
[Fact()]
public async Task HasGlobalAdminAccessAsyncTest()
{
string guid = System.Guid.NewGuid().ToString();
Context context = new Context();
MockStorage.Setup(foo => foo.GetItemAsync<bool>(guid)).ReturnsAsync(true);
var person = await context.HasGlobalAdminAccessAsync(MockStorage.Object);
Assert.True(person , guid);
}

Either use the same value as what's in Config.Constants.HAS_GLOBALE_ADMIN_ACCESS for the GUID value in your test
Or use
foo => foo.GetItemAsync<bool>(It.IsAny<Guid>())
In your Setup call.
As it's written, you have your test set to only accept the exact GUID that is returned from Guid.NewGuid() in your test, so it's not going to work in general.

Related

How to mock MassTransit Header values in unit test using NSubstitute

I'm trying to create a unit test that will test my MassTransit (RabbitMq) consumer. My consumer is expected a bus header key called UserName. I've simplified my consumer class as follows. I need to find a way to mock the bus header data otherwise the test will always raise an exception when it executes context.Headers.TryGetHeader("UserName", out object value)
I'm using the NSubstitute mocking library. So how do I mock the ConsumeContext<MyDataObject> type, and set a mock header value? I prefer a solution with NSubstitute
public class MyDataObjectCommand
{
public string MyProperty { get; set; }
}
// Consumer class
public class MyConsumer : IConsumer<MyDataObjectCommand>
{
private readonly IHubContext<MessageHub> _hubContext;
public MyConsumer(IHubContext<MessageHub> hubContext)
{
_hubContext = hubContext;
}
public async Task Consume(ConsumeContext<MyDataObjectCommand> context)
{
var myProperty = context.Message.MyProperty;
var userName = TryGetHeader(context, "UserName");
DoSomethingWith(_hubContext, myProperty, userName);
}
private string TryGetHeader(ConsumeContext<MyDataObjectCommand> context, string key, bool errorOnNull = true)
{
if (context.Headers.TryGetHeader(key, out object value))
{
return Convert.ToString(value);
}
if (errorOnNull)
{
throw new MyConsumerException(_reportName, $"{key} is not found", _hubContext);
}
return null;
}
}
// Unit Test (I'm using XUnit)
public class MyConsumerTests
{
private readonly IHubContext<MessageHub> _hubContext;
public MyConsumerTests()
{
_hubContext = Substitute.For<IHubContext<MessageHub>>();
}
[Fact]
public async Task ShouldConsume()
{
// arrange
var mockDataObject = new MyDataObjectCommand() { MyProperty = "x" };
var harness = new InMemoryTestHarness();
harness.Consumer(() => new MyConsumer(_hubContext));
// I need to mock header values on ConsumeContext<MyDataObjectCommand> object here
// but how do I do it using NSubstitute?
// context.Headers["UserName"] = "Bob"; ??
await harness.Start();
try
{
// act
await harness.InputQueueSendEndpoint.Send(mockDataObject);
// assert
Assert.True(await harness.Consumed.Any<MyDataObjectCommand>());
Assert.False(await harness.Consumed.Any<Fault<MyDataObjectCommand>>());
//Assert.Equal(context.Header["UserName"], "Bob"); How do I do the assertion??
}
finally
{
await harness.Stop();
}
}
}
You don’t mock header values when using MassTransit, you just have to set them when you’re sending the message using the test harness. There is absolutely zero need for a mocking framework.
await harness.InputQueueSendEndpoint.Send(mockDataObject,
context => context.Headers.Set(“UserName”, “Bob”));
The header will then be available on the ConsumeContext when the message is delivered to the consumer.

Failed Unit Test

I have a service for adding city in DB. It works good, now I need test it. I have never worked with tests before. I'll be grateful for your recommendations. I would test case - If it isn't null - test passed(I mean if User insert anything - test is passed, if he doesn't insert data - test failed). I need just check townName null or no. It is my TestClass
[TestClass]
public partial class TownRepositoryTests
{
[TestMethod]
public async Task InsertTownName_ReturnTownName()
{
var builder = new RepositoryBuilder<TownRepository>().SetupManager();
using (builder.CreateIsolationScope())
{
var expectedTown = await TestBuilder.SetupTownNameAsync(builder);
var repository = builder.Build();
var townName = expectedTown.Name;
var result = await repository.InsertTown(townName);
Assert.IsNotNull(result);
}
}
}
SetupTownNameAsync method(here I add data). I think it works good, because when I used debug test, I checked that this method return (id=1, name= "TestTown")
public static class TestBuilder
{
public static async Task<Town> SetupTownNameAsync(RepositoryBuilder<TownRepository> builder)
{
var town = await builder.CreateTownAsync(name: "TestTown");
var setupResult = new Town
{
Id = town.Id,
Name = town.Name
};
return setupResult;
}
}
And here my InsertTown method
public async Task<int> InsertTown(string townName)
{
var parameters = new { townName };
return await Connection.QueryFirstOrDefaultAsync<int>(Adhoc["AddTown"], parameters, Transaction);
}
When I checked problem with debug test - in this line returns "TestTown", but when I check it in Assert - my test is failed
var result = await repository.InsertTown(townName);
Assert.IsNotNull(result);

How to test async methods with Moq and xUnit

I am having the following method which calls an insert and update methods.
Conditions
In the below ProcessBspLoan function, I am calling other function named getBspData
if getBspData returns any result. I will insert the result
and update the database as a success for a particular Id
if getBspData throws any Exception I will only update the database as a fail for a particular Id without inserting
Here is the ProcessBspLoan function in the given class
public class BspLoanProcessor : IBspLoanProcessor
{
private readonly IBspClient _bspService;
private readonly IBspRepository _bspRepository;
private readonly ILogger<BspLoanProcessor> _logger;
private readonly IMapper _mapper;
private readonly IFhaRepository _fhaRepository;
public BspLoanProcessor(IBspClient bspService, ILogger<BspLoanProcessor> logger,
IMapper mapper, IBspRepository bSPRepository, IFhaRepository fhaRepository)
{
_bspService = bspService;
_logger = logger;
_mapper = mapper;
_bspRepository = bSPRepository;
_fhaRepository = fhaRepository;
}
public async Task<bool> ProcessBspLoan(BspLoanDetails bspLoanDetails, string transactionId, string triggerType)
{
FhaTransactionDetails fhaData = _mapper.Map<FhaTransactionDetails>(bspLoanDetails);
try
{
////////////////////////////Calling getBspData///////////////////////////////////////
var result = await _bspService.getBspData(bspLoanDetails.NsmLoanNumber, transactionId);
result.TransactionId = transactionId;
await _bspRepository.InsertBspResponse(result);
await _fhaRepository.UpdateFhaTransactionWithLoanNumber(transactionId,"SUCCESS");
return true;
}
catch(Exception ex)
{
await _fhaRepository.UpdateFhaTransactionWithLoanNumber(transactionId,"FAIL");
return false;
}
}
}
I wrote the test method for the above function want to check it is returning true or false based on inputs provided
Here is my test function
public async Task Test1Async()
{
Mock<IBspLoanProcessor> _bspLoanProcessor = new Mock<IBspLoanProcessor>();
Mock<IBspRepository> _bspRepository = new Mock<IBspRepository>();
Mock<IFhaRepository> _fhaRepository = new Mock<IFhaRepository>();
Mock<IBspClient> _bspClient = new Mock<IBspClient>();
BspLoanDetails bspLoanDetails = new BspLoanDetails
{
TriggerType = "BLOB",
Attempts = 1,
FirstRunDateTime = DateTime.Now.ToUniversalTime()
};
----> 1
_bspClient.Setup(x => x.getBspData(It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(new BspResponseDetails()));
----> 2
_bspRepository.Setup(x => x.InsertBspResponse(It.IsAny<BspResponseDetails>())).Returns(Task.CompletedTask);
----> 3
_fhaRepository.Setup(x => x.UpdateFhaTransactionWithLoanNumber(It.IsAny<string>(),It.IsAny<string>())).Returns(Task.CompletedTask);
bool value = await _bspLoanProcessor.Object.ProcessBspLoan(bspLoanDetails, "123", "SCHEDULE");
Assert.True(value);
}
In the above the test, I am checking the first condition
I am returning data as an object whenever anyone called gets data
Inserting BspResponsemethod also I am returning task.completedTask
UpdateFhaTransactionWithLoanNumber also I am returning task.completedTask
The actual expected output is true, but it is returning false.
I am new to Moq and xUnit. Please help me to resolve this issue and also test async and await methods.
IBspClient interface
public interface IBspClient
{
Task<BspResponseDetails> getBspData(string loanNumber,string tid);
Task<BspHealthCheck> GetHealthStatus();
}
IBspRepository Interface
public interface IBspRepository
{
Task InsertBspResponse(BspResponseDetails bsp);
}
IFhaRepository interface
public interface IFhaRepository
{
Task UpdateFhaTransactionWithLoanNumber(string tid, string status);
}
Thanks in advance
bool value = await _bspLoanProcessor.Object
You're calling a method on your mock. You shouldn't mock the system-under-test, you only mock its dependencies.
Just new the class you want to test, otherwise you will be testing that your mock framework does what you set it up to.
And because you didn't setup ProcessBspLoan(), it returns the default for its return value, so false.

Unit test - check on Empty

I developed Unit test for my service. Now my test check on inserting Name and 2nd check on a null.
[TestMethod]
public async Task InsertTownName_ReturnTownName()
{
var builder = new RepositoryBuilder<TownRepository>().SetupManager();
using (builder.CreateIsolationScope())
{
var repository = builder.Build();
var townName = "Town" + DateTime.UtcNow.ToString();
await repository.InsertTown(townName);
var connection = builder.TransactionManager.Connection;
var transaction = builder.TransactionManager.Transaction;
var result = await connection.ReadSingleOrDefaultAsync<Town>(x => x.Name == townName, transaction: transaction);
Assert.IsNotNull(result);
Assert.AreEqual(townName, result.Name);
}
}
[TestMethod]
public async Task InsertNullName()
{
var builder = new RepositoryBuilder<TownRepository>().SetupManager();
using (builder.CreateIsolationScope())
{
var repository = builder.Build();
await repository.InsertTown(null);
var connection = builder.TransactionManager.Connection;
var transaction = builder.TransactionManager.Transaction;
var result = await connection.ReadSingleOrDefaultAsync<Town>(x => x.Name == null, transaction: transaction);
Assert.IsNull(result.Name);
Assert.AreEqual(null, result.Name);
}
}
Both of this method works good. Next step I need check on Empty (If in line where user need insert town name - empty name). I have no idea how implement it. Could You recommend to me and could you check 2nd method for working with null. Is it correct unit test? Here my method that I tested
public async Task<int> InsertTown(string townName)
if (String.IsNullOrEmpty(townName))
{
throw new Exception();
}
else
{
var parameters = new { townName };
return await Connection.QueryFirstOrDefaultAsync<int>(Adhoc["AddTown"], parameters, Transaction);
}
Here is an example of what a method with input parameters checking might look like.
public async Task<int> InsertTown(string townName)
{
if (townName == null)
{
throw new ArgumentNullException(nameof(townName));
}
if (string.IsNullOrWhiteSpace(townName))
{
throw new ArgumentException("Parameter cannot be empty", nameof(townName));
}
var parameters = new { townName };
//return await Connection.QueryFirstOrDefaultAsync<int>(Adhoc["AddTown"], parameters, Transaction);
return await Task.Run(() => 42); // for testing puprposes
}
You can test it like this
[TestMethod]
public async Task InsertTown_NullString_ThrowsArgumentNullExceptionAsync()
{
string townName = null;
// var repository = ...;
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() =>
repository.InsertTown(townName));
}
[DataTestMethod]
[DataRow("")]
[DataRow(" ")]
public async Task InsertTown_EmptyString_ThrowsArgumentExceptionAsync(string value)
{
string townName = value;
// var repository = ...;
await Assert.ThrowsExceptionAsync<ArgumentException>(() =>
repository.InsertTown(townName));
}
As mentioned in the comments, it looks like your unit tests are more like integration tests.
In unit tests, you should not access neither real database, work with a network, nor work with a file system.
You have to mock any dependencies. For example with NSubstitute, FakeItEasy or Moq.
Your Connection.QueryFirstOrDefaultAsync method is static, right?
It is highly recommendable to rewrite the code, getting rid of static methods. With the extraction of abstractions to interfaces. Because static methods, like in your case, are untested or difficult to test.

Returning mocked object from partially mocked object not working

I'm writing a unit test where I'm trying to partially mock a service. What I mean is I want one of the methods of the service to return a different mocked object and another method to behave as normal. This is the method I'm testing:
public async Task<List<string>> GetDeletedRecordIds<T>(DateTime startDate)
where T : ISalesForceObject
{
List<string> result;
try
{
var client = await this.GetForceClient();
var init = await client.GetDeleted<DeletedRecordRootObject>(typeof(T).Name, startDate, DateTime.Now);
result = init?.DeletedRecords.Select(d => d.Id).ToList();
}
catch (Exception e)
{
this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, "GetDeletedRecordIds");
throw;
}
return result;
}
This is the method that I need to return a mocked object:
public async Task<IForceClient> GetForceClient()
{
ForceClient forceClient = null;
try
{
var auth = new AuthenticationClient();
var consumerKey = this._settingService.GetSetting("SalesForceConsumerKey");
var consumerSecret = this._settingService.GetSetting("SalesForceConsumerSecret");
var password = this._settingService.GetSetting("SalesForcePassword");
var securityToken = this._settingService.GetSetting("SalesForceSecurityToken");
var username = this._settingService.GetSetting("SalesForceUsername");
var tokenUrl = $"{this._settingService.GetSetting("SalesForceUrl")}/services/oauth2/token";
await auth.UsernamePasswordAsync(
consumerKey,
consumerSecret,
username,
password + securityToken,
tokenUrl);
forceClient = new ForceClient(auth.InstanceUrl, auth.AccessToken, auth.ApiVersion);
}
catch (Exception e)
{
this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"GetForceClient");
throw;
}
return forceClient;
}
And this is what I currently have in my unit test:
var mockForceClient = new Mock<IForceClient>();
mockForceClient
.Setup(
i => i.GetDeleted<DeletedRecordRootObject>(
It.IsAny<string>(),
It.IsAny<DateTime>(),
It.IsAny<DateTime>())).ReturnsAsync(deletedRecordRootObject);
var mockService = new Mock<IForceDotComService>();
mockService.Setup(m => m.GetDeletedRecordIds<sf.Account>(It.IsAny<DateTime>()))
.Returns(async (DateTime d) => await this._service.GetDeletedRecordIds<sf.Account>(d));
mockService.Setup(m => m.GetForceClient())
.ReturnsAsync(mockForceClient.Object);
Currently, the test runs in GetDeletedRecordIds until it hits the call to the GetForceClient method. Then instead of returning the mocked ForceClient object, it actually tries to run the method which of course fails.
Thanks in advance for any help.
SOLUTION:
Here's how I solved my problem.
First, I created a service to return the ForceClient as follows:
public class ForceClientService : IForceClientService
{
private readonly ILogger _logger;
private readonly ISettingService _settingService;
public ForceClientService(
ILogger<ForceClientService> logger,
ISettingService settingService)
{
this._logger = logger;
this._settingService = settingService;
}
public async Task<IForceClient> GetForceClient()
{
ForceClient forceClient = null;
try
{
var auth = new AuthenticationClient();
var consumerKey = this._settingService.GetSetting("SalesForceConsumerKey");
var consumerSecret = this._settingService.GetSetting("SalesForceConsumerSecret");
var password = this._settingService.GetSetting("SalesForcePassword");
var securityToken = this._settingService.GetSetting("SalesForceSecurityToken");
var username = this._settingService.GetSetting("SalesForceUsername");
var tokenUrl = $"{this._settingService.GetSetting("SalesForceUrl")}/services/oauth2/token";
await auth.UsernamePasswordAsync(
consumerKey,
consumerSecret,
username,
password + securityToken,
tokenUrl);
forceClient = new ForceClient(auth.InstanceUrl, auth.AccessToken, auth.ApiVersion);
}
catch (Exception e)
{
this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"GetForceClient");
throw;
}
return forceClient;
}
}
Then I changed the method I am testing:
public async Task DeleteRecord<TSf>(TSf record)
where TSf : ISalesForceObject
{
try
{
var client = await this._forceClientService.GetForceClient();
var response = await client.DeleteAsync(typeof(TSf).Name, record.Id);
if (!response)
{
throw new Exception($"Error deleting record with ID {record.Id}");
}
}
catch (Exception e)
{
this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, $"ForceDotComService.DeleteRecord");
throw;
}
}
Then I rebuilt my mock to mock the dependencies vs. the methods:
var mockForceClient = new Mock<IForceClient>();
mockForceClient
.Setup(
i => i.GetDeleted<DeletedRecordRootObject>(
It.IsAny<string>(),
It.IsAny<DateTime>(),
It.IsAny<DateTime>())).ReturnsAsync(deletedRecordRootObject);
var mockLogger = new Mock<ILogger<ForceDotComService>>();
var mockForceClientService = new Mock<IForceClientService>();
mockForceClientService.Setup(m => m.GetForceClient()).ReturnsAsync(mockForceClient.Object);
this._service = new ForceDotComService(mockLogger.Object, mockForceClientService.Object);
It is now working as expected. Thanks so much for the help!
Extract this.GetForceClient() out into its own service backed by an abstraction
public IForceClientProvider {
Task<IForceClient> GetForceClient();
}
you would then refactor your current class under test to explicitly depend on that interface via constructor injection.
public class ForceDotComService : IForceDotComService {
private readonly IForceClientProvider provider;
public ForceDotComService(IForceClientProvider provider) {
this.provider = provider;
}
public async Task<List<string>> GetDeletedRecordIds<T>(DateTime startDate)
where T : ISalesForceObject {
List<string> result;
try {
var client = await provider.GetForceClient();
var init = await client.GetDeleted<DeletedRecordRootObject>(typeof(T).Name, startDate, DateTime.Now);
result = init?.DeletedRecords.Select(d => d.Id).ToList();
} catch (Exception e) {
this._logger.LogError(LoggingEvents.GENERAL_ERROR, e, "GetDeletedRecordIds");
throw;
}
return result;
}
}
This would then allow you to mock the desired behavior when testing. In implementation code you would have the same code presented above in the GetForceClient() method.
You mock dependencies, not methods on the class under test.
You need to inject the dependency IForceClient, for example by making it a constructor parameter. Because now your GetForceClient() is simply being called on the class under test, which runs in that class and not on your mock, and so simply returns the new ForceClient() stated in there.

Categories

Resources