Skip to content

Commit

Permalink
Support testng
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
  • Loading branch information
jbescos committed Apr 23, 2024
1 parent bcc23b8 commit 1153bc3
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 2 deletions.
5 changes: 5 additions & 0 deletions microprofile/testing/testng/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
<artifactId>helidon-microprofile-cdi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>jersey-weld2-se</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -44,13 +45,17 @@
import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import jakarta.enterprise.inject.spi.AfterBeanDiscovery;
import jakarta.enterprise.inject.spi.AnnotatedParameter;
import jakarta.enterprise.inject.spi.AnnotatedType;
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.BeforeBeanDiscovery;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.enterprise.inject.spi.Extension;
import jakarta.enterprise.inject.spi.InjectionTarget;
import jakarta.enterprise.inject.spi.InjectionTargetFactory;
import jakarta.enterprise.inject.spi.ProcessAnnotatedType;
import jakarta.enterprise.inject.spi.WithAnnotations;
import jakarta.enterprise.inject.spi.configurator.AnnotatedTypeConfigurator;
import jakarta.enterprise.util.AnnotationLiteral;
import jakarta.inject.Inject;
Expand All @@ -67,6 +72,8 @@
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.annotations.Test;
import org.mockito.MockSettings;
import org.mockito.Mockito;

/**
* TestNG extension to support Helidon CDI container in tests.
Expand Down Expand Up @@ -301,6 +308,11 @@ private void validatePerTest() {
+ " injection into fields or constructor is not supported, as each"
+ " test method uses a different CDI container. Field " + field
+ " is annotated with @Inject");
} else if (field.getAnnotation(MockBean.class) != null) {
throw new RuntimeException("When a class is annotated with @HelidonTest(resetPerTest=true),"
+ " injection into fields or constructor is not supported, as each"
+ " test method uses a different CDI container. Field " + field
+ " is annotated with @MockBean");
}
}

Expand All @@ -311,6 +323,11 @@ private void validatePerTest() {
+ " injection into fields or constructor is not supported, as each"
+ " test method uses a different CDI container. Field " + field
+ " is annotated with @Inject");
} else if (field.getAnnotation(MockBean.class) != null) {
throw new RuntimeException("When a class is annotated with @HelidonTest(resetPerTest=true),"
+ " injection into fields or constructor is not supported, as each"
+ " test method uses a different CDI container. Field " + field
+ " is annotated with @MockBean");
}
}
}
Expand Down Expand Up @@ -419,14 +436,15 @@ private <T extends Annotation> T[] getAnnotations(Class<?> testClass, Class<T> a
private static class AddBeansExtension implements Extension {
private final Class<?> testClass;
private final List<AddBean> addBeans;
private final Set<Class<?>> mocks = new HashSet<>();

private AddBeansExtension(Class<?> testClass, List<AddBean> addBeans) {
this.testClass = testClass;
this.addBeans = addBeans;
}

@SuppressWarnings("unchecked")
void registerOtherBeans(@Observes AfterBeanDiscovery event) {
void registerOtherBeans(@Observes AfterBeanDiscovery event, BeanManager beanManager) {
Client client = ClientBuilder.newClient();

event.addBean()
Expand All @@ -445,6 +463,26 @@ void registerOtherBeans(@Observes AfterBeanDiscovery event) {
return client.target("http://localhost:7001");
}
});

// Register all mocks
mocks.forEach(type -> {
event.addBean()
.addType(type)
.scope(ApplicationScoped.class)
.alternative(true)
.createWith(inst -> {
Set<Bean<?>> beans = beanManager.getBeans(MockSettings.class);
if (!beans.isEmpty()) {
Bean<?> bean = beans.iterator().next();
MockSettings mockSettings = (MockSettings) beanManager.getReference(bean, MockSettings.class,
beanManager.createCreationalContext(null));
return Mockito.mock(type, mockSettings);
} else {
return Mockito.mock(type);
}
})
.priority(0);
});
}

void registerAddedBeans(@Observes BeforeBeanDiscovery event) {
Expand All @@ -471,6 +509,20 @@ void registerAddedBeans(@Observes BeforeBeanDiscovery event) {
}
}

void processMockBean(@Observes @WithAnnotations(MockBean.class) ProcessAnnotatedType<?> obj) throws Exception {
var configurator = obj.configureAnnotatedType();
configurator.fields().forEach(field -> {
MockBean mockBean = field.getAnnotated().getAnnotation(MockBean.class);
if (mockBean != null) {
Field f = field.getAnnotated().getJavaMember();
// Adds @Inject to be more user friendly
field.add(Literal.INSTANCE);
Class<?> fieldType = f.getType();
mocks.add(fieldType);
}
});
}

private boolean hasBda(Class<?> value) {
// does it have bean defining annotation?
for (Class<? extends Annotation> aClass : BEAN_DEFINING.keySet()) {
Expand Down Expand Up @@ -617,4 +669,15 @@ public Class<? extends Extension> value() {
}
}

/**
* Supports inline instantiation of the {@link Inject} annotation.
*/
private static final class Literal extends AnnotationLiteral<Inject> implements Inject {

private static final Literal INSTANCE = new Literal();

@Serial
private static final long serialVersionUID = 1L;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.microprofile.testing.testng;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* A field annotated with @MockBean will be mocked by Mockito
* and injected in every place it is referenced.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface MockBean {

}
3 changes: 2 additions & 1 deletion microprofile/testing/testng/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,6 +29,7 @@
requires jakarta.ws.rs;
requires microprofile.config.api;
requires org.testng;
requires org.mockito;

requires static io.helidon.microprofile.server;
requires static jersey.cdi1x;
Expand Down
5 changes: 5 additions & 0 deletions microprofile/tests/testing/testng/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,10 @@
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.microprofile.tests.testing.testng;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.client.WebTarget;

import io.helidon.microprofile.testing.testng.AddBean;
import io.helidon.microprofile.testing.testng.HelidonTest;
import io.helidon.microprofile.testing.testng.MockBean;

import org.mockito.Answers;
import org.mockito.MockSettings;
import org.mockito.Mockito;
import org.testng.annotations.Test;

@HelidonTest
@AddBean(TestMockBeanField.Resource.class)
@AddBean(TestMockBeanField.Service.class)
class TestMockBeanField {

@Inject
@MockBean
private Service service;
@Inject
private WebTarget target;

@Test
void injectionTest() {
String response = target.path("/test").request().get(String.class);
// Defaults to specified in @Produces
assertThat(response, is("Not Mocked"));
Mockito.when(service.test()).thenReturn("Mocked");
response = target.path("/test").request().get(String.class);
assertThat(response, is("Mocked"));
}

@Produces
MockSettings mockSettings() {
return Mockito.withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS);
}

@Path("/test")
public static class Resource {

@Inject
private Service service;

@GET
public String test() {
return service.test();
}
}

static class Service {

String test() {
return "Not Mocked";
}

}
}

0 comments on commit 1153bc3

Please sign in to comment.