How to write unit test for a MediatR handler C# - c#

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?

Related

How to prevent Data to be added if they are overlapping by their datetime?

I have a controller which is following;
public class ScheduleController: ControllerBase
{
private readonly IScheduleService _scheduleService;
public ScheduleController(IScheduleService scheduleService)
{
_scheduleService = scheduleService;
}
[HttpGet]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleResponseDto>> GetDraftSchedule(int plantCode)
{
var schedule = await _scheduleService.GetLatestScheduleForPlant(plantCode);
return Ok(schedule);
}
[HttpPost]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleResponseDto>> PostSchedule(int plantCode, List<ScheduleInputItemDto> scheduleInputItems)
{
var schedule = await _scheduleService.AddNewSchedule(plantCode, scheduleInputItems);
return Ok(schedule);
}
[HttpPost("items")]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleItemResponseDto>> PostScheduleItem(int scheduleId, ScheduleInputItemDto scheduleInputItem)
{
var scheduleItem = await _scheduleService.AddItemToSchedule(scheduleId, scheduleInputItem);
return Ok(scheduleItem);
}
[HttpPut("items/{itemId}")]
[ProducesResponseType(typeof(ScheduleResponseDto), StatusCodes.Status200OK)]
[Produces(MediaTypeNames.Application.Json)]
public async Task<ActionResult<ScheduleItemResponseDto>> PutScheduleItem(int scheduleId, int itemId, ScheduleInputItemDto scheduleInputItem)
{
var scheduleItem = await _scheduleService.ChangeScheduleItem(scheduleId, itemId, scheduleInputItem);
return Ok(scheduleItem);
}
}
User adding new schedule and items to schedule. I need to check if items are overlapping by their start date and end date. I have been trying to find a solution to prevent items to be added if they are overlapping.
In AddItemToSchedule method I need to check if they are overlapping.
public async Task<ScheduleResponseDto> AddItemToSchedule(int scheduleId, ScheduleInputItemDto scheduleItem)
{
var scheduleWithId = await _scheduleRepository.GetScheduleById(scheduleId);
scheduleWithId.AddItem(
start: scheduleItem.Start,
end: scheduleItem.End,
cementType: scheduleItem.CementType,
now: DateTime.UtcNow);
await _scheduleRepository.Update(scheduleWithId);
return scheduleWithId.MapToScheduleDto();
}
And Also same thing for ChangeScheduleItem method.
public async Task<ScheduleResponseDto> ChangeScheduleItem(int scheduleId, int itemId, ScheduleInputItemDto scheduleInputItem)
{
var now = DateTime.UtcNow;
var schedule = await _scheduleRepository.GetScheduleById(scheduleId);
schedule.UpdateItem(itemId, scheduleInputItem.Start, scheduleInputItem.End, scheduleInputItem.CementType, now);
await _scheduleRepository.Update(schedule);
return schedule.MapToScheduleDto();
}
The first test is for adding new item to schedule and testing with Task.WhenAll() what if two different user call the endpoint with the same two items at the same time.
[Fact]
public async Task GivenScheduleWithNoItems_WhenTwoSimultaneousIdenticalAddItemRequests_ThenOneItemIsAddedAndTheOtherRejected()
{
//Setup
var fixture = new Fixture();
var plantCode = fixture.Create<int>().ToString();
var itemToAdd = new ScheduleInputItemDto
{
Start = DateTime.UtcNow,
End = DateTime.UtcNow.AddHours(1),
CementType = "CEM-I"
};
var addScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var latestScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var addItemForScheduleRequest = (string scheduleId) => NewRequest
.AddRoute("schedule/items")
.AddQueryParams("scheduleId", scheduleId);
// Exercise
await addScheduleRequest.Post(new ScheduleInputItemDto[]{});
// First let's get the schedule before adding any items. This schedule is currently empty..
var scheduleBeforeAddition = await latestScheduleRequest.Get<ScheduleResponseDto>();
var scheduleId = scheduleBeforeAddition.ScheduleId.ToString();
var addItemRequest = addItemForScheduleRequest(scheduleId);
// Simultaneously start two tasks that will make the same exact item addition request.
// This is a race condition, the first request should pass and the second should fail.
var itemAddResponses = await Task.WhenAll(addItemRequest.Post(itemToAdd, false), addItemRequest.Post(itemToAdd, false));
//Finally let's get the schedule after the item addition requests. It should have only one item in it.
var scheduleAfterAddition = await latestScheduleRequest.Get<ScheduleResponseDto>();
// Verify
scheduleBeforeAddition.ScheduleItems.Count.Should().Be(0);
//TEST FAILS HERE - only one of the items should be added and the second should cause a conflict
scheduleAfterAddition.ScheduleItems.Count.Should().Be(1);
var failures = itemAddResponses.ToList().Where(it => it.IsSuccessStatusCode == false);
var successes = itemAddResponses.ToList().Where(it => it.IsSuccessStatusCode == true);
failures.Count().Should().Be(1);
successes.Count().Should().Be(1);
}
The second test is for also checking the same thing during changing the item in the schedule.
[Fact]
public async Task GivenScheduleWithItem_WhenTwoClientsAreChangingTheSingleItem_ThenItemModificationShouldHappenInSequence()
{
//Setup
var fixture = new Fixture();
var plantCode = fixture.Create<int>().ToString();
var itemDto = new ScheduleInputItemDto
{
Start = DateTime.UtcNow,
End = DateTime.UtcNow.AddHours(1),
CementType = "CEM-I"
};
var addScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var latestScheduleRequest = NewRequest
.AddRoute("schedule")
.AddQueryParams("plantCode", plantCode);
var changeItemForScheduleRequest = (string scheduleId, string itemId) => NewRequest
.AddRoute($"schedule/items/{itemId}")
.AddQueryParams("scheduleId", scheduleId);
//Exercise
//Make new schedule
await addScheduleRequest.Post(new List<ScheduleInputItemDto> { itemDto });
var scheduleBeforeChanges = await latestScheduleRequest.Get<ScheduleResponseDto>();
var scheduleId = scheduleBeforeChanges.ScheduleId.ToString();
var existingItemId = scheduleBeforeChanges.ScheduleItems.First().ScheduleItemId;
var itemChangeRequest = changeItemForScheduleRequest(scheduleId, existingItemId.ToString());
// Send two simultaneous item change requests
var itemChangeResponses = await Task.WhenAll(itemChangeRequest.Put(itemDto, false), itemChangeRequest.Put(itemDto, false));
//Get the schedule after item change requests, should have only one item and the item should have an update counter of only 1
var scheduleAfterChanges = await latestScheduleRequest.Get<ScheduleResponseDto>();
//verify
scheduleBeforeChanges.ScheduleItems.Count.Should().Be(1);
scheduleBeforeChanges.ScheduleItems.First().NumberOfTimesUpdated.Should().Be(0);
scheduleAfterChanges.ScheduleItems.Count.Should().Be(1);
scheduleAfterChanges.ScheduleItems.First().NumberOfTimesUpdated.Should().Be(1);
var failures = itemChangeResponses.ToList().Where(it => it.IsSuccessStatusCode == false);
var successes = itemChangeResponses.ToList().Where(it => it.IsSuccessStatusCode == true);
//TEST FAILS HERE, as one of the calls should fail
failures.Count().Should().Be(1);
successes.Count().Should().Be(1);
}
In conclusion I need to prevent methods to add overlapping items in the schedule.
Example Json Input is the following;
{
"scheduleId": 12132891,
"plantId": 1213,
"updatedOn": "2021-12-01T12:44:17Z",
"scheduleItems": [
{
"scheduleItemId": 1,
"cementType": "CEM-I",
"start": "2021-11-23T00:00:00Z",
"end": "2021-11-23T02:15:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
},
{
"scheduleItemId": 2,
"cementType": "CEM-II",
"start": "2021-11-23T03:00:00Z",
"end": "2021-11-23T10:30:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
},
{
"scheduleItemId": 3,
"cementType": "CEM-III",
"start": "2021-11-23T10:30:00Z",
"end": "2021-11-23T11:00:00Z",
"updatedOn": "2021-12-01T11:43:17Z"
}
]
}

