Does webjob SDK TimerTrigger support Dependency Injection? - azure-webjobs

I am using .net core 6 WebJob SDK Version 4.0.1:
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions"Version="4.0.1" />
I added the following line to my webjob setup code:
builder.ConfigureServices(s => s.AddSingleton<MyClass>());
I have a timer trigger like this:
public class TimerFunctions
{
public void TimerTriggerTest([TimerTrigger("*/5 * * * * *")] TimerInfo myTimer,
ILogger logger,
MyClass diTest
)
{
logger.LogInformation("TimerTrigger");
}
}
When run my WebJob project locally, I get the following error:
System.InvalidOperationException: Cannot bind parameter 'diTest' to type MyClass. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

Yes, the TimerTrigger in the Azure WebJobs SDK does support Dependency Injection (DI). You can use DI to inject dependencies into your timer trigger function by using the [Inject] attribute.
For example, you could use DI to inject a service or repository into your timer trigger function, like this:
public class MyTimerTrigger
{
private readonly IMyService _myService;
public MyTimerTrigger(IMyService myService)
{
_myService = myService;
}
[FunctionName("MyTimerTrigger")]
public async Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo timer)
{
// Use the injected service here
await _myService.DoWorkAsync();
}
}

Related

How to set up ServiceBusTrigger with Dependency Injection with .Net Core 6.0 Isolated

I currently have a ServiceBus Trigger in .Net Core 6.0 Isolated. Trying to figure out how to Use Dependency Injection, to set up the Trigger. Trying to figure out how to do this with .Net Core 6.0 Isolated.
I have a strongly typed model that is Bound to the appsettings.json file in the Program.cs code. That part works and has been verified. However when trying to do this with .Net Core 6 Isolated It give error about missing reference.
Here's my Config model that is bound to the appsettings.json file. I have left out the appsettings.json file for simplification
public class MyConfig
{
public string Topic { get; set; }
public string SubscriptionName { get; set; }
}
Here is the Service bus trigger class
public class ServiceBusTriggerClass
{
private readonly MyConfig _myConfig;
public ServiceBusTriggerClass(IOptions<MyConfig> config)
{
_myConfig= config.Value;
}
[Function("MySBFunction")]
public async Task MySBFunction([ServiceBusTrigger(_myConfig.Topic, _myConfig.SubscriptionName)] object myObject)
{
// Do things with the myObject thing.
}}
As of 1-13-2022 it is not possible to do this Using .Net 6 Isolated function. The function does not have access to the Host at this point.

What happened to addLifeCycleListener in Jetty 10?

We used to have code that would bootstrap Google Guice on the startup of our jetty embedded server.
// add a lifecycle listener to bootstrap injector on startup
svr.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() {
#Override
public void lifeCycleStarted(LifeCycle event) {
System.out.println("Bootstrapping Guice injector ...");
Guice.createInjector(new GreeterServletModule(), new GreeterAppModule());
}
});
Now when we try to upgrade to Jetty 10 it says addLifeCycleListener no longer exists.
AbstractLifeCycle.AbstractLifeCycleListener is an EventListener.
use LifeCycle.addEventListener(listener).
Incidentally, the normal way to bootstrap Guice is to extend the com.google.inject.servlet.GuiceServletContextListener and add your extension to the ServletContext listeners?
This is how Google recommends it be done, and is also the way that Google themselves initialize Guice within their own frameworks (like Google App Engine).
Example from Google Cloud Platform Java Samples Project - EchoGuiceListener.java
package com.mycompany;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
public class GreeterGuiceListener extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new GreeterServletModule(), new GreeterAppModule());
}
}
with ...
ServletContextHandler contextHandler = new ServletContextHandler()
contextHandler.addEventListener(new GreeterGuiceListener());
// ... other init ...
server.start();

Quarkus unit tests exclude class

