Say I have a play Global file...
public class Global extends GlobalSettings {
private ApplicationContext ctx;
#Override
public void onStart(Application app) {
ctx = new ClassPathXmlApplicationContext("context/components.xml");
}
#Override
public <A> A getControllerInstance(Class<A> clazz) {
return ctx.getBean(clazz);
}
}
And I have a test like this...
#Test
public void itShouldFailOnMissingFields() throws Exception {
running(fakeApplication(), () -> {
Map<String, String> body = new HashMap<String, String>();
body.put("email", "jason#goodwin.com");
body.put("password", "ro");
FakeRequest request = new FakeRequest(POST, "/v1/profile")
.withFormUrlEncodedBody(body);
Result result = route(request);
assertEquals(status(result), 400);
}
);
}
How can I get test dependencies injected into a controller? Is there some other way I should be testing this?
There was an answer on google groups
https://groups.google.com/forum/#!topic/play-framework/MpjMQhEC2C4
Related
I have a small vertx application with an AppLauncher class that extend of VertxCommandLauncher and I set a appConfig.json with the typical config parameters :
public class AppLauncher extends VertxCommandLauncher implements VertxLifecycleHooks {
public static void main(String[] args) {
new AppLauncher().dispatch(args);
}
#Override
public void afterConfigParsed(JsonObject config) {
AppConfig.INSTANCE.setConfig(config);
}
To run my application in my IDE I put in edit configuration my main class (Applauncher.java) and the arguments :
run io.vertx.covid.verticle.MainVerticle -conf../vertx-application/src/main/resources/appConfig.json
This is my test class:
#BeforeAll
static void deployVerticles(Vertx vertx, VertxTestContext testContext) {
vertx.deployVerticle(BaseVerticle.class.getName(),testContext
.succeeding(id->testContext.completeNow()));
}
This is my BaseVerticle class that all my verticles extends from:
public abstract class BaseVerticle extends AbstractVerticle {
public static String CONTEXT_PATH = AppConfig.INSTANCE.getConfig().getString(Constants.CONTEXT_PATH);
}
And this is my AppConfig class :
public enum AppConfig {
INSTANCE;
private JsonObject config;
public JsonObject getConfig() {
return config;
}
public void setConfig(JsonObject config) {
this.config = config;
}
}
Everything works, but if I would like to test it in a separete way then I deploy my verticles but I have a Nullpointer in the CONTEXT_PATH (BaseVerticle class) because the config (suppose to be taken from appConfig.json) is null.
I haven't found a way to pass the arguments with my appConfig.json or should I call to the main method passing the arguments?
I like to do something that is similar to profiles in my vertx application.
If you set an environment variable with the key vertx-config-path before the vertx instance is initialized, you can control where vertx's config retriever (you might need to add vert-config to your gradle/maven dependencies) gets the configuration from.
In your launcher, you can do something like the following, which will give you the ability to add profile based config files to your resources folder conf/config-%s.json where %s is the profile name:
public class CustomLauncher extends Launcher {
public static final String ACTIVE_PROFILE_PROPERTY = "APP_ACTIVE_PROFILE";
private static final CLI cli = CLI.create("main")
.addOption(new Option()
.setShortName("p")
.setLongName("profile")
);
public static void main(String[] args) {
initDefaults(Arrays.asList(args));
new CustomLauncher().dispatch(args);
}
public static void executeCommand(String cmd, String... args) {
initDefaults(Arrays.asList(args));
new CustomLauncher().execute(cmd, args);
}
public static void initDefaults(List<String> args) {
System.setProperty(LoggerFactory.LOGGER_DELEGATE_FACTORY_CLASS_NAME, SLF4JLogDelegateFactory.class.getName());
CommandLine parse = cli.parse(args);
String profile = parse.getOptionValue("p");
if (profile != null && !profile.isEmpty()) {
System.setProperty(ACTIVE_PROFILE_PROPERTY, profile);
System.setProperty("vertx-config-path", String.format("conf/config-%s.json", profile));
}
}
}
Then in your test, instead of relaying on vertx test extension to inject vertx for you, you can initialize it by yourself and control the profile (aka which config file to load) like the following:
private static Vertx vertx;
#BeforeAll
public static void deployVerticles(VertxTestContext testContext) {
CustomLauncher.initDefaults(Arrays.asList("--profile", "test"))
vertx = Vertx.vertx();
ConfigRetriever.create(vertx).getConfig(asyncResult -> {
if (asyncResult.succeeded()) {
JsonObject config = asyncResult.result();
DeploymentOptions deploymentOptions = new DeploymentOptions()
.setConfig(config);
vertx.deployVerticle(BaseVerticle.class.getName(), deploymentOptions);
} else {
// handle failure
}
});
}
Then when you run your application, instead of providing -conf, you can use -p or --profile
I also highly recommend to get familiar with vertx-config as you can also get env variables, k8s config maps, and much more.
EDIT: I also highly recommend to move to Kotlin if possible, makes the async-code much easier to handle in an imperative way (with Coroutines). It's very hard to deal with libraries like Vert.x in Java compared to languages like Kotlin.
I solved my problem creating a verticle with the config stuffs (vertx-config documentation), here is my verticle config class:
public class ConfigVerticle extends AbstractVerticle {
protected static Logger logger = LoggerFactory.getLogger(ConfigVerticle.class);
public static JsonObject config;
#Override
public void start() throws Exception {
ConfigStoreOptions fileStore = new ConfigStoreOptions()
.setType("file")
.setOptional(true)
.setConfig(new JsonObject().put("path", "conf/appConfig.json"));
ConfigStoreOptions sysPropsStore = new ConfigStoreOptions().setType("sys");
ConfigRetrieverOptions options = new ConfigRetrieverOptions().addStore(fileStore).addStore(sysPropsStore);
ConfigRetriever retriever = ConfigRetriever.create(vertx, options);
retriever.getConfig(ar -> {
if (ar.failed()) {
logger.info("Failed to retrieve config from appConfig.json");
} else {
config = ar.result();
vertx.deployVerticle(MainVerticle.class.getName(), new DeploymentOptions().setConfig(config));
}
});
}
}
And my MainVerticle.class I pass the new configuration like this:
public class MainVerticle extends AbstractVerticle {
#Override
public void start(){
vertx.deployVerticle(BackendVerticle.class.getName(), new DeploymentOptions().setConfig(config()));
}
}
Then, my simpleTests :
#ExtendWith(VertxExtension.class)
public class BaseCovidTest {
protected WebClient webClient;
#BeforeEach
void initWebClient(Vertx vertx){
webClient = WebClient.create(vertx);
}
#BeforeAll
static void deployVerticles(Vertx vertx, VertxTestContext vertxTestContext) {
vertx.deployVerticle(ConfigVerticle.class.getName() ,vertxTestContext
.succeeding(id-> {
try {
vertxTestContext.awaitCompletion(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
vertxTestContext.completeNow();
}));
}
}
And everything works, thanks #Tom that inspired me to fix it!
I am trying to create 2 Azure Functions with Spring Cloud but I can't make it work.
#Configuration
public class FirstFunction extends AzureSpringBootRequestHandler<Optional<Void>, String>
{
#FunctionName("firstFunction")
public void run(
#HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context)
{
handleRequest(Optional.empty(), context);
}
#Bean
#Lazy
Function<Optional<Void>, String> firstFunction()
{
return context ->
{
// do firstFunction stuff;
};
}
}
#Configuration
public class SecondFunction extends AzureSpringBootRequestHandler<Optional<Void>, String>
{
#FunctionName("secondFunction")
public void run(
#HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context)
{
handleRequest(Optional.empty(), context);
}
#Bean
#Lazy
Function<Optional<Void>, String> secondFunction()
{
return context ->
{
// do secondFunction stuff;
};
}
}
#SpringBootApplication
public class Application
{
public static void main(final String[] args)
{
SpringApplication.run(Application.class, args);
}
}
Using the above code with the dependency on spring-cloud-function-dependencies 2.0.1.RELEASE, it always hits the firstFunction Bean when calling both the firstFunction and secondFunction endpoints.
After doing some googling, I found this SO answer that suggests to move to 2.1.
However when I tried changing to 2.1.1.RELEASE, I am getting an exception where it is failing to find a main class:
System.Private.CoreLib: Exception while executing function: Functions.extractContent. System.Private.CoreLib: Result: Failure
Exception: IllegalArgumentException: Failed to locate main class
Stack: java.lang.IllegalStateException: Failed to discover main class. An attempt was made to discover main class as 'MAIN_CLASS' environment variable, system property as well as entry
in META-INF/MANIFEST.MF (in that order).
Need some help on what I am doing wrong.
I tested at my side, and everything was OK.
You may get my demo at: https://github.com/AI-EVO/azuresptingfunction.git . The project is based on the official demo: https://github.com/Azure-Samples/hello-spring-function-azure
My changes:
HelloFunction.java
#SpringBootApplication
public class HelloFunction {
public static void main(String[] args) throws Exception {
SpringApplication.run(HelloFunction.class, args);
}
#Bean("hello")
public Function<User, Greeting> hello() {
return user -> new Greeting("Hello! Welcome, " + user.getName());
}
#Bean("hi")
public Function<User, Greeting> hi() {
return user -> new Greeting("Hi! Welcome, " + user.getName());
}
}
Modify HelloHandler.java
public class HelloHandler extends AzureSpringBootRequestHandler<User, Greeting> {
#FunctionName("hello")
public Greeting execute(
#HttpTrigger(name = "request", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<User>> request,
ExecutionContext context) {
context.getLogger().info("Greeting user name: " + request.getBody().get().getName());
return handleRequest(request.getBody().get(), context);
}
}
Add HiHandler.java
public class HiHandler extends AzureSpringBootRequestHandler<User, Greeting> {
#FunctionName("hi")
public Greeting execute(#HttpTrigger(name = "request", methods = { HttpMethod.GET,
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<User>> request,
ExecutionContext context) {
context.getLogger().info("Greeting user name: " + request.getBody().get().getName());
return handleRequest(request.getBody().get(), context);
}
}
Run functions:
mvn azure-functions:run
Test with postman
From function hello:
From function hi:
I have a tricky situation. I am using MVP architecture for android but thats not important. I have a class called DoStandardLoginUsecase that basically just connects to a server with login info and gets a access token. i am trying to test it. But the problem is the context that i am passing in to it so i can initialize dagger.
public class DoStandardLoginUsecase extends BaseUseCase {
#Inject
UserDataRepository mUserDataRepo;
private StandardLoginInfo loginInfo;
public DoStandardLoginUsecase(Context context) {
/* SEE HERE I AM USING A APPLICATION CONTEXT THAT I PASS TO DAGGER
*/
((MyApplication)context).getPresenterComponent().inject(this);
}
#Override
public Observable<Login> buildUseCaseObservable() {
return mUserDataRepo.doStandardLogin(loginInfo);
}
public void setLoginInfo(StandardLoginInfo loginInfo) {
this.loginInfo = loginInfo;
}
}
and here is the test i have so far:
public class DoStandardLoginUsecaseTest {
DoStandardLoginUsecase standardLoginUsecase;
StandardLoginInfo fakeLoginInfo;
TestObserver<Login> subscriber;
MockContext context;
#Before
public void setUp() throws Exception {
//now when i create the object since its a mock context it will fail when it tries to call real things as these are stubs. So how do i test this object. how do i create an instance of this object ? I am willing to use [daggerMock][1] if that helps also.
standardLoginUsecase = New DoStandardLoginUsecase(context);
fakeLoginInfo = new StandardLoginInfo("fred#hotmail.com","Asdfgh4534");
subscriber = TestObserver.create();
}
#Test
public void buildUseCaseObservable(){
standardLoginUsecase.seLoginInfo(fakeLoginInfo);
standardLoginUsecase.buildUseCaseObservable().subscribe(subscriber);
subscriber.assertNoErrors();
subscriber.assertSubscribed();
subscriber.assertComplete();
}
}
I would do the test like this:
public class DoStandardLoginUsecaseTest {
private DoStandardLoginUsecase target;
private MyApplication contextMock;
#Before
public void beforeEach() {
contextMock = Mockito.mock(MyApplication.class);
// Note that you need to mock the getPresenterComponent
// but I don't know what it returns.
target = new DoStandardLoginUsecase(contextMock);
}
#Test
public void buildUseCaseObservable() {
UserDataRepository userDataMock = Mockito.mock(UserDataRepository.class);
StandardLoginInfo loginInfoMock = Mockito.mock(StandardLoginInfo.class);
target.mUserDataRepo = userDataMock;
target.setLoginInfo(loginInfoMock);
Observable<Login> expected = // create your expected test data however you like...
Mockito.when(userDataMock.doStandardLogin(loginInfoMock)).thenReturn(expected);
Observable<Login> actual = target.buildUseCaseObservable();
Assert.areSame(actual, expected);
}
}
I followed guide here, and I was successfully able to configure a producer on my bean endpoint like this:
#Produce( uri = "activemq:foo" )
private MyListener myListener;
MyListener is:
public interface MyListener {
#InOnly
public void send( String message );
}
and my bean:
public class MyBeanEndpoint {
#Produce( uri = "activemq:foo" )
private MyListener myListener;
#Handler
public void doSomething( final Object body ) {
...
}
public void setMyListener( final MyListener myListener ) {
this.myListener = myListener;
}
Now, how can I test this?
I mean: my test extends CamelTestSupport and I configured my routes with
#Override
public RouteBuilder createRouteBuilder() {
return new RouteBuilder() { ... }
That is: I've reproduced camel context, but I've NO spring context configured and I want (if possible) to avoid instantiating it.
How can I mock producer or make Camel instantiate and inject this bean into my bean endpoint?
What is the best way to test such situation using Apache Camel features like CamelTestSupport and similar utilities?
My reference test is:
public class Test extends CamelTestSupport {
private static BeanEndpoint beanEndpoint
#BeforeClass
public static void init() {
beanEndpoint.setActivemqMyListener( ??? );
}
#Override
public CamelContext createCamelContext() {
context = new DefaultCamelContext();
context.addComponent( "activemq", new SedaComponent() );
return context;
}
#Override
public RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from( "activemq:foo" )
.to( "mock:out" );
}
};
}
#Test
public void testFooQueue() throws Exception {}
Let Camel create your bean, then the various dependency injection and whatnot is configured for you.
private static BeanEndpoint beanEndpoint
...
beanEndpoint = camelContext.getInjector().newInstance(BeanEndpoint.class);
I have a little problem. I think this is typical question. However, I can't find good example. My application is using Jersey. And I want to test controller by client as test. Controller has private field - StudentService. When I debug test I see, that field is null. This leads to error. And I need to inject this field. I tried this:
My Controller
#Path("/student")
#Component
public class StudentResourse {
#Autowired
private StrudentService service; // this field Spring does not set
#Path("/getStudent/{id}")
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Student getStudent(#PathParam("id") long id) {
return service.get(id);
}
}
My JUnit test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:config.xml")
#TestExecutionListeners({ DbUnitTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class StudentResourseTest extends JerseyTest {
private static final String PACKAGE_NAME = "com.example.servlet";
private static final String FILE_DATASET = "/data.xml";
#Autowired
private StudentService service; // this field is setted by Spring, but I do not need this field for test
public StudentResourseTest() {
super(new WebAppDescriptor.Builder(PACKAGE_NAME).build());
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new HTTPContainerFactory();
}
#Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("restful.server.resource")
.contextParam("contextConfigLocation",
"classpath:/config.xml").contextPath("/")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class).build();
}
#Test
#DatabaseSetup(FILE_DATASET)
public void test() throws UnsupportedEncodingException {
ClientResponse response = resource().path("student").path("getStudent")
.path("100500").accept(MediaType.APPLICATION_XML)
.get(ClientResponse.class);
Student student = (Student) response.getEntity(Student.class);
} }
I guees, that problem is in test class. Because, when I run my application not in test, I can directly request students and everything working fine. But when I test classes, internal field of Controller does not setted. How to fix this bug? Thanks for your answers.
This is in my config.xml
<context:component-scan base-package="com.example" />
<bean id="StudentResourse" class="com.example.servlet.StudentResourse">
<property name="service" ref="studentService" />
</bean>
<bean id="service" class="com.example.service.StudentServiceImpl" />
One issue may be that you're trying to configure your test application in constructor and in configure() method. Use one or another but not both because in this case your configure() method is not invoked and hence you may not be using SpringServlet and everything that is defined in this method.
Reference: https://github.com/jiunjiunma/spring-jersey-test and http://geek.riffpie.com/unit-testing-restful-jersey-services-glued-together-with-spring/
Idea is to get a hold of the application context inside jersey by using ApplicationContextAware interface. There after we can grab the exact bean already created by spring, in your case, StudentService. Below example shows a mocked version of the dependency, SampleService, used to test the resource layer apis.
Resource class delegating the processing to a service layer
#Component
#Path("/sample")
public class SampleResource {
#Autowired
private SampleService sampleService;
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path ("/{id}")
public Sample getSample(#PathParam("id") int id) {
Sample sample = sampleService.getSample(id);
if (sample == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return sample;
}
}
Service layer encapsulating business logic
#Service
public class SampleService {
private static final Map<Integer, Sample> samples = new HashMap<>();
static {
samples.put(1, new Sample(1, "sample1"));
samples.put(2, new Sample(2, "sample2"));
}
public Sample getSample(int id) {
return samples.get(id);
}
}
Unit test for the above resource
public class SampleResourceTest extends SpringContextAwareJerseyTest {
private SampleService mockSampleService;
// create mock object for our test
#Bean
static public SampleService sampleService() {
return Mockito.mock(SampleService.class);
}
/**
* Create our own resource here so only the test resource is loaded. If
* we use #ComponentScan, the whole package will be scanned and more
* resources may be loaded (which is usually NOT what we want in a test).
*/
#Bean
static public SampleResource sampleResource() {
return new SampleResource();
}
// get the mock objects from the internal servlet context, because
// the app context may get recreated for each test so we have to set
// it before each run
#Before
public void setupMocks() {
mockSampleService = getContext().getBean(SampleService.class);
}
#Test
public void testMock() {
Assert.assertNotNull(mockSampleService);
}
#Test
public void testGetSample() {
// see how the mock object hijack the sample service, now id 3 is valid
Sample sample3 = new Sample(3, "sample3");
Mockito.when(mockSampleService.getSample(3)).thenReturn(sample3);
expect().statusCode(200).get(SERVLET_PATH + "/sample/3");
String jsonStr = get(SERVLET_PATH + "/sample/3").asString();
Assert.assertNotNull(jsonStr);
}
}
SpringContextAwareJerseyTest
#Configuration
public class SpringContextAwareJerseyTest extends JerseyTest {
protected static String SERVLET_PATH = "/api";
final private static ThreadLocal<ApplicationContext> context =
new ThreadLocal<>();
protected String getResourceLocation() {
return "example.rest";
}
protected String getContextConfigLocation() {
return getClass().getName();
}
static private String getContextHolderConfigLocation() {
return SpringContextAwareJerseyTest.class.getName();
}
protected WebAppDescriptor configure() {
String contextConfigLocation = getContextConfigLocation() + " " +
getContextHolderConfigLocation();
Map<String, String> initParams = new HashMap<>();
initParams.put("com.sun.jersey.config.property.packages",
getResourceLocation());
initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "true");
return new WebAppDescriptor.Builder(initParams)
.servletClass(SpringServlet.class)
.contextParam(
"contextClass",
"org.springframework.web.context.support.AnnotationConfigWebApplicationContext")
.contextParam("contextConfigLocation", contextConfigLocation)
.servletPath(SERVLET_PATH) // if not specified, it set to root resource
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
protected final ApplicationContext getContext() {
return context.get();
}
#Bean
public static ContextHolder contextHolder() {
return new ContextHolder();
}
private static class ContextHolder implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context.set(applicationContext);
}
}
}
Using the above with jersey 1.8