How to mock methods in the below code in the .NET 6 using Moq?

Below is InvokeAsync method which need to tested.
public async Task<bool> InvokeAsync(Batch batch)
{
var refundRequests = await this.RefundRepository.GetsAsync(batch.RefundRequests.Select(x => x.Id));
refundRequests.RemoveAll(x => x.Status != RefundRequestStatus.Approved);
var distributions = await DistributionRepository.GetsAsync(refundRequests.Select(x => x.Distribution.Id));
var bundles = await this.BundleRepository.GetsAsync(distributions.Select(x => x.BundleId));
foreach (var getRefundRequest in refundRequests)
{
var distribution = distributions.FirstOrDefault(x => x.Id == getRefundRequest.Distribution.Id);
if (distribution?.BundleId != null)
{
var bundle = bundles.First(x => x.Id == distribution?.BundleId);
Bundle result = await Reverse.InvokeAsync(getRefundRequest, distribution, bundle); //MOCK
}
getRefundRequest.Status = RefundRequestStatus.Closed;
getRefundRequest.LastUpdatedBy = "Test User";
bool isUpdated = await UpdateRefund.InvokeAsync(getRefundRequest); //MOCK
}
batch.Status = BatchStatus.Posted;
batch.LastUpdatedBy = "Test User";
var isSuccess = await UpdateBatch.InvokeAsync(batch); //MOCK
return isSuccess;
}
Unit test method
[Fact]
public async Task Post_Batch()
{
foreach (var refundBatch in Factory.Batch.CreateRefundBatchesData())
{
var refundRequests = await this.RefundRequestRepository.GetsAsync(batch.RefundRequests.Select(x => x.Id));
var distributions = await this.DistributionRepository.GetsAsync(refundRequests.Select(x => x.Distribution.Id));
var bundles = await this.BundleRepository.GetsAsync(distributions.Select(x => x.BundleId));
for (int i = 0; i < refundRequests.Count; i++)
{
var refundRequest = refundRequests[i];
var bundle = bundles[i];
var distribution = distributions[i];
MockSetupReverse(refundRequest, distribution, bundle);
MockSetupUpdateRefund(refundRequest);
}
MockSetupUpdateBatch(batch);
//Act
var postRefund = await UseCase.InvokeAsync(batch);
//Assert
postRefund.ShouldNotBeNull();
postRefund.IsPosted.ShouldBeTrue();
}
}
MockSetup methods
private void MockSetupReverse(RefundRequest refundRequest, Distribution distribution, Bundle bundle)
{
this.MockReverse
.Setup(x => x.InvokeAsync(refundRequest, distribution, bundle))
.Returns(async () =>
{
bundle.Status = BundleStatus.Closed;
return await Task.Run(() => bundle);
});
}
private void MockSetupUpdateRefund(RefundRequest refundRequest)
{
this.MockUpdateRefund
.Setup(x => x.InvokeAsync(refundRequest))
.Returns(async () =>
{
refundRequest.Status = RefundRequestStatus.Closed;
refundRequest.LastUpdatedBy = Factory.RefundRequest.TestUserName;
return await Task.Run(() => true);
});
}
private void MockSetupUpdateBatch(Batch batch)
{
this.MockUpdateBatch
.Setup(x => x.InvokeAsync(batch))
.Returns(async () =>
{
refundBatch.Status = BatchStatus.Posted;
refundBatch.LastUpdatedBy = Factory.RefundRequest.TestUserName;
return await Task.Run(() => true);
});
}
The mocking of the UpdateBatch is working and returns true when the method is invoked. But, the mocking of the Reverse and UpdateRefund returns false when the respective method is invoked. Any thoughts?
Please let me know if more info is required to support the question.
When you Setup your mocks with a specific parameter, the Returns only applies when this specific parameter is used.
The reason UpdateBatch works is because you're using the same reference to the same Batch in both the mock and the class under test:
MockSetupUpdateBatch(batch);
//Act
var postRefund = await UseCase.InvokeAsync(batch); // <---- Same "batch"
When your test code is calling RefundRequestRepository.GetsAsync you probably get different results than when the tested class calls GetsAsync, so the setup is not relevant for the calls of the tested class and probably returns the default bool value (false).
For more information on how to mock correctly refer to this GitHub page

