I'm trying to test my network module. When I run this on simulator or device, handler is ok, but when I'm trying to do it from tests, handler = null and callback doesn't get called. How can I solve this problem?
public void performCall(Call callToPerform){
callToPerform.call.enqueue(new okhttp3.Callback() {
Handler handler = new Handler();
#Override
public void onFailure(okhttp3.Call call, IOException e) {
handler.post(() -> {
for (Callback callback : callToPerform.callbacks) {
callback.onFailure(callToPerform, e);
}
});
}
#Override
public void onResponse(okhttp3.Call call, final okhttp3.Response response){
handler.post(() -> {
for (Callback callback : callToPerform.callbacks) {
try {
callback.onResponse(callToPerform, new Response(response.body().bytes(), response.headers().toMultimap()));
} catch (IOException e) {
callback.onFailure(call, e);
}
}
});
}
});
}
My graddle app file contains this params.
testOptions {
unitTests.returnDefaultValues = true
}
Ok, after a few hours of research I've found solution and it's similar to this:
package com.dpmedeiros.androidtestsupportlibrary;
import android.os.Handler;
import android.os.Looper;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.*;
/**
* Utility methods that unit tests can use to do common android library mocking that might be needed.
*/
public class AndroidMockUtil {
private AndroidMockUtil() {}
/**
* Mocks main thread handler post() and postDelayed() for use in Android unit tests
*
* To use this:
* <ol>
* <li>Call this method in an {#literal #}Before method of your test.</li>
* <li>Place Looper.class in the {#literal #}PrepareForTest annotation before your test class.</li>
* <li>any class under test that needs to call {#code new Handler(Looper.getMainLooper())} should be placed
* in the {#literal #}PrepareForTest annotation as well.</li>
* </ol>
*
* #throws Exception
*/
public static void mockMainThreadHandler() throws Exception {
PowerMockito.mockStatic(Looper.class);
Looper mockMainThreadLooper = mock(Looper.class);
when(Looper.getMainLooper()).thenReturn(mockMainThreadLooper);
Handler mockMainThreadHandler = mock(Handler.class);
Answer<Boolean> handlerPostAnswer = new Answer<Boolean>() {
#Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
Runnable runnable = invocation.getArgumentAt(0, Runnable.class);
Long delay = 0L;
if (invocation.getArguments().length > 1) {
delay = invocation.getArgumentAt(1, Long.class);
}
if (runnable != null) {
mainThread.schedule(runnable, delay, TimeUnit.MILLISECONDS);
}
return true;
}
};
doAnswer(handlerPostAnswer).when(mockMainThreadHandler).post(any(Runnable.class));
doAnswer(handlerPostAnswer).when(mockMainThreadHandler).postDelayed(any(Runnable.class), anyLong());
PowerMockito.whenNew(Handler.class).withArguments(mockMainThreadLooper).thenReturn(mockMainThreadHandler);
}
private final static ScheduledExecutorService mainThread = Executors.newSingleThreadScheduledExecutor();
}
If you run this sample code on JUnit, this will not work because JUnit tests are running on a JVM, and Instrumented tests are running on a Simulator or Real Device
You can take a look at this link, it explains why :
Instrumented tests or Local tests
Related
If #Around only #Timed annotated method like this:
package ru.fabit.visor.config.aop;
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.lang.NonNullApi;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.scheduling.annotation.Scheduled;
import java.lang.reflect.Method;
import java.util.function.Function;
/**
* The type Targeted timed aspect.
*/
#Aspect
#NonNullApi
public class TargetedTimedAspect {
public static final String DEFAULT_METRIC_NAME = "method.timed";
public static final String EXCEPTION_TAG = "exception";
public static final String BINDING_TAG = "binding";
public static final String SCHEDULED_CRON_TAG = "cron";
private final MeterRegistry registry;
private final Function<ProceedingJoinPoint, Iterable<Tag>> tagsBasedOnJoinPoint;
public TargetedTimedAspect(MeterRegistry registry) {
this(registry, pjp ->
Tags.of("class", pjp.getStaticPart().getSignature().getDeclaringTypeName(),
"method", pjp.getStaticPart().getSignature().getName())
);
}
public TargetedTimedAspect(MeterRegistry registry, Function<ProceedingJoinPoint, Iterable<Tag>> tagsBasedOnJoinPoint) {
this.registry = registry;
this.tagsBasedOnJoinPoint = tagsBasedOnJoinPoint;
}
// enable TimedAspect only for #StreamListener and #Scheduled annotated methods or allowed methods pointcut
#Around("timedAnnotatedPointcut() )")
public Object timedMethod(ProceedingJoinPoint pjp) throws Throwable {
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
StreamListener streamListener = method.getAnnotation(StreamListener.class);
Scheduled scheduled = method.getAnnotation(Scheduled.class);
// timed can be on method or class
Timed timed = method.getAnnotation(Timed.class);
if (timed == null) {
method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
timed = method.getAnnotation(Timed.class);
}
final String metricName = timed.value().isEmpty() ? DEFAULT_METRIC_NAME : timed.value();
Timer.Sample sample = Timer.start(registry);
String exceptionClass = "none";
try {
return pjp.proceed();
} catch (Exception ex) {
exceptionClass = ex.getClass().getSimpleName();
throw ex;
} finally {
try {
Timer.Builder timerBuilder = Timer.builder(metricName)
.description(timed.description().isEmpty() ? null : timed.description())
.tags(timed.extraTags())
.tags(EXCEPTION_TAG, exceptionClass)
.tags(tagsBasedOnJoinPoint.apply(pjp))
.publishPercentileHistogram(timed.histogram())
.publishPercentiles(timed.percentiles().length == 0 ? null : timed.percentiles());
if (streamListener != null) {
timerBuilder.tags(
BINDING_TAG,
streamListener.value().isEmpty() ? streamListener.target() : streamListener.value()
);
} else if (scheduled != null) {
timerBuilder.tags(SCHEDULED_CRON_TAG, scheduled.cron());
}
sample.stop(timerBuilder.register(registry));
} catch (Exception e) {
// ignoring on purpose
}
}
}
#Pointcut(
"(#annotation(org.springframework.cloud.stream.annotation.StreamListener) ||" +
"#annotation(org.springframework.scheduling.annotation.Scheduled))"
)
public void asyncAnnotatedPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
#Pointcut("execution(public * ru.fabit.visor.service.impl.StorageClientImpl.*(..)) ||" +
"execution(public * ru.fabit.visor.service.s3storage.S3StorageClientImpl.*(..))")
public void allowedMethodPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
#Pointcut("#annotation(io.micrometer.core.annotation.Timed)")
public void timedAnnotatedPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
}
Then return: java.lang.IllegalArgumentException: Prometheus requires that all meters with the same name have the same set of tag keys. There is already an existing meter named 'web_photos_gotten_list_seconds' containing tag keys [class, exception, method]. The meter you are attempting to register has keys [exception, method, outcome, status, uri].
But, if add all #Timed method in Pointcut, all good work, i dont understand, why we need all annotated method add to Pointcut separately?
This work:
package ru.fabit.visor.config.aop;
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.lang.NonNullApi;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.scheduling.annotation.Scheduled;
import java.lang.reflect.Method;
import java.util.function.Function;
/**
* The type Targeted timed aspect.
*/
#Aspect
#NonNullApi
public class TargetedTimedAspect {
public static final String DEFAULT_METRIC_NAME = "method.timed";
public static final String EXCEPTION_TAG = "exception";
public static final String BINDING_TAG = "binding";
public static final String SCHEDULED_CRON_TAG = "cron";
private final MeterRegistry registry;
private final Function<ProceedingJoinPoint, Iterable<Tag>> tagsBasedOnJoinPoint;
public TargetedTimedAspect(MeterRegistry registry) {
this(registry, pjp ->
Tags.of("class", pjp.getStaticPart().getSignature().getDeclaringTypeName(),
"method", pjp.getStaticPart().getSignature().getName())
);
}
public TargetedTimedAspect(MeterRegistry registry, Function<ProceedingJoinPoint, Iterable<Tag>> tagsBasedOnJoinPoint) {
this.registry = registry;
this.tagsBasedOnJoinPoint = tagsBasedOnJoinPoint;
}
// enable TimedAspect only for #StreamListener and #Scheduled annotated methods or allowed methods pointcut
#Around("timedAnnotatedPointcut() && (asyncAnnotatedPointcut() || allowedMethodPointcut())")
public Object timedMethod(ProceedingJoinPoint pjp) throws Throwable {
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
StreamListener streamListener = method.getAnnotation(StreamListener.class);
Scheduled scheduled = method.getAnnotation(Scheduled.class);
// timed can be on method or class
Timed timed = method.getAnnotation(Timed.class);
if (timed == null) {
method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
timed = method.getAnnotation(Timed.class);
}
final String metricName = timed.value().isEmpty() ? DEFAULT_METRIC_NAME : timed.value();
Timer.Sample sample = Timer.start(registry);
String exceptionClass = "none";
try {
return pjp.proceed();
} catch (Exception ex) {
exceptionClass = ex.getClass().getSimpleName();
throw ex;
} finally {
try {
Timer.Builder timerBuilder = Timer.builder(metricName)
.description(timed.description().isEmpty() ? null : timed.description())
.tags(timed.extraTags())
.tags(EXCEPTION_TAG, exceptionClass)
.tags(tagsBasedOnJoinPoint.apply(pjp))
.publishPercentileHistogram(timed.histogram())
.publishPercentiles(timed.percentiles().length == 0 ? null : timed.percentiles());
if (streamListener != null) {
timerBuilder.tags(
BINDING_TAG,
streamListener.value().isEmpty() ? streamListener.target() : streamListener.value()
);
} else if (scheduled != null) {
timerBuilder.tags(SCHEDULED_CRON_TAG, scheduled.cron());
}
sample.stop(timerBuilder.register(registry));
} catch (Exception e) {
// ignoring on purpose
}
}
}
#Pointcut(
"(#annotation(org.springframework.cloud.stream.annotation.StreamListener) ||" +
"#annotation(org.springframework.scheduling.annotation.Scheduled))"
)
public void asyncAnnotatedPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
#Pointcut("execution(public * ru.fabit.visor.service.impl.StorageClientImpl.*(..)) ||" +
"execution(public * ru.fabit.visor.service.s3storage.S3StorageClientImpl.*(..))")
public void allowedMethodPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
#Pointcut("#annotation(io.micrometer.core.annotation.Timed)")
public void timedAnnotatedPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the advices.
}
}
pom.xml:
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
The problem youre discribing has nothing to do with pointcuts. There is one piece of code that generates a Timer with three tags (class, exception, method) and another one creating the timer with the exact same same with 5 tags (exception, method, outcome, status, uri) and the framework clearly says, that this is now allowed.
There are several possibilites to solve the issue:
simply use another name for the timer (if you need the other one)
find the other piece of code that generates that timer and deactivate it. Maybe you need to use the debugger and set an conditional breakpoint in MeterRegistry.register()` that registers the meters with the condition that the meter name matches.
PS: using the URI as a tag is not a good practice. The issue is that anyone access your service using different URIs (e.g. by just adding a random number) that will end up in a very high number of meters, which will finally kill your prometheus.
I'm using Cucumber, Selenium, Java, Maven and JUnit stack in my automation-test-project.
The goal is to take screenshots on fails and broken tests. I have found the solution for Java/Maven/JUnit stack:
#Rule
public TestWatcher screenshotOnFailure = new TestWatcher() {
#Override
protected void failed(Throwable e, Description description) {
makeScreenshotOnFailure();
}
#Attachment("Screenshot on failure")
public byte[] makeScreenshotOnFailure() {
return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
}
};
But, of course it does not work in case of using Cucumber, because it does not use any #Test methods.
So, I've decided to change #Rule to #ClassRule, to make it listen to any fails, so here it is:
#ClassRule
public static TestWatcher screenshotOnFailure = new TestWatcher() {
#Override
protected void failed(Throwable e, Description description) {
makeScreenshotOnFailure();
}
#Attachment("Screenshot on failure")
public byte[] makeScreenshotOnFailure() {
logger.debug("Taking screenshot");
return ((TakesScreenshot) Application.getInstance().getWebDriver()).getScreenshotAs(OutputType.BYTES);
}
};
And this solution didn't help me.
So, the question is: "How to attach screenshots on fail, when I use Java/Selenium/Cucumber/JUnit/Maven in my test project?"
The solution is just to add following code to your definition classes:
#After
public void embedScreenshot(Scenario scenario) {
if (scenario.isFailed()) {
try {
byte[] screenshot = ((TakesScreenshot) Application.getInstance().getWebDriver())
.getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
} catch (Exception e) {
e.printStackTrace();
}
}
}
In the GlobalGlue
public class GlobalGlue {
#Before
public void before(Scenario scenario) throws Exception {
CONTEXT.setScenario(scenario);
}
#After
public void after() {
WebDriverUtility.after(getDriver(), CONTEXT.getScenario());
}
}
Create another class WebDriverUtility and in that add method:
public static void after(WebDriver driver, Scenario scenario) {
getScreenshot(driver, scenario);
driver.close();
}
and
public static void getScreenshot(WebDriver driver, Scenario scenario) {
if (scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
log.info("Thread: " + Thread.currentThread().getId() + " :: "
+ "Screenshot taken and inserted in scenario report");
}
}
the main part is you need to embed the screen shot in scenario when scenario is failed:
final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
ExecutionContext.java
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import cucumber.api.Scenario;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openqa.selenium.WebDriver;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
/**
* Maintains one webdriver per scenario and one scenario per thread.
* Can be used for parallel execution.
* Assumes that scenarios within one feature are not parallel.
* Can be rewritten using <code>ThreadLocal</code>.
*
* #author dkhvatov
*/
public enum ExecutionContext {
CONTEXT;
private static final Logger log = LogManager.getLogger(ExecutionContext.class);
private final LoadingCache<Scenario, WebDriver> webDrivers =
CacheBuilder.newBuilder()
.build(CacheLoader.from(scenario ->
WebDriverUtility.createDriver()));
private final Map<String, Scenario> scenarios = new ConcurrentHashMap<>();
/**
* Lazily gets a webdriver for the current scenario.
*
* #return webdriver
*/
public WebDriver getDriver() {
try {
Scenario scenario = getScenario();
if (scenario == null) {
throw new IllegalStateException("Scenario is not set for context. " +
"Please verify your glue settings. Either use GlobalGlue, or set " +
"scenario manually: CONTEXT.setScenario(scenario)");
}
return webDrivers.get(scenario);
} catch (ExecutionException e) {
log.error("Unable to start webdriver", e);
throw new RuntimeException(e);
}
}
/**
* Gets scenario for a current thread.
*
* #return scenario
*/
public Scenario getScenario() {
return scenarios.get(Thread.currentThread().getName());
}
/**
* Sets current scenario. Overwrites current scenario in a map.
*
* #param scenario scenario
*/
public void setScenario(Scenario scenario) {
scenarios.put(Thread.currentThread().getName(), scenario);
}
}
This way you can attach screens to your allure report.
Also use the Latest allure version in your pom file
import com.google.common.io.Files;
import io.qameta.allure.Attachment;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import java.io.File;
import java.io.IOException;
public class ScreenshotUtils {
#Attachment(type = "image/png")
public static byte[] screenshot(WebDriver driver)/* throws IOException */ {
try {
File screen = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
return Files.toByteArray(screen);
} catch (IOException e) {
return null;
}
}
}
Pom File :
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-cucumber4-jvm</artifactId>
<version>${allure.version}</version>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit4</artifactId>
<version>${allure.version}</version>
<scope>test</scope>
</dependency>
public static void TakeScreenshot(string text)
{
byte[] content = ((ITakesScreenshot)driver).GetScreenshot().AsByteArray;
AllureLifecycle.Instance.AddAttachment(text, "image/png", content);
}
public static void Write(By by, string text)
{
try
{
driver.FindElement(by).SendKeys(text);
TakeScreenshot( "Write : " + text);
}
catch (Exception ex)
{
TakeScreenshot( "Write Failed : " + ex.ToString());
Assert.Fail();
}
}
I'm trying to unit test presenter in my Android app. Method I'm trying to test looks like this:
#Override
public boolean loadNextPage() {
if (!mIsLoading) {
mIsLoading = true;
if (mViewReference.get() != null) {
mViewReference.get().showProgress();
}
mService.search(mSearchQuery, ++mCurrentPage)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(itemsPage -> {
mIsLoading = false;
mTotalPages = itemsPage.getPagination().getTotalPages();
if (mViewReference.get() != null) {
mViewReference.get().showMovies(itemsPage.getItems());
}
},
error -> {
mIsLoading = false;
Log.d(LOG_TAG, error.toString());
});
}
return mTotalPages == 0 || mCurrentPage < mTotalPages;
}
mService is Retrofit interface and mService.search() method returns RxJava's Observable<SearchResults>. My unit test code looks like this:
package mobi.zona.presenters;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.List;
import com.example.api.Service;
import com.example.model.Movie;
import com.example.model.SearchResults;
import com.example.views.MoviesListView;
import rx.Observable;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class SearchPresenterTest {
#Mock
Service mService;
#Mock
MoviesListView mMoviesListView;
#Test
public void testLoadNextPage() throws Exception {
String searchQuery = "the hunger games";
SearchResults searchResults = new SearchResults();
List<Movie> movies = new ArrayList<>();
searchResults.setItems(movies);
when(mService.search(searchQuery, 1)).thenReturn(Observable.just(new SearchResults()));
MoviesListPresenter presenter = new SearchPresenter(mZonaService, mMoviesListView, searchQuery);
presenter.loadNextPage();
verify(mService, times(1)).search(searchQuery, 1);
verify(mMoviesListView, times(1)).showProgress();
verify(mMoviesListView, times(1)).showMovies(movies);
}
}
The problem is the third verify(mMoviesListView, times(1)).showMovies(movies); line - it allways fails. Whem I'm trying to debug this test I see that control flow never goes into .subscribe(itemPage - {.... I think that it's something related to the fact that I'm subscribing on Schedulers.io() thread, but have no idea on how to fix this. Any ideas?
EDIT 1:
Changed the presenter to take Scheduler's as constructor parameters. Changed test to look like this:
#Test
public void testLoadNextPage() throws Exception {
String searchQuery = "the hunger games";
SearchResults searchResults = new SearchResults();
List<Movie> movies = new ArrayList<>();
searchResults.setItems(movies);
when(mZonaService.search(searchQuery, 1)).thenReturn(Observable.just(new SearchResults()));
MoviesListPresenter presenter = new SearchPresenter(mZonaService, mMoviesListView, searchQuery,
Schedulers.test(), Schedulers.test());
presenter.loadNextPage();
verify(mZonaService, times(1)).search(searchQuery, 1);
verify(mMoviesListView, times(1)).showProgress();
verify(mMoviesListView, times(1)).showMovies(movies);
}
Still getting this test failure message:
Wanted but not invoked:
mMoviesListView.showMovies([]);
-> at com.example.presenters.SearchPresenterTest.testLoadNextPage(SearchPresenterTest.java:46)
However, there were other interactions with this mock:
mMoviesListView.showProgress();
-> at com.example.presenters.SearchPresenter.loadNextPage(SearchPresenter.java:41)
In my apps interactors/use-cases/model (mService in your case) is responsible for specifying Scheduler for the operation (since it knows better what kind of operation it does).
So, move your subscribeOn to mService. After that your mock will work fine.
Going deeper, if now you'll want to test mService I would recommend you to make it "dependent" on Scheduler. In other words - add Sheduler as a constructor parameter.
public class MyService {
private final Scheduler taskScheduler;
public MyService(Scheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
// ...
public Observable<Something> query() {
return someObservable.subscribeOn(taskScheduler);
}
}
Then, in tests you can use Schedulers.immediate() and for actual app Schedulers.io() (or whatever you like, really).
Hi I've been trying all night to run this example and have had no luck what so ever, I cannot find a solution. I have two file.
First is Worker.java and here is its contents
import javafx.application.Application;
import java.util.logging.Level;
import java.util.logging.Logger;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author brett
*/
public class Worker {
/**
* #param args the command line arguments
* #throws java.lang.Exception
*/
/**
*
* #param args
* #throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO code application logic here
doit();
}
private static void doit(){
try {
IteratingTask mytask = new IteratingTask(800000);
mytask.call();
System.out.println(mytask.getValue());
int pro = (int) mytask.getProgress();
System.out.println(pro);
} catch (Exception ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Next is the IteratingTask.java file and its contents
//import javafx.concurrent.Task;
import javafx.application.Application;
import javafx.concurrent.Task;
/**
*
* #author brett
*/
public class IteratingTask extends Task<Integer> {
private final int totalIterations;
public IteratingTask(int totalIterations) {
this.totalIterations = totalIterations;
}
#Override protected Integer call() throws Exception {
int iterations;
// iterations = 0;
for (iterations = 0; iterations < totalIterations; iterations++) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
updateMessage("Iteration " + iterations);
updateProgress(iterations, totalIterations);
}
return iterations;
}
}
I know I'm doing something very wrong but... I just cant see it.
Here is the error it get
run:
Jan 31, 2015 11:56:38 PM Worker doit
SEVERE: null
java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:270)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:265)
at javafx.application.Platform.runLater(Platform.java:81)
at javafx.concurrent.Task.runLater(Task.java:1211)
at javafx.concurrent.Task.updateMessage(Task.java:1129)
at IteratingTask.call(IteratingTask.java:24)
at Worker.doit(Worker.java:38)
at Worker.main(Worker.java:31)
BUILD SUCCESSFUL (total time: 0 seconds)
It builds ok.... any advice would be awesome.
The problem is that the FX Toolkit, and in particular the FX Application Thread have not been started. The update...(...) methods in Task update various state on the FX Application Thread, so your calls to those methods cause an IllegalStateException as there is no such thread running.
If you embed this code in an actual FX Application, it will run fine. Calling launch() causes the FX toolkit to be started.
Also, note that while this will run, Tasks are generally intended to be run in a background thread, as below:
import javafx.application.Application;
import javafx.scene.Scene ;
import javafx.scene.layout.StackPane ;
import javafx.scene.control.Label ;
import javafx.stage.Stage ;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Worker extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane(new Label("Hello World"));
Scene scene = new Scene(root, 350, 75);
primaryStage.setScene(scene);
primaryStage.show();
doit();
}
public static void main(String[] args) throws Exception {
launch(args);
}
private void doit(){
try {
IteratingTask mytask = new IteratingTask(800000);
// mytask.call();
Thread backgroundThread = new Thread(mytask);
backgroundThread.start(); // will return immediately, task runs in background
System.out.println(mytask.getValue());
int pro = (int) mytask.getProgress();
System.out.println(pro);
} catch (Exception ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
For most of my integration test I don't need any security checks. I just want to have shiro out of my way. Beeing a shiro noob I'm wondering whether there's a better way than the one I found.
In my ShiroFilter class, if authentication fails I added this code:
try {
currentUser.login(token);
return CONTINUE;
} catch (AuthenticationException e1) {
// if everything failed, we might actualy have the integration test configuration, let's try
UsernamePasswordToken testToken = new UsernamePasswordToken("testUser", "testPassword", true, host);
try {
currentUser.login(testToken);
return CONTINUE;
} catch (AuthenticationException e2) {
LOGGER.info("Unable to login", e2);
}
}
And this is the shiro.ini for the integration tests:
[users]
testUser = testPassword, administrator
[roles]
administrator = *
Create a class for mock Shiro on integration tests.
package util;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
import org.apache.shiro.util.LifecycleUtils;
import org.apache.shiro.util.ThreadState;
import org.junit.AfterClass;
/**
* Abstract test case enabling Shiro in test environments.
*/
public abstract class AbstractShiroTest {
private static ThreadState subjectThreadState;
public AbstractShiroTest() {
}
/**
* Allows subclasses to set the currently executing {#link Subject} instance.
*
* #param subject the Subject instance
*/
protected void setSubject(Subject subject) {
clearSubject();
subjectThreadState = createThreadState(subject);
subjectThreadState.bind();
}
protected Subject getSubject() {
return SecurityUtils.getSubject();
}
protected ThreadState createThreadState(Subject subject) {
return new SubjectThreadState(subject);
}
/**
* Clears Shiro's thread state, ensuring the thread remains clean for future test execution.
*/
protected void clearSubject() {
doClearSubject();
}
private static void doClearSubject() {
if (subjectThreadState != null) {
subjectThreadState.clear();
subjectThreadState = null;
}
}
protected static void setSecurityManager(SecurityManager securityManager) {
SecurityUtils.setSecurityManager(securityManager);
}
protected static SecurityManager getSecurityManager() {
return SecurityUtils.getSecurityManager();
}
#AfterClass
public static void tearDownShiro() {
doClearSubject();
try {
SecurityManager securityManager = getSecurityManager();
LifecycleUtils.destroy(securityManager);
} catch (UnavailableSecurityManagerException e) {
//we don't care about this when cleaning up the test environment
//(for example, maybe the subclass is a unit test and it didn't
// need a SecurityManager instance because it was using only
// mock Subject instances)
}
setSecurityManager(null);
}
}
Then on test classes that you have Shiro dependency:
#RunWith(MockitoJUnitRunner.class)
public class ManterCampanhaServiceImplTest extends AbstractShiroTest {
#Test
public void someTest() throws Exception {
Subject subjectUnderTest = Mockito.mock(Subject.class);
when(subjectUnderTest.getPrincipal()).thenReturn(EntityObjectMother.getUserData()); //Subject for test
setSubject(subjectUnderTest);
// Now you have a test with a mock subject
// Write the test...
}}