I have these three lines of c# code using Moq, how can I write a single line?
JobQueueRepository.Setup(r => r.UpdateJobQueueStatus(DefaultJobId, JobStatus.Success)).Callback(() => statuses.Add(JobStatus.Success));
JobQueueRepository.Setup(r => r.UpdateJobQueueStatus(DefaultJobId, JobStatus.Failed)).Callback(() => statuses.Add(JobStatus.Failed));
JobQueueRepository.Setup(r => r.UpdateJobQueueStatus(DefaultJobId, JobStatus.Running)).Callback(() => statuses.Add(JobStatus.Running));
Thanks for the help.
There is a piece of code you are asking for
JobQueueRepository
.Setup(it => it.UpdateJobQueueStatus(DefaultJobId, It.IsAny<JobStatus>()))
.Callback<int, JobStatus>((id, status) => statuses.Add(status));
And a test that tests how it works
[TestClass]
public class TestClass
{
[TestMethod]
public void TestMethod()
{
var statuses = new List<JobStatus>();
var JobQueueRepository = new Mock<IJobQueueRepository>();
int DefaultJobId = 100500;
JobQueueRepository
.Setup(it => it.UpdateJobQueueStatus(DefaultJobId, It.IsAny<JobStatus>()))
.Callback<int, JobStatus>((id, status) => statuses.Add(status));
JobQueueRepository.Object.UpdateJobQueueStatus(DefaultJobId, JobStatus.Failed);
JobQueueRepository.Object.UpdateJobQueueStatus(DefaultJobId, JobStatus.Running);
JobQueueRepository.Object.UpdateJobQueueStatus(DefaultJobId, JobStatus.Success);
statuses.Should().HaveCount(3);
statuses.Should().Contain(JobStatus.Failed);
statuses.Should().Contain(JobStatus.Running);
statuses.Should().Contain(JobStatus.Success);
}
public enum JobStatus
{
Success,
Failed,
Running
}
public interface IJobQueueRepository
{
void UpdateJobQueueStatus(int id, JobStatus status);
}
}
You can easily create an extension method to do that as below.
public class Class1
{
[Test]
public void CallBackDemo() {
var statuses = new List<JobStatus>();
var jobQueueRepositoryStub = new Mock<IJobQueueRepository>();
const int defaultJobId = 100500;
jobQueueRepositoryStub.Setup(r => r.UpdateJobQueueStatus(defaultJobId, JobStatus.Success))
.Callback( new Action[]
{
() => statuses.Add(JobStatus.Success),
() => statuses.Add(JobStatus.Failed),
() => statuses.Add(JobStatus.Running)
});
var sut = new Sut(jobQueueRepositoryStub.Object);
sut.Do(defaultJobId);
Assert.True(statuses.Count == 3);
Assert.True(statuses.Any(x => x == JobStatus.Failed));
Assert.True(statuses.Any(x => x == JobStatus.Running));
Assert.True(statuses.Any(x => x == JobStatus.Success));
}
Callback extension method:
public static class Ext
{
public static void Callback<TRepo>(this ISetup<TRepo> repo, IEnumerable<Action> actions ) where TRepo : class {
foreach (var action in actions) {
action();
}
}
}
Sut (System Under Test) and other classes:
public enum JobStatus { Success, Failed, Running }
public interface IJobQueueRepository {
void UpdateJobQueueStatus(int id, JobStatus status);
}
public class Sut {
private readonly IJobQueueRepository _repository;
public Sut(IJobQueueRepository repository) {
_repository = repository;
}
public void Do(int jobId) {
_repository.UpdateJobQueueStatus(jobId, JobStatus.Success);
_repository.UpdateJobQueueStatus(jobId, JobStatus.Failed);
_repository.UpdateJobQueueStatus(jobId, JobStatus.Running);
}
}
Related
I have a .NET 6 Razor Pages app that triggers background tasks and then informs the user of their status via SignalR.
I'm trying to use Database1 context in the PerformBackgroundJob method, but it's disposed. What technique should I use to inject Database1 context in PerformBackgroundJob, or how else can I get this to work?
namespace Toolkat.Pages
{
public class ProcessModel : PageModel
{
private readonly Database1Context _context;
private readonly ToolkatContext _tkcontext;
private IConfiguration configuration;
private readonly IQueue _queue;
private readonly IHubContext<JobHub> _hubContext;
static ServerConnection conn;
static Server server;
static Job job;
public ProcessModel(
Database1Context context,
ToolkatContext tkcontext,
IConfiguration _configuration,
IQueue queue,
IHubContext<JobHub> hubContext)
{
_context = context;
_tkcontext = tkcontext;
configuration = _configuration;
_queue = queue;
_hubContext = hubContext;
}
public IList<CustomFileImport> CustomFileImport { get; set; } = default!;
[BindProperty]
public CustomFileImport CustomFileImportNumberTwo { get; set; } = default!;
public async Task OnGetAsync()
{
if (_context.CustomFileImports != null)
{
CustomFileImport = await _context.CustomFileImports
.Include(c => c.FileImportType)
.Include(c => c.FileImportStatus)
.Where(i => i.FileImportStatusId.Equals(1))
.ToListAsync();
}
}
public async Task<IActionResult> OnPostAsync(int[] fileImportId)
{
//Generate GUID
Guid jobId = Guid.NewGuid();
//Update FileImportItems with GUID
foreach (var id in fileImportId)
{
if (/*id == null ||*/ _context.CustomFileImports == null)
{
return NotFound();
}
var customfileimport = await _context.CustomFileImports.FirstOrDefaultAsync(m => m.FileImportId == id);
if (customfileimport == null)
{
return NotFound();
}
customfileimport.ProcessId = jobId;
await _context.SaveChangesAsync();
}
_queue.QueueAsyncTask(() => PerformBackgroundJob(jobId));
return RedirectToPage("./Result", new { jobId });
}
private async Task PerformBackgroundJob(Guid jobId /*CancellationToken cancellationToken*/)
{
await _hubContext.Clients.Group(jobId.ToString()).SendAsync("progress", "PerformBackgroundJob Started");
/*
var customFileImports = await _context.CustomFileImports
.Include(c => c.FileImportType)
.Where(i => i.ProcessId.Equals(jobId))
.ToListAsync();
*/
Debug.WriteLine("ProviderName:" + _context.Database.ProviderName);
/*
foreach (var f in customFileImports)
{
await _hubContext.Clients.Group(jobId.ToString()).SendAsync("progress", WebUtility.HtmlEncode(f.FileName));
}
*/
}
}
}
I had to combine lessons from lots of articles to figure this out. Hangfire has a nice way of approaching this.
Replace
_queue.QueueAsyncTask(() => PerformBackgroundJob(jobId));
With
BackgroundJob.Enqueue<ProcessFilesService>(x => x.DoWork());
Passing dependencies
and create this class
public class ProcessFilesService
{
IServiceProvider _serviceProvider;
public ProcessFilesService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void DoWork()
{
using var scope = _serviceProvider.CreateScope();
var ctx = scope.ServiceProvider.GetRequiredService<MyDatabaseContext>();
using var hubScope = _serviceProvider.CreateScope();
var _hubContext = hubScope.ServiceProvider.GetRequiredService<JobHub>();
Debug.WriteLine(ctx.Database.ProviderName);
}
}
Hmm...I didn't need to register it as a service in program.cs and it appears to still be working. Will have to learn more about that.
I have created an attribute for action names. I want to get the attribute names in my service. I tried so many solutions but it doesn't return anything.
This is my attribute class:
public class CustomeAttribute : ActionFilterAttribute
{
public string Name { get; set; }
}
This is the action that I used the attribute for:
[Custome(Name ="ُShow courses")]
public IActionResult Index()
{
var course = _courseService.GetAllCourses();
return View(course);
}
This is the method that I want to return the attribute name:
public IList<ActionAndControllerName> AreaAndActionAndControllerNamesList(Assembly asm)
{
var contradistinction = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type))
.SelectMany(type =>
type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly |
BindingFlags.Public))
.Select(x => new
{
Controller = x.DeclaringType?.Name,
//Action = x.Name,
//Action=x.DeclaringType?.GetCustomAttributes(typeof(CustomeAttribute), false),
//
Action=x.DeclaringType?.CustomAttributes.Where(c=>c.AttributeType==typeof(CustomeAttribute)),
// Action=x.DeclaringType?.GetCustomAttributes(typeof(CustomeAttribute), false),
// Action=x.DeclaringType?.CustomAttributes(typeof(CustomeAttribute), false),
//Action=x.DeclaringType?.GetCustomAttribute(typeof(CustomeAttribute), false),
Action=x.DeclaringType?.GetCustomAttributes<CustomeAttribute>(),
//Action = x.DeclaringType?.GetCustomAttributes().Where(a => a.GetType() ==
typeof(CustomeAttribute))
Area = x.DeclaringType?.CustomAttributes.Where(c => c.AttributeType ==
typeof(AreaAttribute)),
});
}
As I said I tried the solutions above that are commented but none of them worked. What should I do?
You can try to save Name to some place in ActionfilterAttribute.Here is a demo to save data to session in OnActionExecuting method:
TestController:
SomeOtherClass _someOtherClass;
public TestController(SomeOtherClass someOtherClass)
{
_someOtherClass = someOtherClass;
}
[Custome(Name = "Show courses")]
public IActionResult TestActionFilterAttribute()
{
var Name = _someOtherClass.TestGet();
return Ok();
}
SomeOtherClass:
public class SomeOtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession _session => _httpContextAccessor.HttpContext.Session;
public SomeOtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string TestGet()
{
return _session.GetString("Custome_Name");
}
}
Startup.cs(IHttpContextAccessor can help get seesion outside controller):
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromDays(1);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<SomeOtherClass, SomeOtherClass>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
...
}
CustomeAttribute:
public class CustomeAttribute: ActionFilterAttribute
{
public string Name { get; set; }
public override void OnActionExecuting(ActionExecutingContext
context)
{
if (Name != null)
{
context.HttpContext.Session.SetString("Custome_Name", Name);
}
}
public override void OnActionExecuted(ActionExecutedContext
context)
{
}
}
result:
I found the solution.I shouldn't have used "DeclaringType" in service.
This is the solution:
var contradistinction = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type))
.SelectMany(type =>
type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly |
BindingFlags.Public))
.Select(x => new
{
Controller = x.DeclaringType?.Name,
Action = x.GetCustomAttribute<CustomeAttribute>()?.Name,
Area = x.DeclaringType?.CustomAttributes.Where(c => c.AttributeType ==
typeof(AreaAttribute)),
});
I create Event Maped Seat reservation and using SignalR creates a realtime seat update status view
My BroadcastHub
public class BroadcastHub : Hub
{
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
public async Task RemoveFromGroup(string groupName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
public async Task SeatUpdate(long SectinId, string groupName, long SeatId, SeatStatus seatStatus)
{
await Clients.OthersInGroup(groupName).SendAsync("ReceiveSeatUpdate", SectinId, SeatId, seatStatus);
}
}
Component
<div class="col-md-12 mb-3">
#((MarkupString)#SectionData.Salon.SalonMap)
</div>
...Seat Selection Murkup....
#code {
private HubConnection hubConnection;
public bool IsConnected => hubConnection.State == HubConnectionState.Connected;
Task SeatUpdate(long SectinId, string EventId, long SeatId, SeatStatus seatStatus) => hubConnection.SendAsync("SeatUpdate", SectinId, EventId, SeatId, seatStatus);
protected override async Task OnInitializedAsync()
{
SectionData.OnChange += StateHasChanged;
SectionData.Salon = await DataService.GetSalon();
action = GetSection;
foreach (var item in salon.Sections)
{
SectionData.Salon.SalonMap =salon.SalonMap.Replace(item.Action,$"onclick='app.GetSectionCallerJS({item.Id})'");
}
#region Hub
hubConnection = new HubConnectionBuilder().WithUrl(NavigationManager.ToAbsoluteUri("/broadcastHub")).Build();
hubConnection.On("ReceiveSeatUpdate", async (long SectinId, long SeatId, SeatStatus seatStatus) =>
{
if (SectionData.Section.Id == SectinId)
{
var Seat = SectionData.Section.Seats.Values.Where(x => x.Id == SeatId).FirstOrDefault();
Seat.SeatStatus = seatStatus;
}
StateHasChanged();
});
await hubConnection.StartAsync();
await hubConnection.SendAsync("AddToGroup", EventSansUniqueId);
#endregion Hub
}
#region GetSection
private static Action<long> action;
private void GetSection(long SectionId)
{
var section= salon.Sections.Where(x => x.Id == SectionId).FirstOrDefault();
SectionData.SetSection(section);
SectionData.Section.Seats = DataService.GetSection(SectionId);
StateHasChanged();
}
[JSInvokable]
public static void GetSectionCaller(long SectionId)
{
action.Invoke(SectionId);
}
#endregion GetSection
public void Dispose()
{
SectionData.OnChange -= StateHasChanged;
if (IsConnected) hubConnection.SendAsync("RemoveFromGroup", EventSansUniqueId);
}
}
JavaScript Is
window.app = {
GetSectionCallerJS: (id) => {
DotNet.invokeMethodAsync('KishApp.TRMS.Salon', 'GetSectionCaller', id);
}
};
The problem is when the hub registers for the second, third, and... time DotNet.invokeMethodAsync Call Last registered page, not the one actually calling the method and causing the wrong page update
tanks to #MisterMango I found that problem GetSectionCaller is a static method and I must have created new DotNetObjectReference every time page initial so
DotNetObjectReference<Salon> ObjectReference;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
ObjectReference = DotNetObjectReference.Create<Salon>(this);
await JSRuntime.InvokeVoidAsync("app.setObjectReference", ObjectReference);
}
}
[JSInvokable("GetSectionCaller")]
public void GetSectionCaller(long SectionId)
{
GetSection(SectionId);
}
JavaScript
window.app = {
GetSectionCallerJS: (id) => {
dotNetObject.invokeMethodAsync('GetSectionCaller', id);
},
setObjectReference: (ObjectReference) => {
this.dotNetObject = ObjectReference;
}};
Does the SimpleInjectorContainerAdapter support Sagas that are registered through the SimpleInjector container, using the following code I always get the exception;
type RebusPlaypen.MyMessageA, RebusPlaypen could not be dispatched to any handl
ers
The following code demonstrates the issue. Could this be that I am implementing the Saga registration incorrectly, or does the SimpleInjectorContainerAdapter not support this type of registration ?
using Rebus.Bus;
using Rebus.Config;
using Rebus.Handlers;
using Rebus.Retry.Simple;
using Rebus.Routing.TypeBased;
using Rebus.Sagas;
using Rebus.SimpleInjector;
using Rebus.Transport.InMem;
using SimpleInjector;
using System;
using System.Threading.Tasks;
// Rebus.3.1.2
// Rebus.SimpleInjector.3.0.0
namespace RebusPlaypen
{
public interface IMyDependency
{
void DoSomethingGood();
}
public class MyDependency : IMyDependency
{
public void DoSomethingGood()
{
Console.WriteLine("I've done something");
}
}
public class MyMessageA
{
public Guid CollationId { get; set; }
public string FaveIceCreamFlavour { get; set; }
}
public class MyMessageB
{
public Guid CollationId { get; set; }
public string FaveBand{ get; set; }
}
public class MyMessageSagaData : ISagaData
{
public Guid Id {get;set;}
public int Revision {get;set;}
public Guid CollationId {get;set;}
public bool HasFaveBand { get; set; }
}
public interface IMyMessageSaga : IAmInitiatedBy<MyMessageA>,
IHandleMessages<MyMessageB>
{
}
public class MyMessageSaga: Saga<MyMessageSagaData>,
IMyMessageSaga
{
readonly IMyDependency _myDependency;
readonly IBus _bus;
public MyMessageSaga(IMyDependency myDependency,
IBus bus)
{
_myDependency = myDependency;
_bus = bus;
}
protected override void CorrelateMessages(ICorrelationConfig<MyMessageSagaData> config)
{
config.Correlate<MyMessageA>(s => s.CollationId, d => d.CollationId);
config.Correlate<MyMessageB>(s => s.CollationId, d => d.CollationId);
}
public async Task Handle(MyMessageA message)
{
Console.WriteLine("Handled MyMessageA");
_myDependency.DoSomethingGood();
await _bus.Send(new MyMessageB { CollationId = message.CollationId, FaveBand = "Depeche Mode" });
await PossiblyPerformCompleteAction();
}
public async Task Handle(MyMessageB message)
{
Console.WriteLine("Handled MyMessageB");
_myDependency.DoSomethingGood();
Data.HasFaveBand = true;
await PossiblyPerformCompleteAction();
}
async Task PossiblyPerformCompleteAction()
{
if (Data.HasFaveBand)
{
MarkAsComplete();
}
}
}
public static class RebusSimpleInjectorSagaDemo
{
public static void Run()
{
var container = new Container();
container.Register<IMyDependency, MyDependency>();
container.Register<MyMessageSaga>(Lifestyle.Transient);
container.Register<IMyMessageSaga>(() => container.GetInstance<MyMessageSaga>(), Lifestyle.Transient);
var network = new InMemNetwork(true);
var adapter = new SimpleInjectorContainerAdapter(container);
var _bus = Configure
.With(adapter)
.Logging(l => l.ColoredConsole(Rebus.Logging.LogLevel.Error))
.Transport(t => t.UseInMemoryTransport(network,"my_nice_queue"))
.Routing(r => r.TypeBased().MapAssemblyOf<MyMessageA>("my_nice_queue"))
.Options(o =>
{
o.SetNumberOfWorkers(1);
o.SetMaxParallelism(1);
o.SimpleRetryStrategy(maxDeliveryAttempts: 1);
})
.Start();
container.Verify();
_bus.Send(new MyMessageA { CollationId = Guid.NewGuid(), FaveIceCreamFlavour = "Strawberry" }).Wait();
Console.WriteLine("Running");
Console.ReadLine();
}
}
}
For completeness, the following changes allowed the code in the original question to work correctly with the Simple Injector container;
public static class RebusSimpleInjectorSagaDemo
{
public static void Run()
{
var container = new Container();
container.Register<IMyDependency, MyDependency>();
// The missing registration
container.RegisterCollection(typeof(IHandleMessages<>), new [] {Assembly.GetExecutingAssembly()});**
var network = new InMemNetwork(true);
var adapter = new SimpleInjectorContainerAdapter(container);
var _bus = Configure
.With(adapter)
.Logging(l => l.ColoredConsole(Rebus.Logging.LogLevel.Error))
.Transport(t => t.UseInMemoryTransport(network,"my_nice_queue"))
.Routing(r => r.TypeBased().MapAssemblyOf<MyMessageA>("my_nice_queue"))
.Options(o =>
{
o.SetNumberOfWorkers(1);
o.SetMaxParallelism(1);
o.SimpleRetryStrategy(maxDeliveryAttempts: 1);
})
.Start();
container.Verify();
_bus.Send(new MyMessageA { CollationId = Guid.NewGuid(), FaveIceCreamFlavour = "Strawberry" }).Wait();
Console.WriteLine("Running");
Console.ReadLine();
}
}
No matter which IoC container you use, you must ensure that your handlers are resolved by the IHandleMessages<TMessage> implementations they provide.
If you try and
container.GetAllInstances<IHandleMessages<MyMessageA>>();
or
container.GetAllInstances<IHandleMessages<MyMessageB>>();
you will see that no handlers are returned. That's why Rebus cannot find any handlers to dispatch your messages to :)
My problem is mocked object doesn't return the value it return null instead
My MSpec UnitTest as follows
public class With_Fake_Data_Service
{
protected static Mock<IMProposalWCFService> _fakeDataService;
protected static FaultContract fault;
private Establish context = () =>
{
_fakeDataService = new Mock<IMProposalWCFService>();
_fakeDataService.Setup(
service =>
service.ReplyToProposal(new ReplyToProposalRequest(Moq.It.IsAny<Proposal>(), Moq.It.IsAny<bool>())))
.Returns(new ReplyToProposalResponse( Moq.It.IsAny<bool>(), fault));
_fakeDataService.Setup(
service => service.ReplyToProposalEmail(new ReplyToProposalEmailRequest(Moq.It.IsAny<string>(), Moq.It.IsAny<bool>())))
.Returns(new ReplyToProposalEmailResponse(Moq.It.IsAny<string>(), fault));
_fakeDataService.Setup(service => service.GetAllProposals(Moq.It.IsAny<GetAllProposalsRequest>()))
.Returns(() => new GetAllProposalsResponse(new List<Proposal>(){new Proposal()}, fault));
_fakeDataService.Setup(service => service.GetAllProposals(Moq.It.IsAny<GetAllProposalsRequest>())).Verifiable();
};
}
public class When_Testing_HomeController_Index : With_Fake_Data_Service
{
protected static HomeController _homeController;
protected static IList<Proposal> _proposals;
private Establish context = () =>
{
_homeController = new HomeController(_fakeDataService.Object);
};
private Because of = () =>
{
var result = _homeController.Index() as ViewResult;
_proposals = result.Model as IList<Proposal>;
};
private It Should_Have_Called_GetAllProposals_At_Least_Once =
() => _fakeDataService.Verify(service => service.GetAllProposals(Moq.It.IsAny<GetAllProposalsRequest>()), Times.Exactly(1));
}
Inside the Actual Code HomeController Index Method I get response as null when running above unit test
public HomeController(IMProposalWCFService service)
{
_service = service;
}
public ActionResult Index()
{
var response = _service.GetAllProposals(new GetAllProposalsRequest());
if (response.fault == null) /*Right Here reponse is null when running unit test*/
{
var proposals = response.GetAllProposalsResult;
}
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
Why am i Not getting not null response in other words why i'm not getting
new GetAllProposalsResponse(new List(){new Proposal()}, fault)
please help
Found the problem its
public class With_Fake_Data_Service
{
protected static Mock<IMProposalWCFService> _fakeDataService;
protected static FaultContract fault;
private Establish context = () =>
{
_fakeDataService = new Mock<IMProposalWCFService>();
_fakeDataService.Setup(
service =>
service.ReplyToProposal(new ReplyToProposalRequest(Moq.It.IsAny<Proposal>(), Moq.It.IsAny<bool>())))
.Returns(new ReplyToProposalResponse( Moq.It.IsAny<bool>(), fault));
_fakeDataService.Setup(
service => service.ReplyToProposalEmail(new ReplyToProposalEmailRequest(Moq.It.IsAny<string>(), Moq.It.IsAny<bool>())))
.Returns(new ReplyToProposalEmailResponse(Moq.It.IsAny<string>(), fault));
_fakeDataService.Setup(service => service.GetAllProposals(Moq.It.IsAny<GetAllProposalsRequest>()))
.Returns(() => new GetAllProposalsResponse(new List<Proposal>(){new Proposal()}, fault)).Verifiable();
};
}