LSP for VS Code - client/server, need some tips

I struggle with understanding how does LSP client-side works. I mean I think I understand the theory of communication (JSON-RPC/LSP Protocol basics) but I struggle with existing libraries that are used for this for VS Code and I think trying to rewrite it is kinda pointless, especially client-side where I do not feel proficient at all
All examples I see provide a path to the server, so the LSP client can start it
it makes sense, but I'd rather avoid it during development, I'd want to have the server aopen in debugging mode and just start VS Code
I tried to start with basic of basic server implementation (C#)
public class Server
{
private JsonRpc RPC { get; set; }
public async Task Start()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
var pipeName = "LSP_Pipe";
var writerPipe = new NamedPipeClientStream(pipeName);
var readerPipe = new NamedPipeClientStream(pipeName);
await writerPipe.ConnectAsync(10_000);
await readerPipe.ConnectAsync(10_000);
Log.Information("RPC Listening");
RPC = new JsonRpc(writerPipe, readerPipe, this);
RPC.StartListening();
this.RPC.Disconnected += RPC_Disconnected;
await Task.Delay(-1);
}
private void RPC_Disconnected(object sender, JsonRpcDisconnectedEventArgs e)
{
Log.Information("Disconnected");
}
[JsonRpcMethod(RPCMethods.InitializeName)]
public object Initialize(JToken arg)
{
Log.Information("Initialization");
var serializer = new JsonSerializer()
{
ContractResolver = new ResourceOperationKindContractResolver()
};
var param = arg.ToObject<InitializeParams>();
var clientCapabilities = param?.Capabilities;
var capabilities = new ServerCapabilities
{
TextDocumentSync = new TextDocumentSyncOptions(),
CompletionProvider = new CompletionOptions(),
SignatureHelpProvider = new SignatureHelpOptions(),
ExecuteCommandProvider = new ExecuteCommandOptions(),
DocumentRangeFormattingProvider = false,
};
capabilities.TextDocumentSync.Change = TextDocumentSyncKind.Incremental;
capabilities.TextDocumentSync.OpenClose = true;
capabilities.TextDocumentSync.Save = new SaveOptions { IncludeText = true };
capabilities.CodeActionProvider = clientCapabilities?.Workspace?.ApplyEdit ?? true;
capabilities.DefinitionProvider = true;
capabilities.ReferencesProvider = true;
capabilities.DocumentSymbolProvider = true;
capabilities.WorkspaceSymbolProvider = false;
capabilities.RenameProvider = true;
capabilities.HoverProvider = true;
capabilities.DocumentHighlightProvider = true;
return new InitializeResult { Capabilities = capabilities };
}
}
but I'm unable to setup client with those vscode-languageclient/node libraries even to get Log.Information("Initialization"); part
How can I provide the way they communicate - e.g name of named pipe? or just HTTP posts?
I'm not proficent in js / node development at all, so sorry for every foolish question
I've seen mature/production grade C# Language Server implementations but I'm overwhelmed just by their builders, there's sooo much stuff happening, sop that's why I'd want to write server from scratch, but for client use existing libs
var server = await LanguageServer.From(
options =>
options
.WithInput(Console.OpenStandardInput())
.WithOutput(Console.OpenStandardOutput())
.ConfigureLogging(
x => x
.AddSerilog(Log.Logger)
.AddLanguageProtocolLogging()
.SetMinimumLevel(LogLevel.Debug)
)
.WithHandler<TextDocumentHandler>()
.WithHandler<DidChangeWatchedFilesHandler>()
.WithHandler<FoldingRangeHandler>()
.WithHandler<MyWorkspaceSymbolsHandler>()
.WithHandler<MyDocumentSymbolHandler>()
.WithHandler<SemanticTokensHandler>()
.WithServices(x => x.AddLogging(b => b.SetMinimumLevel(LogLevel.Trace)))
.WithServices(
services => {
services.AddSingleton(
provider => {
var loggerFactory = provider.GetService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<Foo>();
logger.LogInformation("Configuring");
return new Foo(logger);
}
);
services.AddSingleton(
new ConfigurationItem {
Section = "typescript",
}
).AddSingleton(
new ConfigurationItem {
Section = "terminal",
}
);
}
)
.OnInitialize(
async (server, request, token) => {
var manager = server.WorkDoneManager.For(
request, new WorkDoneProgressBegin {
Title = "Server is starting...",
Percentage = 10,
}
);
workDone = manager;
await Task.Delay(2000);
manager.OnNext(
new WorkDoneProgressReport {
Percentage = 20,
Message = "loading in progress"
}
);
}
)
.OnInitialized(
async (server, request, response, token) => {
workDone.OnNext(
new WorkDoneProgressReport {
Percentage = 40,
Message = "loading almost done",
}
);
await Task.Delay(2000);
workDone.OnNext(
new WorkDoneProgressReport {
Message = "loading done",
Percentage = 100,
}
);
workDone.OnCompleted();
}
)
.OnStarted(
async (languageServer, token) => {
using var manager = await languageServer.WorkDoneManager.Create(new WorkDoneProgressBegin { Title = "Doing some work..." });
manager.OnNext(new WorkDoneProgressReport { Message = "doing things..." });
await Task.Delay(10000);
manager.OnNext(new WorkDoneProgressReport { Message = "doing things... 1234" });
await Task.Delay(10000);
manager.OnNext(new WorkDoneProgressReport { Message = "doing things... 56789" });
var logger = languageServer.Services.GetService<ILogger<Foo>>();
var configuration = await languageServer.Configuration.GetConfiguration(
new ConfigurationItem {
Section = "typescript",
}, new ConfigurationItem {
Section = "terminal",
}
);
var baseConfig = new JObject();
foreach (var config in languageServer.Configuration.AsEnumerable())
{
baseConfig.Add(config.Key, config.Value);
}
logger.LogInformation("Base Config: {Config}", baseConfig);
var scopedConfig = new JObject();
foreach (var config in configuration.AsEnumerable())
{
scopedConfig.Add(config.Key, config.Value);
}
logger.LogInformation("Scoped Config: {Config}", scopedConfig);
}
)
);
Thanks in advance