I am moving from Thorntail to Quarkus.
In my tests I used to create a #deployment method in which I put only what was needed by the tests. In particular I didn't put a Class having a #Startup annotation (because I didn't want to test that ...).
When I moved to QUARKUS, I suppress de #deployment static method, then when I launch the tests #Startup is ... started and a lot of bad things happen which prevent me from testing what I want to test (well, it crashes because it tries to connect to services which are not available).
So the question is : is there a way to exclude some package or class when lauching a test with quarkusTest ?
I finally created a class :
#ApplicationScoped
public class ApplicationLifeCycle {
private final Logger log = Logger.getLogger(getClass());
#Inject
Startup startup;
void onStart(#Observes StartupEvent ev) {
log.info("The application is starting with profile " + ProfileManager.getActiveProfile());
if (!ProfileManager.getActiveProfile().equalsIgnoreCase("test")) {
startup.getModel();
}
}
void onStop(#Observes ShutdownEvent ev) {
log.info("The application is stopping...");
startup.stopMQ();
}
}
A bit ugly isn't it ?
Is there a better way to do it ?
Quarkus adds a few capabilities to handle such scenarios regarding CDI, which can be found here: https://quarkus.io/guides/cdi-reference#enable_build_profile
For instance:
#ApplicationScoped
#UnlessBuildProfile("test")
class ConnectsToExternalService implements ExternalConnector {}
Will prevent CDI from injecting this implementation when the quarkus profile is "test", which quarkus sets when running a #QuarkusTest.
So if you inject "ExternalConnector" in a #QuarkusTest, you'll get an Unsatisfied dependency exception, which can be fixed with:
#ApplicationScoped
#IfBuildProfile("test") // not required if class is in "test" dir
class MockExternalService implements ExternalConnector {}
Using this approach you can prevent external connections but also mock them.
To top things off, one can now disable all StartupEvent and ShutdownEvent observers declared on application using a QuarkusTestProfile in combination with #TestProfile (See Quarkus Testing with Different Profiles). You have to return true in disableApplicationLifecycleObservers in that case.

How to do Unit Test with Dependency Injection .net Core 2

I start to develop a new web application, I create a Domain Object, Inteface, DAL and BLL...
I would like to test all before use that.
If I use the developed function in web application in .net core 2 I put in Startup.cs some code like this :
public void ConfigureServices(IServiceCollection services)
{
**services.AddTransient<ITableOfTableRepository, DBTableOfTableRepository>();**
services.AddMvc();
services.AddSingleton<IConfiguration>(Configuration);
}
And in my Controller add this code
public class TablesController : Controller
{
private readonly ITableOfTableRepository _repository;
public TablesController(ITableOfTableRepository repository)
{
this._repository = repository;
}
How to do a UnitTest project for testing all before of the use in web application?
How to use dependency Injection in unit test?
BR
If you are trying to develop in a test first approach...
You will pass by several steps:
Your test is not compiling because you need to write a controller action and create an interface
Once you created an interface you could mock/stub it (using framework like NSubstitute or others) and inject when you create the controller
var userService = Substitute.For();
...
var controller = new MyController(userService)
you write the controller code for your test to pass

JMS integration testing

I am trying to write an integration test for JMS service which looks like something like this.
#JmsListener(destination = "mailbox", containerFactory = "myFactory")
public void receiveMessage(Email message) throws InterruptedException {
try {
sendEmail(message);
}catch (Exception e){
LOGGER.log(Level.SEVERE,"Failed to deliver email",e);
Thread.sleep(TimeUnit.SECONDS.toSeconds(Optional.of(retryInterval).orElse(5)));
throw e;
}
}
private void sendEmail(Email message){
...............
}
First of all, can I mock this some how? I tried mocking it, but when I send a message the spring boot application is calling the actual JMS bean not the mock one. Seems like this is not possible.
Even if this is not possible, can I at least aoutowire the bean and somehow check if the receiveMessage method is being invoked. Furthermore, if it is being invoked, the sendEmail part should be faked so that it does not do any work. I have a few ideas such as creating a subclass for testing, but not happy with either of them. So wanted to if you can suggest me a better work around?
One approach is to use different profiles for say development, integration test and production and annotate the different components and your integration test class accordingly.
#Component
#Profile("it")
public class MessageReceiverIT {
#JmsListener(destination = "mailbox", containerFactory = "myFactory")
public void receiveMessage(SimpleMessage email) {
log.info("Integration test pretend to receive {}", email);
// (...)
This is the Integration test that uses the same Application class as the real Application, but if a message is received the MessageReceiverIT.receiveMessage() method will be invoked instead of the production component:
#RunWith(SpringRunner.class)
#SpringBootTest(classes=Application.class)
#ActiveProfiles("it")
public class JmsIntegrationTest {
#Inject
ConfigurableApplicationContext context;
#Test
public void testSend() throws Exception{
JmsTemplate jmsTemplate = context.getBean(JmsTemplate.class);
jmsTemplate.convertAndSend("mailbox", new SimpleMessage("it", "we need more IT"));
// (...)
Also check out Spring Boot Testing for alternative approaches such as the use of #TestConfiguration. I'm using Spring Boot in my examples, but there should be similar approaches if you have a none Spring Boot Application.