Open Closed

How to initialize Quartz in integration testing #1847


0
LW created
  • ABP Framework version: 4.4.0

  • UI type: Angular

  • DB provider: EF Core

  • Tiered (MVC) or Identity Server Separated (Angular): yes

  • **Exception message and stack trace olo.Abp.AbpInitializationException : An error occurred during the initialize Volo.Abp.Modularity.OnApplicationInitializationModuleLifecycleContributor phase of the module Volo.Abp.Quartz.AbpQuartzModule, Volo.Abp.Quartz, Version=4.4.0.0, Culture=neutral, PublicKeyToken=null: An exception was thrown while activating λ:Quartz.IScheduler.. See the inner exception for details. ---- Autofac.Core.DependencyResolutionException : An exception was thrown while activating λ:Quartz.IScheduler. -------- Quartz.SchedulerException : Scheduler with name 'QuartzScheduler' already exists.

    Stack Trace: ModuleManager.InitializeModules(ApplicationInitializationContext context) AbpApplicationBase.InitializeModules() AbpApplicationWithExternalServiceProvider.Initialize(IServiceProvider serviceProvider) AbpIntegratedTest1.ctor() BackgroundProcessesTestBase1.ctor() SchedulerCreationFailingTestExample.ctor() ----- Inner Stack Trace ----- ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action1 next) <>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) ResolvePipeline.Invoke(ResolveRequestContext ctxt) <42 more frames...> AsyncHelper.RunSync[TResult](Func1 func) <>c.<ConfigureServices>b__1_1(IServiceProvider serviceProvider) <>c__DisplayClass3_0.<Register>b__0(IComponentContext context, IEnumerable1 parameters) DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable1 parameters) DelegateActivator.<ConfigurePipeline>b__2_0(ResolveRequestContext ctxt, Action1 next) DelegateMiddleware.Execute(ResolveRequestContext context, Action1 next) <>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) DisposalTrackingMiddleware.Execute(ResolveRequestContext context, Action1 next) <>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt) ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action1 next)

  • **Steps to reproduce the issue:

I'm tring to unit test my Quarz background workers. Whenever I run multiple tests where I initialize a backround worker the tests fail because the IScheduler is already created with another test. My tests are integration tests since I need some registered services in my workers. I dosen't make a difference if I use a new scheduler for the actual scheduling in my tests like this:

	IScheduler scheduler = await GetNewScheduler();

    // Act
    ISchedulerListener listener = Substitute.For<ISchedulerListener>();
    scheduler.ListenerManager.AddSchedulerListener(listener);
    await worker.ScheduleJob.Invoke(scheduler);

My test module depend on a module that depends on AbpBackgroundWorkersQuartzModule and through that the Quartz gets initialized. It seems to me that AbpIntegratedTest initialization doesn't take into account that the Quartz cannot be initialized multiple times. I realize that this is possibly a Quarz problem but is there a some way to change the Quarz initialization so that IScheduler implementation will be initialized only once.


5 Answer(s)
  • 0
    berkansasmaz created
    Support Team

    Hi,

    The point we are trying to test here should not be Quartz. Currently your tests depend on Quartz, ie the behavior of a library, but this is not a good method.

    In my view changing your Execute method as follows provides a more testable environment:

            public override async Task Execute(IJobExecutionContext context)
            {
                Logger.LogInformation("Starting:  EventReminderWorker...");
    
                
                // It might be better to write tests for this service
                await ServiceProvider
                    .GetRequiredService<YourOperationService>() 
                    .EventReminder();
                
                
                Logger.LogInformation("Completed: EventReminderWorker...");
            }
    
  • 0
    LW created

    Hello, thank you for your answer. I wasn't quite clear on what I was testing. I'm not trying to test the Quarz library, I'm just using it as a tool in my test to assert my logic.

    public Ctor(Guid tenantId, string tenantName, string scheduleDefinition)
    		{
    			Check.NotNullOrEmpty(tenantName, nameof(tenantName));
                Check.NotNullOrEmpty(scheduleDefinition, nameof(scheduleDefinition));
    
    			JobDataMap jobDataMap = new()
    			{
    				{ "TenantId", tenantId }
    			};
    			JobDetail = JobBuilder.Create<ScheduledControlValueCalculationBackgroundWorker>()
    				.WithIdentity(TenantSpesificBackgoundProcessNameFormat.GetFormattedName(this, tenantName))
    				.SetJobData(jobDataMap)
    				.Build();
    			Trigger = TriggerBuilder.Create()
    				.WithIdentity(TenantSpesificBackgoundProcessNameFormat.GetFormattedName(this, tenantName))
    				/* 
    				 * Put the schedule to the definition and do the rescheduling based on that info. No other easy way to check
    				 * if the triggers are really the same. 
    				 */
    				.WithDescription(scheduleDefinition)
    				.WithCronSchedule(scheduleDefinition, options => options.WithMisfireHandlingInstructionFireAndProceed() )
    				.Build();
    			
    			ScheduleJob = async scheduler =>
    			{
    				if (!await scheduler.CheckExists(JobDetail.Key))
    				{
    					await scheduler.ScheduleJob(JobDetail, Trigger);
    				}
    				else
    				{
    					// If the job already exists there might be changes to the schedule so reschedule the job if so
    					IJobDetail jobDetail = await scheduler.GetJobDetail(JobDetail.Key);
    					
    					var trigger = await scheduler.GetTrigger(Trigger.Key);
    					if(trigger.Description != Trigger.Description)
    					{
    						await scheduler.RescheduleJob(trigger.Key, Trigger);
    					}
    				}
    			};
    		}
    
  • 0
    liangshiwei created
    Support Team

    Hi,

    See https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs#L69

    You can get existing instances from the DI container

  • 0
    LW created

    Hi, that won't matter. For some reason the module initalization tries to initialize the scheduler twice if I have two tests. The problem is in the initalization, not in how I get the scheduler in my tests.

  • 0
    liangshiwei created
    Support Team

    Hi,

    Can you proivde steps to reproduce it? it will be better if you can provide a simple project, thanks.