Managing DbContext correctly in a multi thread app

I've read that I shouldn't be using same db context in a multi thread app.
so here is my scenario: I have 2 functions that I run for each years, and each function call sub functions multiple times depending on the row count.
The goal here is : If there are new records add it to the database otherwise update some fields of the record.
What I do is:
1- await StaticSettings.CACHED_DATA.FillAll(); Caches the POCO classes I need into lists. (I'm doing this because I thought that it would be faster to check the records from a cached context rather than using linq on dbcontext multiple times.
2- I run the below code. and Inside it I compare data with cached data
await GetAllData(cookie, datTypes.Select(x => x.DMT_CODE).ToList(), "me", year.Y_YEAR);
await GetAllData(cookie, datTypes.Select(x => x.DMT_CODE).ToList(), "they", year.Y_YEAR);
3- When all tasks are done I call to save changes and dispose objects..
await StaticSettings.CACHED_DATA.ProcessTasks(); //Saves
so What I would like to know is if I can rely on this code..
Trace.WriteLine($#"Caching data");
await StaticSettings.CACHED_DATA.FillAll();
Trace.WriteLine($#"Caching done");
var ts = years.Select(year => Task.Run(async () =>
{
Trace.WriteLine($"Login Start {year.Y_YEAR}");
var cookie = await MahkemeWebUtils.ForceLoginWebClient(user.MU_USERNAME, user.MU_PASSWORD);
Trace.WriteLine($"Login End {year.Y_YEAR}");
await GetAllData(cookie, datTypes.Select(x => x.DMT_CODE).ToList(), "me", year.Y_YEAR);
await GetAllData(cookie, datTypes.Select(x => x.DMT_CODE).ToList(), "they", year.Y_YEAR);
//GC.Collect();
}))
.ToList();
await Task.WhenAll(ts);
await StaticSettings.CACHED_DATA.ProcessTasks(); //Saves
UPDATE
public class CACHED_DATA
{
public AppStructMahkeme _db;
public List<DAVALAR> DavaLarToProcess { get; set; } = new List<DAVALAR>();
public List<KAZALAR> KazaList = new List<KAZALAR>();
public List<DAVA_TURLERI> DavaTurList = new List<DAVA_TURLERI>();
public List<DAVA_SCALE> DavaScaleList = new List<DAVA_SCALE>();
public List<STATUSES> StatusList = new List<STATUSES>();
public List<DAVA_KONULARI> DavakonuList = new List<DAVA_KONULARI>();
public List<AVUKATLAR> AvukatList = new List<AVUKATLAR>();
public List<EVRAK_TYPES> EvrakTypeList = new List<EVRAK_TYPES>();
public List<SENDERS> SendersList = new List<SENDERS>();
public List<ICRA_STATUS> IcraStatusList = new List<ICRA_STATUS>();
public List<DAVALAR> DavaList = new List<DAVALAR>();
public List<ISTIDA_TURLERI> IstidaTurList = new List<ISTIDA_TURLERI>();
public List<ISTIDALAR> IstidaList = new List<ISTIDALAR>();
public async Task FillAll()
{
_db = new AppStructMahkeme();
DavaLarToProcess = new List<DAVALAR>();
//_db.Configuration.LazyLoadingEnabled = false;
//KazaList = null;
KazaList = await _db.KAZALAR.ToListAsync();
DavaTurList = await _db.DAVA_TURLERI.ToListAsync();
DavaScaleList = await _db.DAVA_SCALE.ToListAsync();
StatusList = await _db.STATUSES.ToListAsync();
AvukatList = await _db.AVUKATLAR.ToListAsync();
EvrakTypeList = await _db.EVRAK_TYPES.ToListAsync();
SendersList = await _db.SENDERS.ToListAsync();
IcraStatusList = await _db.ICRA_STATUS.ToListAsync();
DavakonuList = await _db.DAVA_KONULARI.ToListAsync();
DavaList = await _db.DAVALAR.ToListAsync();
IstidaTurList = await _db.ISTIDA_TURLERI.ToListAsync();
IstidaList = await _db.ISTIDALAR.ToListAsync();
}
public async Task ProcessTasks()
{
if(DavaLarToProcess.Count!=0)
_db.DAVALAR.AddRange(DavaLarToProcess);
Trace.WriteLine("saving");
await _db.SaveChangesAsync();
Trace.WriteLine("saved");
_db.Dispose();
DavaLarToProcess = null;
KazaList = null;
DavaTurList = null;
DavaScaleList = null;
StatusList = null;
AvukatList = null;
EvrakTypeList = null;
SendersList = null;
IcraStatusList = null;
DavakonuList = null;
DavaList = null;
IstidaTurList = null;
IstidaList = null;
}
}

How to create request in Asp.net core 1 for testing

In my Asp.net core 1 app I have controller with following method:
[Microsoft.AspNetCore.Mvc.HttpPost()]
[Microsoft.AspNetCore.Mvc.RequireHttps]
public async System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> Save()
{
if (ModelState.IsValid)
{
try
{
var pass = Request.Form["password"].ToString();
var pass1 = Request.Form["password1"].ToString();
if (!pass.Equals(pass1))
{
return View("~/Views/PasswordRecovery.cshtml");
}
}
catch (System.Exception ex)
{
return View("~/Views/Message.cshtml");
}
}
return View("~/Views/Message.cshtml");
}
I want to write a test for this method. So I have written this:
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Controllers.PasswordRecoveryController(_mockRepo.Object);
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
var collection = new Microsoft.AspNetCore.Http.FormCollection(dic);
controller.Request.Form = collection; //request is null
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/Message.cshtml", viewResult.ViewName);
}
The problem is that I need to set some test values to Form, Form is in Request, and Request is NULL. I can not find, how can I create some not NULL request and fill it's Form with values.
EDIT
Answers helped me to finish up with following solution:
I've created a method that will return a FormCollection:
private Microsoft.AspNetCore.Http.FormCollection GetFormCollection()
{
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
return new Microsoft.AspNetCore.Http.FormCollection(dic);
}
And my test method is:
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Findufix.Controllers.PasswordRecoveryController(_mockRepo.Object);
var httpContext = new Moq.Mock<Microsoft.AspNetCore.Http.HttpContext>();
httpContext.Setup( x => x.Request.Form).Returns(GetFormCollection());
controller.ControllerContext.HttpContext = httpContext.Object;
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/PasswordRecovery.cshtml", viewResult.ViewName);
}
If you pass a DefaultHttpContext to your controller, Request won't be null and you can assign the form to Request.Form. No mocking required.
[Xunit.Fact]
public async System.Threading.Tasks.Task SavePassNotEqualTest()
{
var controller = new Controllers.PasswordRecoveryController(_mockRepo.Object);
var dic = new System.Collections.Generic.Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
dic.Add("password", "test");
dic.Add("password1", "test1");
var collection = new Microsoft.AspNetCore.Http.FormCollection(dic);
// Give the controller an HttpContext.
controller.ControllerContext.HttpContext = new DefaultHttpContext();
// Request is not null anymore.
controller.Request.Form = collection;
var result = await controller.Save();
var viewResult = Xunit.Assert.IsType<Microsoft.AspNetCore.Mvc.ViewResult>(result);
Xunit.Assert.Equal("~/Views/Message.cshtml", viewResult.ViewName);
}
With Moq, you can do it like this:
var httpContext = new Mock<HttpContextBase>();
httpContext.Setup(c => c.Request.Form).Returns(delegate()
{
var formVaues = new NameValueCollection();
formVaues .Add("Id", "123");
formVaues .Add("Name", "Smith");
return formVaues ;
});
In Moq you can try to use Setup() or SetupGet() to teach it to return something that you need
something along the lines of
controller.SetupGet(x => x.Request.Form).Returns(collection);

Categories