I have a service:
#RolesAllowed({ROLE_ADMIN, ROLE_USER})
#Stateless
#Path("test")
public class TestServiceImpl implements TestService
And a TestNG test with the following
#Listeners({ApplicationComposerListener.class})
#EnableServices("jax-rs")
public class BaseTest {
#EJB
private TestService rs;
#Module
#Classes(cdi = true, value = {...})
public WebApp myWebApplication() {
return new WebApp()
.contextRoot("myRoot")
.addServlet("", ApplicationConfig.class.getName());
}
...
MyResponse res = WebClient
.create("http://localhost:4204/myRoot")
.accept(MediaType.APPLICATION_JSON)
.type(MediaType.TEXT_PLAIN)
.path("test/...")
.query("param", someParam)
.get(MyResponse.class);
This works fine as long as I remove the RolesAllowed from the service, but won't allow access when it's there (as it should)
Since I'm not testing the security but rather end-2-end functionality, is there a way to simulate the relevant security-context with no knowledge of the full authentication mechanism?
Maybe something like
#Module
#Classes(cdi = true, value = {...})
public WebApp myWebApplication() {
return new WebApp()
.contextRoot("myRoot")
.withRoles({ROLE_ADMIN, ROLE_USER})
.addServlet("", ApplicationConfig.class.getName());
}
Related
Like Spring framework, I want to create a Pointcut to execute some logic before executing the method. Is it possible to do that in Helidon MP?
#Pointcut("execution(public * *(..))")
private void anyPublicOperation(String input) {}
Helidon MP, like all MicroProfile implementations, is centered around CDI, which offers, for this purpose, interceptors and decorators.
I have already done that using Interceptor. Thanks!
Here is the example:
Creating a custom annotation with #InterceptorBinding
#InterceptorBinding
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface SoapSecure {
String tokenParam() default "token";
}
Creating the interceptor
#Priority(1)
#Interceptor
#SoapSecure
#Slf4j
public class SoapAuthenticationInterceptor {
#Inject
private AuthService authService;
#AroundInvoke
public Object validateToken(InvocationContext invocationContext) throws Exception {
Method method = invocationContext.getMethod();
log.info("Validate the token from SOAP APIs: " + method.getName());
String tokenParam = method
.getAnnotation(SoapSecure.class)
.tokenParam();
Parameter[] params = method.getParameters();
String accessToken = null;
for (Parameter p : params) {
if (p.getName().equals(tokenParam)) {
// validate the access token
authService.validateAccessToken(Objects.toString(method.invoke(p)));
}
}
return invocationContext.proceed();
}
}
Then use it:
#SoapSecure
public boolean test(String token){}
I have the following ASP.NET Core integration test using a custom WebApplicationFactory
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint>
where TEntryPoint : class
{
public CustomWebApplicationFactory()
{
this.ClientOptions.AllowAutoRedirect = false;
this.ClientOptions.BaseAddress = new Uri("https://localhost");
}
public ApplicationOptions ApplicationOptions { get; private set; }
public Mock<IClockService> ClockServiceMock { get; private set; }
public void VerifyAllMocks() => Mock.VerifyAll(this.ClockServiceMock);
protected override TestServer CreateServer(IWebHostBuilder builder)
{
this.ClockServiceMock = new Mock<IClockService>(MockBehavior.Strict);
builder
.UseEnvironment("Testing")
.ConfigureTestServices(
services =>
{
services.AddSingleton(this.ClockServiceMock.Object);
});
var testServer = base.CreateServer(builder);
using (var serviceScope = testServer.Host.Services.CreateScope())
{
var serviceProvider = serviceScope.ServiceProvider;
this.ApplicationOptions = serviceProvider.GetRequiredService<IOptions<ApplicationOptions>>().Value;
}
return testServer;
}
}
which looks like it should work but the problem is that the ConfigureTestServices method is never being called, so my mock is never registered with the IoC container. You can find the full source code here.
public class FooControllerTest : IClassFixture<CustomWebApplicationFactory<Startup>>, IDisposable
{
private readonly HttpClient client;
private readonly CustomWebApplicationFactory<Startup> factory;
private readonly Mock<IClockService> clockServiceMock;
public FooControllerTest(CustomWebApplicationFactory<Startup> factory)
{
this.factory = factory;
this.client = factory.CreateClient();
this.clockServiceMock = this.factory.ClockServiceMock;
}
[Fact]
public async Task Delete_FooFound_Returns204NoContent()
{
this.clockServiceMock.SetupGet(x => x.UtcNow).ReturnsAsync(new DateTimeOffset.UtcNow);
var response = await this.client.DeleteAsync("/foo/1");
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
}
public void Dispose() => this.factory.VerifyAllMocks();
}
I've blogged about ASP.NET Core Integration Testing & Mocking using Moq. It's not simple and requires some setup but I hope it helps someone out. Here is the basic code you need using ASP.NET Core 3.1:
Startup
The ConfigureServices and Configure methods in your applications Startup class must be virtual. This is so that we can iherit from this class in our tests and replace production versions of certain services with mock versions.
public class Startup
{
private readonly IConfiguration configuration;
private readonly IWebHostingEnvironment webHostingEnvironment;
public Startup(IConfiguration configuration, IWebHostingEnvironment webHostingEnvironment)
{
this.configuration = configuration;
this.webHostingEnvironment = webHostingEnvironment;
}
public virtual void ConfigureServices(IServiceCollection services) =>
...
public virtual void Configure(IApplicationBuilder application) =>
...
}
TestStartup
In your test project, override the Startup class with one that registers the mock and the mock object with IoC.
public class TestStartup : Startup
{
private readonly Mock<IClockService> clockServiceMock;
public TestStartup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
: base(configuration, hostingEnvironment)
{
this.clockServiceMock = new Mock<IClockService>(MockBehavior.Strict);
}
public override void ConfigureServices(IServiceCollection services)
{
services
.AddSingleton(this.clockServiceMock);
base.ConfigureServices(services);
services
.AddSingleton(this.clockServiceMock.Object);
}
}
CustomWebApplicationFactory
In your test project, write a custom WebApplicationFactory that configures the HttpClient and resolves the mocks from the TestStartup, then exposes them as properties, ready for our integration test to consume them. Note that I'm also changing the environment to Testing and telling it to use the TestStartup class for startup.
Note also that I've implemented IDisposable's `Dispose method to verify all of my strict mocks. This means I don't need to verify any mocks manually myself. Verification of all mock setups happens automatically when xUnit is disposing the test class.
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint>
where TEntryPoint : class
{
public CustomWebApplicationFactory()
{
this.ClientOptions.AllowAutoRedirect = false;
this.ClientOptions.BaseAddress = new Uri("https://localhost");
}
public ApplicationOptions ApplicationOptions { get; private set; }
public Mock<IClockService> ClockServiceMock { get; private set; }
public void VerifyAllMocks() => Mock.VerifyAll(this.ClockServiceMock);
protected override void ConfigureClient(HttpClient client)
{
using (var serviceScope = this.Services.CreateScope())
{
var serviceProvider = serviceScope.ServiceProvider;
this.ApplicationOptions = serviceProvider.GetRequiredService<IOptions<ApplicationOptions>>().Value;
this.ClockServiceMock = serviceProvider.GetRequiredService<Mock<IClockService>>();
}
base.ConfigureClient(client);
}
protected override void ConfigureWebHost(IWebHostBuilder builder) =>
builder
.UseEnvironment("Testing")
.UseStartup<TestStartup>();
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.VerifyAllMocks();
}
base.Dispose(disposing);
}
}
Integration Tests
I'm using xUnit to write my tests. Note that the generic type passed to CustomWebApplicationFactory is Startup and not TestStartup. This generic type is used to find the location of your application project on disk and not to start the application.
I setup a mock in my test and I've implemented IDisposable to verify all mocks for all my tests at the end but you can do this step in the test method itself if you like.
Note also, that I'm not using xUnit's IClassFixture to only boot up the application once as the ASP.NET Core documentation tells you to do. If I did so, I'd have to reset the mocks between each test and also you would only be able to run the integration tests serially one at a time. With the method below, each test is fully isolated and they can be run in parallel. This uses up more CPU and each test takes longer to execute but I think it's worth it.
public class FooControllerTest : CustomWebApplicationFactory<Startup>
{
private readonly HttpClient client;
private readonly Mock<IClockService> clockServiceMock;
public FooControllerTest()
{
this.client = this.CreateClient();
this.clockServiceMock = this.ClockServiceMock;
}
[Fact]
public async Task GetFoo_Default_Returns200OK()
{
this.clockServiceMock.Setup(x => x.UtcNow).ReturnsAsync(new DateTimeOffset(2000, 1, 1));
var response = await this.client.GetAsync("/foo");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
xunit.runner.json
I'm using xUnit. We need to turn off shadown copying, so any separate files like appsettings.json are placed in the right place beside the application DLL file. This ensures that our application running in an integration test can still read the appsettings.json file.
{
"shadowCopy": false
}
appsettings.Testing.json
Should you have configuration that you want to change just for your integration tests, you can add a appsettings.Testing.json file into your application. This configuration file will only be read in our integration tests because we set the environment name to 'Testing'.
The best way to handle this is to factor out parts of your Startup that will need to be substituted during test. For example, instead of calling services.AddDbContext<MyContext>(...); directly in ConfigureServices, create a virtual private method like:
protected virtual void ConfigureDatabase(IServiceCollection services)
{
services.AddDbContext<MyContext>(...);
}
Then, in your test project, create a class like TestStartup which derives from your SUT's Startup class. Then, you can override these virtual methods to sub in your test services, mocks, etc.
Finally, just do something like:
builder
.UseEnvironment("Testing")
.UseStartup<TestStartup>();
You should create a fake startup:
public class FakeStartup : Startup
{
public FakeStartup(IConfiguration configuration)
: base(configuration)
{
}
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
// Your fake go here
//services.AddScoped<IService, FakeService>();
}
}
Then use it with IClassFixture<CustomWebApplicationFactory<FakeStartup>>.
Make sure to make your original ConfigureServices method virtual.
I am writing a JUnit test case for the controller in my micronaut application. The controller has a GET endpoint which invokes a method in my service class. I am getting a NullPointerException so I am assuming that my service class might not be properly mocked however, I am not sure. I am using #Mock (Mockito) for the service.
Am I using the correct annotation to mock the service layer? I have tried to search on google but it hasn't given me much to look into. Thanks.
#MicronautTest
public class FPlanControllerTest {
private static final String url = "dummy_url";
#Inject
FPlanService fplanService;
#Inject
#Client("/")
RxHttpClient client;
#Test
public void testGetLayout() {
FPlanUrl expectedFPlanUrl = new FPlanUrl(url);
when(fplanService.getLayoutUrl(Mockito.anyString(), Mockito.anyString()))
.thenReturn(expectedFPlanUrl);
FPlanUrl actualFPlanUrl = client.toBlocking()
.retrieve(HttpRequest.GET("/layout/1000545").header("layoutId", "7"), FPlanUrl.class);
assertEquals(expectedFPlanUrl , actualFPlanUrl);
}
#MockBean(FPlanService.class)
FPlanService fplanService() {
return mock(FPlanService.class);
}
}
I received the below error.
java.lang.NullPointerException at com.apartment.controller.FPlanControllerTest.testGetLayout(FPlanControllerTest.java:44)
Use #MockBean (io.micronaut.test.annotation.MockBean).
Docs - https://micronaut-projects.github.io/micronaut-test/latest/guide/#junit5
Simply try to mock as below :-
#MockBean(MyService.class)
MyService myService() {
return mock(MyService.class);
}
Now the service can be injected as:-
#Inject
private MyService myService;
Use inside your test method as:-
#Test
public void myServiceTest() {
when(myService.foo(any())).thenReturn(any());
MutableHttpResponse<FooResponse> response = controller.bar(new
MyRequest());
Assertions.assertNotNull(response);
}
I figured out what went wrong. This was giving a NullPointerException because the HTTP response was expecting a String and not the FPlanUrl object. The correct code is as below:
#Test
public void testGetLayout() {
FPlanUrl expectedFPlanUrl = new FPlanUrl("http://dummyurl.com");
when(fplanService.getLayoutUrl(Mockito.anyString(), Mockito.anyString()))
.thenReturn(expectedFPlanUrl);
Assertions.assertEquals("{\"url\":\"http://dummyurl.com\"}", client.toBlocking().retrieve(HttpRequest.GET("/layout/123").header("layoutId", "7"), String.class);
verify(fplanService).getLayoutUrl("123","7");
}
I have a TypeFilterAtrribute which instantiates and ActionFilter. The ActionFilter needs two services injected.
public class ValidateUserAttribute : TypeFilterAttribute
{
public ValidateUserAttribute() : base(typeof(AuthenticationFilter))
{
}
private class AuthenticationFilter : ActionFilterAttribute
{
private readonly IActiveDirectoryService ActiveDirectoryService;
private readonly MessageService MessageSerivce;
public AuthenticationFilter(IActiveDirectoryService activeDirectoryService, MessageService messageSerivce)
{
ActiveDirectoryService = activeDirectoryService;
MessageSerivce = messageSerivce;
}
I have it working with the default IOC container of dot net core 2 but I could not use Funq container to do it.
I am reading from appsettings.json (I think I read on the docs ServiceStack does not support this) and registering
var config = Configuration.GetSection("LdapAuth");
services.Configure<LdapAuthenticationOptions>(Configuration.GetSection("LdapAuth"));
services.AddActiveDirectoryService(options =>
Configuration.GetSection("LdapAuth"));//uses collection.AddTransient<IActiveDirectoryService, ActiveDirectoryService>()
services.AddMessageService(); //same as above
I can't think of a way to inject into the filter a parameterized service.
So this does not work at all because I don't have a default constructor.
public class AuthenticationFilter : ActionFilterAttribute
{
public IActiveDirectoryService ActiveDirectoryService {get; set;};
But this below at least should have worked. I'm not using an interface here though.
public class AuthenticationFilter : ActionFilterAttribute
{
public MessageService MessageService{get; set}; //notice not using interface here although this as default constructor.
Where this gets really ugly is then I have controllers that inherit ServiceStackController and I inject services using ResolveService from the Funq container.
public class MessageController : ServiceStackController
{
...
var messageService = ResolveService<MessageService>()
I re-register them.. like below.
public override void Configure(Funq.Container container)
{
SqlServerDialect.Provider.RegisterConverter<TimeSpan>(new ServiceStack.OrmLite.SqlServer.Converters.SqlServerTimeConverter
{
Precision = 7
});
var connectionString = GetConnectionString(AppSettings);
container.Register<IDbConnectionFactory>(
new OrmLiteConnectionFactory(connectionString, new SqlServerOrmLiteDialectProvider()));
LdapAuthenticationOptions options = GetLdapAuthenticationOptions(AppSettings); //notice now I have to read from AppSetting.. which is appsettings.txt file.
container.Register(c => new ActiveDirectoryService(options));
container.Register(c => new MessageService());
}
I created a project to test the dependency injection offered by Google Guice in my Jax-rs resources, using Resteasy.
My intentions are:
Use multiple #ApplicationPath for the versions of my API. In each class annotated with #ApplicationPath I load a set of classes for the specific version.
Each resource have a #Inject (from Google Guice) in his constructor to inject some services.
I created two classes annotated with #ApplicationPath: ApplicationV1RS and ApplicationV2RS. In both I added the same resources classes (UserResource and HelloResource), only for my test.
My Module is configured like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
When I call http://localhost:9095/v1/hello/world or http://localhost:9095/v2/hello/world, I receive the same error:
java.lang.RuntimeException: RESTEASY003190: Could not find constructor
for class: org.jboss.resteasy.examples.guice.hello.HelloResource
Well, as I expected, this not works. The Google Guice is not "smart" to instantiate the resource classes using the construtor for me.
But I can't find a way to work. To be really honest, I'm really confuse about how the Google Guice, Jetty and Resteasy play with each other in this scenario.
If I abandon the idea of use #ApplicationPath, my resources work with Google Guice configuring my HelloModule like this:
public class HelloModule implements Module
{
public void configure(final Binder binder)
{
binder.bind(HelloResource.class);
binder.bind(IGreeterService.class).to(GreeterService.class);
binder.bind(UserResource.class);
binder.bind(IUserService.class).to(UserService.class);
}
}
But in this case, I'm passing the control to register my resources (HelloResource and UserResource) to Guice. It's not flexible for me, I can't setup my multiple #ApplicationPath.
So, what I'm missing or not understanding?
I created a project with the problemetic code. Is very easy to setup and test: https://github.com/dherik/resteasy-guice-hello/tree/so-question/README.md
Thanks!
When you have getClasses method in your Application then it tries to create instance for all the registered resources using the default constructor which is missing in our Resources class. One way is to create a default constructor and Inject the dependencies through setter Injection.
And then instead of overriding getClasses in ApplicationV1RS and ApplicationV2RS you override getSingletons. Since Resources can be Singleton.
Below are the changes that I made to make it work the way you want.
ApplicationV1RS.java
#ApplicationPath("v1")
public class ApplicationV1RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV1RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
ApplicationV2RS.java
#ApplicationPath("v2")
public class ApplicationV2RS extends Application {
private Set<Object> singletons = new HashSet<Object>();
public ApplicationV2RS(#Context ServletContext servletContext) {
}
#Override
public Set<Object> getSingletons() {
Injector injector = Guice.createInjector(new HelloModule());
HelloResource helloResource = injector.getInstance(HelloResource.class);
UserResource userResource = injector.getInstance(UserResource.class);
singletons.add(helloResource);
singletons.add(userResource);
return singletons;
}
}
HelloResource.java
#Path("hello")
public class HelloResource {
#Inject
private IGreeterService greeter;
public HelloResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return greeter.greet(name);
}
}
UserResource.java
#Path("user")
public class UserResource {
#Inject
private IUserService userService;
public UserResource() {
}
#GET
#Path("{name}")
public String hello(#PathParam("name") final String name) {
return userService.getUser(name);
}
}
Add #Singleton to your Service Classes.
Hope it helps.
I have also pushed the code to forked repo. check it out