I want to mock QueueMessage for unit test,but I can not find any lib to mock
public async Task<QueueMessage[]> ReceiveMessagesAsync(QueueClient queue)
{
QueueProperties properties = queue.GetProperties();
// Retrieve the cached approximate message count.
int cachedMessagesCount = properties.ApproximateMessagesCount;
QueueMessage[] queueMessages =new QueueMessage[cachedMessagesCount];
int num = cachedMessagesCount / 32;
for (int i = 0; i < num + 1; i++)
{
var messages = await queue.ReceiveMessagesAsync(maxMessages: 32);
messages.Value.CopyTo(queueMessages,i*32);
}
return queueMessages;
}
Choice of Mocking lib would be an opinionated answer. There are several mocking frameworks available. One of the popular ones is Moq.
Using Moq, the sample test for your above code would look like below. Note that mocking storage lib is a bit tedious task as you can see.
[Test]
public async Task ReceiveMessagesAsync_StateUnderTest_ExpectedBehavior()
{
// Arrange
var queueClientHelper = new QueueClientHelper();
var queueMock = new Mock<QueueClient>();
var mockPropertiesResponse = new Mock<Response<QueueProperties>>();
var properties = new QueueProperties();
properties.GetType().GetProperty(nameof(properties.ApproximateMessagesCount), BindingFlags.Public | BindingFlags.Instance).SetValue(properties, 64); // little hack since ApproximateMessagesCount has internal setter
mockPropertiesResponse.SetupGet(r => r.Value).Returns(properties);
queueMock.Setup(q => q.GetProperties(It.IsAny<CancellationToken>())).Returns(mockPropertiesResponse.Object);
var mockMessageReponse = new Mock<Response<QueueMessage[]>>();
mockMessageReponse.SetupGet(m => m.Value).Returns(new QueueMessage[32]);
queueMock.Setup(q => q.ReceiveMessagesAsync(It.IsAny<int?>(), It.IsAny<TimeSpan?>(), It.IsAny<CancellationToken>())).ReturnsAsync(mockMessageReponse.Object);
// Act
var result = await queueClientHelper.ReceiveMessagesAsync(queueMock.Object);
// Assert
Assert.AreEqual(64, result.Length);
// verify mocks as required
}
The constructors of the Queue models are internal, but you can create objects using the QueuesModelFactory which provides utilities for mocking.
QueueMessage queueMsg = QueuesModelFactory.QueueMessage(
messageId: "id2",
popReceipt: "pr2",
body: JsonConvert.SerializeObject("Test"),
dequeueCount: 1,
insertedOn: DateTimeOffset.UtcNow);
var metadata = new Dictionary<string, string> { { "key", "value" }, };
int messageCount = 5;
QueueProperties queueProp = QueuesModelFactory.QueueProperties(metadata, messageCount);
Try to mock the response of queueClient this will verify sendMessageAsync response
[Fact]
public async Task SendMessage_ShouldReturnSuccess()
{
var receipt = QueuesModelFactory.SendReceipt("1", DateTimeOffset.Now, DateTimeOffset.Now.AddDays(2), "pop", DateTimeOffset.Now.AddDays(1));
var res = Response.FromValue<SendReceipt>(receipt, null);
var queueClientmock = new Mock<QueueClient>();
queueClientmock.Setup(q => q.SendMessageAsync(It.IsAny<string>())).Returns(Task.FromResult(res));
var sqmock = new Mock<IStorageQueueProvider>();
sqmock.Setup(s => s.GetStorageQueueClient()).Returns(Task.FromResult(queueClientmock.Object)).Verifiable();
var storageQueueRepository = new StorageQueueRepository(sqmock.Object, DummyLogger);
var result = await storageQueueRepository.SendMessage("test message");
result.StatusCode.Should().Be(HttpStatusCode.OK);
}
return QueuesModelFactory.QueueMessage(
messageId: "id2",
popReceipt: "pr2",
body: BinaryData.FromString(encode ? Convert.ToBase64String(Encoding.UTF8.GetBytes(Message)) : Message),
dequeueCount: dequeueCount,
insertedOn: DateTimeOffset.UtcNow);
Related
I'm trying to write unit test using Moq framework for one of my MediatR handler. I have the following code. Briefly, what this handler does is that it queries for the given key and returns a response that contains key and its value using EF Core. Also there is a basic cache mechanism. If the key found in cache it is pulled from cache and returned.
Handler
public GetConfigByKeyRequestHandler(MyContext context, ICacheProvider cacheProvider, IOptions<CacheConfigs> cacheConfigs)
{
this.context = context;
this.cacheProvider = cacheProvider;
this.cacheConfigs = cacheConfigs?.Value;
}
public async Task<ConfigResponse> Handle(GetConfigByKeyRequest request, CancellationToken cancellationToken)
{
ConfigResponse config;
if (!await cacheProvider.ExistsAsync(request.Key))
{
config = await context.Configs
.Where(x.ConfigKey.Equals(request.Key))
.Select(x =>
new ConfigResponse {
ConfigKey = x.ConfigKey,
ConfigValue = x.ConfigValue
})
.FirstOrDefaultAsync(cancellationToken);
if (config is not null)
{
await cacheProvider.PutAsync(new CacheItem<ConfigResponse>(request.Key, config), new CacheOptions
{
ExpireAfter = TimeSpan.FromMinutes(cacheConfigs.ExpireAfterInMinutes).TotalMilliseconds,
ExpireInactive = TimeSpan.FromMinutes(cacheConfigs.ExpireInActiveInMinutes).TotalMilliseconds
});
}
return config;
}
config = await cacheProvider.PullAsync<ConfigResponse>(request.Key);
return config;
}
I have thought that I should cover 2 different scenarios:
When the key found in the cache
When the key is not found in cache and it's returned from DbContext.
Unit tests
private Mock<ICacheProvider> cacheProviderMock;
private IOptions<CacheConfigs> cacheConfigs;
public GetConfigByKeyRequestHandlerTests()
{
cacheProviderMock = new Mock<ICacheProvider>();
cacheConfigs = Options.Create(
new CacheConfigs
{
ExpireAfterInMinutes = 3,
ExpireInActiveInMinutes = 3
});
}
[Fact]
public async Task GetConfigByKeyHandler_WhenKeyIsCached_ShouldReturnConfigByKey()
{
// arrange
var options = new DbContextOptionsBuilder<MyContext>().UseInMemoryDatabase("MyInMemoryDatabase").Options;
var configItems = Enumerable.Range(0, 5).Select(x => new Config
{
ConfigKey = $"key{x}",
ConfigValue = $"value{x}"
});
using (var context = new MyContext(options))
{
await context.Configs.AddRangeAsync(configItems);
await context.SaveChangesAsync();
}
using (var context = new MyContext(options))
{
cacheProviderMock.Setup(x => x.ExistsAsync(It.IsAny<string>())).Returns(Task.FromResult(true));
cacheProviderMock.Setup(x => x.PullAsync<ConfigResponse>("key2"))
.Returns(Task.FromResult(new ConfigResponse
{
ConfigKey = "key2",
ConfigValue = "value2"
}));
var getConfigByKeyHandler = new GetConfigByKeyRequestHandler(context, cacheProviderMock.Object, cacheConfigs);
var getConfigByKeyRequest = new GetConfigByKeyRequest("key2");
// act
var result = await getConfigByKeyHandler.Handle(getConfigByKeyRequest, CancellationToken.None);
// assert
Assert.NotNull(result);
Assert.Equal("key2", result.ConfigKey);
}
}
...
...
With the same logic, I have one more test for the other scenario that when key is not cached
...
...
[Fact]
public async Task GetConfigByKeyHandler_WhenKeyIsNotCached_ShouldReturnConfigByKey()
{
// arrange
var options = new DbContextOptionsBuilder<MyContext>().UseInMemoryDatabase("MyInMemoryDatabase").Options;
var configItems = Enumerable.Range(0, 5).Select(x => new Config
{
ConfigKey = $"key{x}",
ConfigValue = $"value{x}"
});
using (var context = new MyContext(options))
{
await context.Configs.AddRangeAsync(configItems);
await context.SaveChangesAsync();
}
using (var context = new MyContext(options))
{
cacheProviderMock.Setup(x => x.ExistsAsync(It.IsAny<string>())).Returns(Task.FromResult(false));
var getConfigByKeyHandler = new GetConfigByKeyRequestHandler(context, cacheProviderMock.Object, cacheConfigs);
var getConfigByKeyRequest = new GetConfigByKeyRequest("key2");
// act
var result = await getConfigByKeyHandler.Handle(getConfigByKeyRequest, CancellationToken.None);
// assert
Assert.NotNull(result);
Assert.Equal("key2", result.ConfigKey);
}
}
I have written 2 unit tests that covers the scenarios that I have mentioned above, but I'm not sure that they are reasonable ones and I'm not sure it should be tested like this way. Do you have any suggestions what/how should I write tests for the handler I shared above?
Having some issues setting up my mock data for a unit test in my API. The data is retrieved from a service.
Here is my endpoint and how the data is retrieved:
RetrieveExceptionReportSessionDatesResponse response
= await ResolveServiceClient().RetrieveExceptionReportSessionDatesAsync(new RetrieveExceptionReportSessionDatesRequest());
List<ExceptionReportSessionDataModel> result
= GetSessionData(response.RetrieveExceptionReportSessionDatesResult);
if (result != null && result.Count > 0)
{
logText = LogFormatter.Format(
WebUtilities.GetUser((ClaimsIdentity)HttpContext.User.Identity),
startTime, DateTime.Now, Privilege.EditSession,
"Get Exception Report Session Data", "Exception Report Session Data retrieved successfully.");
logger.LogInfo(logText);
}
else
{
logText = LogFormatter.Format(
WebUtilities.GetUser((ClaimsIdentity)HttpContext.User.Identity),
startTime, DateTime.Now, Privilege.ViewOrderExceptionReport,
"Get exception report session data", "Exception report session data is null or empty.");
logger.LogWarn(logText);
}
return Ok(result);
Here is what I have so far in setting up my unit test:
//Arrange
List<ExceptionReportSessionDataModel> sessionData = new List<ExceptionReportSessionDataModel>()
{
new ExceptionReportSessionDataModel() {SessionName = "Session1", ReportFiles = null },
new ExceptionReportSessionDataModel() {SessionName = "Session2", ReportFiles = null },
new ExceptionReportSessionDataModel() {SessionName = "Session3", ReportFiles = null }
};
//NEED HELP HERE
var ERSessionDataMock = new Mock<>();
ERSessionDataMock.Setup(x => x.).Returns();
var loggerMock = new Mock<ILogger>();
loggerMock.Setup(x => x.LogInfo(null));
GetSessionData Method:
public List<ExceptionReportSessionDataModel> GetSessionData(string sessionData)
{
List<ExceptionReportSessionDataModel> reports = new List<ExceptionReportSessionDataModel>();
if (!string.IsNullOrWhiteSpace(sessionData))
{
string[] splitString = sessionData.Split("\n", StringSplitOptions.RemoveEmptyEntries);
foreach (string s in splitString)
{
string[] temp = s.Split(",", StringSplitOptions.RemoveEmptyEntries);
List<string> files = new List<string>();
for (int index = 1; index < temp.Length; index++)
{
files.Add(temp[index]);
}
reports.Add(new ExceptionReportSessionDataModel()
{
ReportFiles = files,
SessionName = temp[0]
});
}
}
return reports;
}
I need help setting up the ERSessionDataMock
It is impossible to know without seeing how the GetSessionData method is implemented. But here are some quick suggestions:
Inject your ServiceClient as a dependency instead of calling a method to get it. It needs to either:
be an interface (like IServiceClient) - then in your Startup.cs class:
services.AddScoped<IServiceClient>(sp => ResolveServiceClient().Result);
or the RetrieveExceptionReportSessionDatesAsync method needs to be virtual or abstract so you can override it with a mock:
public virtual RetrieveExceptionReportSessionDatesAsync(RetrieveExceptionReportSessionDatesRequest request);
Then in your test, create a response variable that will exercise the GetSessionData in a certain way to get either an empty List<ExceptionReportSessionDataModel> or not, depending on the test (again, no way to know exactly how without seeing your logic):
var response = new RetrieveExceptionReportSessionDatesResponse();
And override the RetrieveExceptionReportSessionDatesAsync method on the mock:
var serviceClient = new Mock<IServiceClient>();
serviceClient
.Setup(x => x.RetrieveExceptionReportSessionDatesAsync(It.IsAny<RetrieveExceptionReportSessionDatesRequest>()))
.Returns(response);
Now, if you've setup response in a way that triggers GetSessionData to return a List<ExceptionReportSessionDataModel> that has Count > 0, you can verify that log message, otherwise verify the opposite log message.
But IMO, the way you've written this code makes unit testing a chore. You don't seem to be taking advantage of dependency injection.
UPDATE:
It seems you can either assign response as:
var response = new RetrieveExceptionReportSessionDatesResponse
{
RetrieveExceptionReportSessionDatesResult = Guid.NewGuid().ToString()
}
and verify that logger.LogInfo is called with Privilege.EditSession, or assign response as:
var response = new RetrieveExceptionReportSessionDatesResponse
{
RetrieveExceptionReportSessionDatesResult = string.Empty
}
and verify that logger.LogWarn is called with Privilege.ViewOrderExceptionReport.
Just one more observation - is there an off-by-one error in the for loop? (It is staring at index 1)
I'm trying to send multiple same requests at (almost) once to my WebAPI to do some performance testing.
For this, I am calling PerformRequest multiple times and wait for them using await Task.WhenAll.
I want to calculate the time that each request takes to complete plus the start time of each one of them. In my code,however, I don't know what happens if the result of R3 (request number 3) comes before R1? Would the duration be wrong?
From what I see in the results, I think the results are mixing with each other. For example, the R4's result sets as R1's result. So any help would be appreciated.
GlobalStopWatcher is a static class that I'm using to find the start time of each request.
Basically I want to make sure that elapsedMilliseconds and Duration of each request is associated with the request itself.
So that if the result of request 10th comes before the result of 1st request, then duration would be duration = elapsedTime(10th)-(startTime(1st)). Isn't that the case?
I wanted to add a lock but it seems impossible to add it where there's await keyword.
public async Task<RequestResult> PerformRequest(RequestPayload requestPayload)
{
var url = "myUrl.com";
var client = new RestClient(url) { Timeout = -1 };
var request = new RestRequest { Method = Method.POST };
request.AddHeaders(requestPayload.Headers);
foreach (var cookie in requestPayload.Cookies)
{
request.AddCookie(cookie.Key, cookie.Value);
}
request.AddJsonBody(requestPayload.BodyRequest);
var st = new Stopwatch();
st.Start();
var elapsedMilliseconds = GlobalStopWatcher.Stopwatch.ElapsedMilliseconds;
var result = await client.ExecuteAsync(request).ConfigureAwait(false);
st.Stop();
var duration = st.ElapsedMilliseconds;
return new RequestResult()
{
Millisecond = elapsedMilliseconds,
Content = result.Content,
Duration = duration
};
}
public async Task RunAllTasks(int numberOfRequests)
{
GlobalStopWatcher.Stopwatch.Start();
var arrTasks = new Task<RequestResult>[numberOfRequests];
for (var i = 0; i < numberOfRequests; i++)
{
arrTasks[i] = _requestService.PerformRequest(requestPayload, false);
}
var results = await Task.WhenAll(arrTasks).ConfigureAwait(false);
RequestsFinished?.Invoke(this, results.ToList());
}
Where I think you're going wrong with this is trying to use a static GlobalStopWatcher and then pushing this code into your function that you're testing.
You should keep everything separate and use a new instance of Stopwatch for each RunAllTasks call.
Let's make it so.
Start with these:
public async Task<RequestResult<R>> ExecuteAsync<R>(Stopwatch global, Func<Task<R>> process)
{
var s = global.ElapsedMilliseconds;
var c = await process();
var d = global.ElapsedMilliseconds - s;
return new RequestResult<R>()
{
Content = c,
Millisecond = s,
Duration = d
};
}
public class RequestResult<R>
{
public R Content;
public long Millisecond;
public long Duration;
}
Now you're in a position to test anything that fits the signature of Func<Task<R>>.
Let's try this:
public async Task<int> DummyAsync(int x)
{
await Task.Delay(TimeSpan.FromSeconds(x % 3));
return x;
}
We can set up a test like this:
public async Task<RequestResult<int>[]> RunAllTasks(int numberOfRequests)
{
var sw = Stopwatch.StartNew();
var tasks =
from i in Enumerable.Range(0, numberOfRequests)
select ExecuteAsync<int>(sw, () => DummyAsync(i));
return await Task.WhenAll(tasks).ConfigureAwait(false);
}
Note that the line var sw = Stopwatch.StartNew(); captures a new Stopwatch for each RunAllTasks call. Nothing is actually "global" anymore.
If I execute that with RunAllTasks(7) then I get this result:
It runs and it counts correctly.
Now you can just refactor your PerformRequest method to just do what it needs to:
public async Task<string> PerformRequest(RequestPayload requestPayload)
{
var url = "myUrl.com";
var client = new RestClient(url) { Timeout = -1 };
var request = new RestRequest { Method = Method.POST };
request.AddHeaders(requestPayload.Headers);
foreach (var cookie in requestPayload.Cookies)
{
request.AddCookie(cookie.Key, cookie.Value);
}
request.AddJsonBody(requestPayload.BodyRequest);
var response = await client.ExecuteAsync(request);
return response.Content;
}
Running the tests is easy:
public async Task<RequestResult<string>[]> RunAllTasks(int numberOfRequests)
{
var sw = Stopwatch.StartNew();
var tasks =
from i in Enumerable.Range(0, numberOfRequests)
select ExecuteAsync<string>(sw, () => _requestService.PerformRequest(requestPayload));
return await Task.WhenAll(tasks).ConfigureAwait(false);
}
If there's any doubt about the thread-safety of Stopwatch then you could do this:
public async Task<RequestResult<R>> ExecuteAsync<R>(Func<long> getMilliseconds, Func<Task<R>> process)
{
var s = getMilliseconds();
var c = await process();
var d = getMilliseconds() - s;
return new RequestResult<R>()
{
Content = c,
Millisecond = s,
Duration = d
};
}
public async Task<RequestResult<int>[]> RunAllTasks(int numberOfRequests)
{
var sw = Stopwatch.StartNew();
var tasks =
from i in Enumerable.Range(0, numberOfRequests)
select ExecuteAsync<int>(() => { lock (sw) { return sw.ElapsedMilliseconds; } }, () => DummyAsync(i));
return await Task.WhenAll(tasks).ConfigureAwait(false);
}
I am trying to test for the results of certain exceptions when Nest has a value in IGetResponse.OriginalException property.
I first set up the response:
var response = A.Fake<Nest.IGetResponse<Dictionary<string, object>>>();
A.CallTo(() => response.OriginalException).Returns(new Exception("Status code 404"));
Then the fake elastic client:
var client = A.Fake<Nest.IElasticClient>();
A.CallTo(client)
.WithReturnType<Nest.IGetResponse<Dictionary<string, object>>>()
.Returns(response);
The client gets injected into the class I am testing.
However, when stepping through the code, when the client is called it returns a faked response, but the OriginalException getter has no value. Its not null, but none of the properties has any value. I was expecting the OriginalException.Message to equal Status code 404.
I also tried setting the response object to:
var response = A.Fake<Nest.IGetResponse<Dictionary<string, object>>>();
A.CallTo(() => response.OriginalException.Message).Returns("Status code 404");
... with equally poor results.
How can I set the IGetResponse so I can evaluate OriginalException.Message in the class being tested?
More code was requested. I can show the entire test, and I will show the method being tested. Here is my entire test:
[TestMethod]
[ExpectedException(typeof(NotFoundException))]
public void Get_ClientReturns404_ThrowsNotFoundException()
{
// setup
var request = new DataGetRequest
{
CollectionName = string.Empty,
DocumentType = string.Empty,
DataAccessType = string.Empty
};
var response = A.Fake<Nest.IGetResponse<Dictionary<string, object>>>();
A.CallTo(() => response.OriginalException.Message).Returns("Status code 404");
var client = A.Fake<Nest.IElasticClient>();
A.CallTo(client)
.WithReturnType<Nest.IGetResponse<Dictionary<string, object>>>()
.Returns(response);
var elasticSearch = new ElasticSearch(null, client);
// test
var result = elasticSearch.Get(request);
// assert
Assert.Fail("Should have hit an exception.");
}
}
And here is the method being tested:
public async Task<Dictionary<string, object>> Get(DataGetRequest getRequest)
{
GetRequest request = new GetRequest(getRequest.CollectionName, getRequest.DocumentType, getRequest.Id);
var response = await Client.GetAsync<Dictionary<string, object>>(request);
if (response.OriginalException != null)
{
var message = response.OriginalException.Message;
if (message.Contains("Status code 404"))
throw new NotFoundException(String.Format("Not Found for id {0}", getRequest.Id));
else
throw new Exception(message);
}
return response.Source;
}
The error handling in the IF block is not very robust. Once the unit test works then the code will likely receive more love.
The return type of the mocked client is wrong as the IElasticClient.GetAsync<> returns a Task<IGetResponse<T>>.
Task<IGetResponse<T>> GetAsync<T>(IGetRequest request, CancellationToken cancellationToken = default(CancellationToken)) where T : class;
Source
So the setup needs to return a Task derived result to allow the async code
var response = await Client.GetAsync<Dictionary<string, object>>(request);
to flow as expected.
For example
[TestMethod]
[ExpectedException(typeof(NotFoundException))]
public async Task Get_ClientReturns404_ThrowsNotFoundException() {
//Arrange
var originalException = new Exception("Status code 404");
var response = A.Fake<Nest.IGetResponse<Dictionary<string, object>>>();
A.CallTo(() => response.OriginalException).Returns(originalException);
var client = A.Fake<Nest.IElasticClient>();
A.CallTo(() =>
client.GetAsync<Dictionary<string, object>>(A<IGetRequest>._, A<CancellationToken>._)
).Returns(Task.FromResult(response));
var request = new DataGetRequest {
CollectionName = string.Empty,
DocumentType = string.Empty,
DataAccessType = string.Empty
};
var elasticSearch = new ElasticSearch(null, client);
// Act
var result = await elasticSearch.Get(request);
// Assert
Assert.Fail("Should have hit an exception.");
}
I have a functioning test but I need guidance how to properly test the logic. My understanding of testing is that it should be done without tight coupling to the resources (which is where mocking comes in) but if everything is mocked (especially the return result) how can the logic be tested, properly, without instantiating a bunch of classes?
ValidateEmployeeConfigurationAsync (below) will return RulesValidationResult which is what I want to assert. So I can answer my own question of how, which would require newing up repositories and services - that's one way. Is there a best practice way to accomplish that? That feels wrong.
Functioning Test
[TestMethod]
public async Task PassValidateEmployeeConfigurationTest()
{
//ARRANGE
const long employeeId = 200L;
const int configurationTypeId = (int) Constants.Configuration.ConfigurationTypes.User;
const bool enabled = true;
_ruleService = new Mock<IRuleService>();
_configurationService = new Mock<IConfigurationService>();
_ruleFacade = new Mock<IRuleFacade>();
_configurationService.Setup(x => x.GetByConfigurationNameAsync(It.IsAny<string>()))
.ReturnsAsync(GetConfigurations(enabled));
_ruleService.Setup(x => x.GetConfigRulesByEmployeeIdAsync(It.IsAny<long>()))
.ReturnsAsync(GetRules(enabled));
_ruleFacade.Setup(x =>
x.ValidateEmployeeConfigurationAsync(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<int>()))
.ReturnsAsync(GetPassedValidationResult());
//ACT
var result = await
_ruleFacade.Object.ValidateEmployeeConfigurationAsync(employeeId, "TestConfiguration", configurationTypeId);
//ASSERT
Assert.IsNotNull(result);
Assert.AreEqual(true, result.PassedValidation);
}
Method of interest
public async Task<RulesValidationResult> ValidateEmployeeConfigurationAsync(long employeeId, string configurationName, int configurationTypeId = 6)
{
var key = GetDefaultKey(configurationName);
var rules = new List<Rule>();
var validationResult = new RulesValidationResult();
validationResult.Messages.Add("Configuartion not found", configurationName);
var configurations = await _configurationService.GetByConfigurationNameAsync(configurationName);
if (!configurations.Any())
return validationResult;
var configuration = configurations.FirstOrDefault(c => c.ConfigurationTypeId == configurationTypeId);
rules = await _ruleService.GetConfigRulesByEmployeeIdAsync(employeeId);
if (rules.Any() && configuration.ConfigurationSettings.Any())
{
var testTargets = new List<ConfigurationSetting>();
testTargets.AddRange(from setting in configuration.ConfigurationSettings
where setting.IsActive && setting.Key == key
select new ConfigurationSetting
{
ConfigurationId = setting.ConfigurationId,
Key = setting.Key,
Value = setting.Value
});
if (PassesRules(testTargets, rules))
{
var msg = $"{configurationName} passed rule validation";
validationResult.PassedValidation = true;
validationResult.Messages.Clear();
validationResult.Messages.Add("Passed", msg);
}
else
{
var msg = $"{configurationName} failed rule validation";
validationResult.Messages.Clear();
validationResult.Messages.Add("Failed", msg);
}
}
return validationResult;
}
This is how I structured the test, view the previous version of the TestMethod to see the differences. In the original question, the test would always pass given the incorrect way the test was structured.
In this version, the input is mocked, the dependencies are mocked, and the IOC container is setup so the targeted code is tested and the assertion can be applied to the generated output.
[TestMethod]
public async Task PassValidateEmployeeConfigurationTest()
{
//ARRANGE
const long employeeId = 200L;
const int configurationTypeId = (int) Constants.Configuration.ConfigurationTypes.User;
//need the IOC container
var unityContainer = new UnityContainer();
//Mock ALL the dependencies (services and repositories)
var ruleFacade = new Mock<IRuleFacade>();
var employeeConfigMapRepo = new Mock<IEmployeeConfigurationMapRepo>();
var ruleRepo = new Mock<IRuleRepo>();
var ruleService = new Mock<IRuleService>();
var configurationService = new Mock<IConfigurationService>();
var employeeConfigurationService = new Mock<IEmployeeConfigurationService>();
var organizationConfigurationService = new Mock<IOrganizationConfigurationService>();
var facilityConfigurationService = new Mock<IFacilityConfigurationService>();
var configurationSettingService = new Mock<IConfigurationSettingService>();
// configure the dependencies so that the proper inputs are available
configurationService.Setup(x => x.GetByConfigurationNameAsync(It.IsAny<string>()))
.ReturnsAsync(GetConfigurations(true));
employeeConfigMapRepo.Setup(x => x.GetAllByEmployeeIdAsync(It.IsAny<int>()))
.ReturnsAsync(GetEmployeeConfigMaps(true));
employeeConfigurationService.Setup(x => x.GetAllByEmployeeIdAsync(It.IsAny<long>()))
.ReturnsAsync(GetEmployeeConfigMaps(true));
ruleRepo.Setup(x => x.GetByConfigurationIdAsync(It.IsAny<int>()))
.ReturnsAsync(GetRules(true));
ruleService.Setup(x => x.GetConfigRulesByEmployeeIdAsync(It.IsAny<long>(), It.IsAny<string>()))
.ReturnsAsync(GetRules(true));
// help the IOC do its thing, map interfaces to Mocked objects
unityContainer.RegisterInstance<IRuleService>(ruleService.Object);
unityContainer.RegisterInstance<IRuleRepo>(ruleRepo.Object);
unityContainer.RegisterInstance<IConfigurationService>(configurationService.Object);
unityContainer.RegisterInstance<IEmployeeConfigurationService>(employeeConfigurationService.Object);
unityContainer.RegisterInstance<IOrganizationConfigurationService>(organizationConfigurationService.Object);
unityContainer.RegisterInstance<IFacilityConfigurationService>(facilityConfigurationService.Object);
unityContainer.RegisterInstance<IConfigurationSettingService>(configurationSettingService.Object);
unityContainer.RegisterInstance<IRuleFacade>(ruleFacade.Object);
// thanks to all the mocking, the facade method ValidateEmployeeConfigurationAsync can now be tested properly
var ruleHelper = unityContainer.Resolve<RuleFacade>();
//ACT
var result = await
ruleHelper.ValidateEmployeeConfigurationAsync(employeeId, _testConfigName, configurationTypeId);
//ASSERT
Assert.IsNotNull(result);
Assert.AreEqual(true, result.PassedValidation);
}