I'm learning to write tests.
I wrote my first test and got error from RxJava 2 + Retrofit.
java.lang.ExceptionInInitializerError on line .subscribeOn(Schedulers.io())
Code:
public void search(String query) {
getViewState().showProgress();
disposable.add(dataManager
.searchMovies(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {
getViewState().hideProgress();
if(response.isSuccessful()) {
getViewState().showSearchResults(response.body());
} else {
getViewState().showToast("Error " + response.code());
}
}, e -> {
getViewState().showToast(e.getMessage());
getViewState().hideProgress();
}
));
}
My test:
public class SearchPresenterTest {
#Rule
public TestComponentRule testComponentRule = new TestComponentRule();
SearchPresenter searchPresenter;
#Mock ISearchView$$State searchView$$State;
#Mock DataManager dataManager;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
RxJavaPlugins.reset();
RxJavaPlugins.setInitIoSchedulerHandler(schedulerCallable -> Schedulers.trampoline());
RxAndroidPlugins.reset();
RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline());
RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
RxAndroidPlugins.setMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline());
searchPresenter = new SearchPresenter();
searchPresenter.setViewState(searchView$$State);
}
#Test
public void searchTest() {
MovieList movieList = new MovieList();
Response response = Response.success(new MovieList());
Observable<Response<MovieList>> obs = Observable.just(response);
when(dataManager.searchMovies(anyString())).thenReturn(obs);
searchPresenter.search(searchRequest());
verify(searchView$$State).showProgress();
verify(searchView$$State).hideProgress();
verify(searchView$$State).showSearchResults(movieList);
}
private String searchRequest() {
return "pirates";
}
}
I tried to fix it with RxJavaPlugins.setIoSchedulerHandler() and other method in #Before but it doesn't help.
This fixed my issue:
#BeforeClass
public static void setUpRxSchedulers() {
Scheduler immediate = new Scheduler() {
#Override
public Disposable scheduleDirect(#NonNull Runnable run, long delay, #NonNull TimeUnit unit) {
// this prevents StackOverflowErrors when scheduling with a delay
return super.scheduleDirect(run, 0, unit);
}
#Override
public Worker createWorker() {
return new ExecutorScheduler.ExecutorWorker(Runnable::run);
}
};
RxJavaPlugins.setInitIoSchedulerHandler(scheduler -> immediate);
RxJavaPlugins.setInitComputationSchedulerHandler(scheduler -> immediate);
RxJavaPlugins.setInitNewThreadSchedulerHandler(scheduler -> immediate);
RxJavaPlugins.setInitSingleSchedulerHandler(scheduler -> immediate);
RxAndroidPlugins.setInitMainThreadSchedulerHandler(scheduler -> immediate);
}
Related
Hi can someone help as to why the main in below never completes. When I pass 1 to the test method it get stuck completely. However passing 2 makes the run ok. Want to understand the actual issue and also what would be the correct way to code this.
public class Test {
private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1, r -> {
Thread t = defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
});
public static void main(String[] args) {
Test t = new Test();
t.test(1).toCompletableFuture().join();
System.out.println("DONE");
}
public CompletionStage<Void> run(int i) {
if (i == 1) throw new RuntimeException();
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
return future;
}
public CompletionStage<Void> test(int i) {
CompletableFuture<Void> future = new CompletableFuture<>();
EXECUTOR.schedule(() -> run(i).handle((output, error) -> {
if (error instanceof CompletionException) {
error = error.getCause();
}
if (error != null) {
CompletableFuture<Void> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(error);
return failedFuture;
}
return completedFuture(output);
}).thenCompose(u -> u).thenApply(future::complete).exceptionally(future::completeExceptionally), 0, TimeUnit.SECONDS);
return future;
}
}
EDIT: used solution suggested by holger and working fine. In this solution the handle catches the runtime exception. Why not previously?
public class Test {
private static final ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(1, r -> {
Thread t = defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
});
public static void main(String[] args) {
Test t = new Test();
System.out.println("STARTED");
t.test(1).toCompletableFuture().join();
System.out.println("DONE");
}
public CompletionStage<Void> run(int i) {
if (i == 1)
throw new RuntimeException();
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
return future;
}
public CompletionStage<Void> test(int i) {
return completedFuture(i).thenComposeAsync(this::run, r -> EXECUTOR.schedule(r, 0, TimeUnit.SECONDS))
.handle((res, ex) -> {
if (ex == null)
return completedFuture(res);
if (ex instanceof CompletionException) {
ex = ex.getCause();
}
CompletableFuture<Void> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(ex);
return failedFuture;
}).thenCompose(u -> u);
}
}
I want to do chaining of async calls and get a single callback response when finish.
For example something like this:
public invokeAsyncAPI1 () {
AsyncRabbitTemplate.RabbitConverterFuture<Response1> futureResponse1 = asyncAPI1();
futureResponse1.addCallback {
successCallBack (Response1 result1) {
if(result != OK) {
return immediately with false
} else {
invokeAsyncAPI2(result1);
}
}
failureCallBack () {
return immediately with false
}
}
}
public invokeAsyncAPI2 (result1) {
AsyncRabbitTemplate.RabbitConverterFuture<Response2> futureResponse2 = asyncAPI2();
futureResponse2.addCallback {
successCallBack (Response2 result2) {
if(result != OK) {
return immediately with false
} else {
return true response
}
}
failureCallBack () {
return immediately with false
}
}
}
In the end get result2.get(), if valid retrun ok response.
Yes; it's possible - here's an example:
#SpringBootApplication
public class So69213655Application {
public static void main(String[] args) {
SpringApplication.run(So69213655Application.class, args);
}
#RabbitListener(queues = "service1")
#SendTo
public String service1(String in) {
System.out.println("service1: " + in);
return in.toUpperCase();
}
#RabbitListener(queues = "service2")
#SendTo
public String service2(String in) {
System.out.println("service2: " + in);
return in.toLowerCase();
}
#Bean
AsyncRabbitTemplate template(RabbitTemplate template) {
AsyncRabbitTemplate async = new AsyncRabbitTemplate(template);
return async;
}
#Bean
ApplicationRunner runner(ChainedSender sender) {
return args -> {
System.out.println("And the answer is: " + sender.send("TesT").get(10, TimeUnit.SECONDS));
};
}
}
#Component
class ChainedSender {
private final AsyncRabbitTemplate template;
ChainedSender(AsyncRabbitTemplate template) {
this.template = template;
}
Future<String> send(String out) {
SettableListenableFuture<String> future = new SettableListenableFuture<>();
RabbitConverterFuture<Object> future1 = this.template.convertSendAndReceive("service1", out);
future1.addCallback(result1 -> {
RabbitConverterFuture<Object> future2 = this.template.convertSendAndReceive("service2", result1);
future2.addCallback(result2 -> {
future.set((String) result2);
}, ex -> {
future.setException(ex);
});
}, (ex) -> {
future.setException(ex);
});
return future;
}
}
service1: TesT
service2: TEST
And the answer is: test
In the below code the subscriber stops recieving data whenever there is a timeout exception. How can I make sure that the subscriber does not stop when there is exception.
public class ReactiveDataService
{
private static String[] quotes = {"ITEM1", "ITEM2", "ITEM3"};
public Observable<Notification<String>> getStreamData()
{
return Observable.create(subscriber -> {
if(!subscriber.isUnsubscribed())
{
Stream<String> streams = Arrays.stream(quotes);
streams.map(quote -> quote.toString()).filter(quote -> quote!=null)
.forEach(q -> {
subscriber.onNext(Notification.createOnNext(q));
try
{
Random rand = new Random();
Integer i = (rand.nextInt(5)+1)*1000;
Thread.sleep(i);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
});
}
subscriber.onCompleted();
});
}
}
public class ReactiveResource
{
public static void main(String args[])
{
Observable<Notification<String>> watcher = new ReactiveResource().getData()
.timeout(4, TimeUnit.SECONDS)
.doOnError(failure -> System.out.println("Error:" + failure.getCause()))
.onErrorResumeNext(th -> {
return Observable.just(Notification.createOnError(new TimeoutException("Timed Out!")));
});
watcher.subscribe(
ReactiveResource::callBack,
ReactiveResource::errorCallBack,
ReactiveResource::completeCallBack
);
}
public static Action1 callBack(Notification<String> data)
{
System.out.println(data.getValue());
return null;
}
public static void errorCallBack(Throwable throwable)
{
System.out.println(throwable instanceof TimeoutException);
System.out.println(throwable);
}
public static void completeCallBack()
{
System.out.println("On completed successfully");
}
private Observable<Notification<String>> getData()
{
return new ReactiveDataService().getStreamData();
}
You can combine publish, mergeWith and timer to achieve this effect:
static <T> ObservableTransformer<T, T> onTimeoutKeepAlive(
long timeout, TimeUnit unit, Scheduler scheduler, T keepAliveItem) {
return upstream ->
upstream.publish(o ->
o.mergeWith(
Observable.timer(timeout, unit, scheduler)
.map(t -> keepAliveItem)
.takeUntil(o)
.repeat()
.takeUntil(o.ignoreElements().toObservable())
)
);
}
usage:
source
.compose(onTimeoutKeepAlive(
10, TimeUnit.SECONDS, Schedulers.computation(),
Notification.createOnError(new TimeoutException())
))
.subscribe(/* ... */);
I'm trying to write a unit test for the following function.
responseHandler and ErrorHandler are both methods that are passes using the Command Pattern
and are used to continue the program's flow.
futureCommand is a lettuce object (Redis implementation in java).
I'm having difficulties with how to test this method since is both using a future and does not return anything.
public void getOfferData(EventRequestContext<? extends BaseEvent> ctx, int offerId, ResponseHandler<T1Offer> responseHandler,
ErrorHandler<Throwable> errorHandler) throws Exception {
String redisKey = keyPrefix + offerId;
RedisFuture<List<String>> futureCommand = connectionWrapper.getHashValues(redisKey, getRequiredParams());
futureCommand.thenAccept(valuesList -> {
TrackerScheduler.processT1GenreicPool.execute(ctx, () -> {
Map<String, String> resultMap = reconstructMapValues(valuesList, getRequiredParams(), redisKey, ctx);
T1Offer offerData;
if(!resultMap.isEmpty()) {
offerData = new T1Offer(resultMap);
} else {
offerData = new T1Offer();
}
if(!offerData.isValid()) {
errorHandler.onError(new Exception("Invalid fields in offerData"));
} else {
responseHandler.onResponse(offerData);
}
});
});
}
My best attempt was to send the assertion using the responseHandler method like this:
#Test
public void getOfferData_offerFullData_parseSuccess() throws Exception {
T1ClickOfferDao.instance.getOfferData(null, Integer.parseInt(OFF_ID), resultOffer -> {
Assert.assertEquals("", resultOffer.getActivationDate());
}, error -> {
});
}
but the Test context is finished before the future is evaluated. And even if I Threas.sleep for a second - the assertion does not affect the test result.
How about
#Test
public void getOfferData_offerFullData_parseSuccess() throws Exception {
final String lock = new String("lock");
T1ClickOfferDao.instance.getOfferData(null, Integer.parseInt(OFF_ID), resultOffer -> {
Assert.assertEquals("", resultOffer.getActivationDate());
synchronized(lock){
lock.notifyAll();
}
}, error -> {
});
synchronized(lock){
try{
lock.wait(1000*2);
} catch (InterruptedException ex) {
fail("Timeout");
}
}
}
I was trying to use netflix observable however I managed to do so only synchronously:
This is how I define the remote call:
#Named
public class BroConsumerService {
..
#HystrixCommand(fallbackMethod = "stubbedMethod")
public Observable<String> executeObservableBro(String name) {
return new ObservableResult<String>() {
#Override
public String invoke() {
return executeRemoteService(name);
}
};
}
private String stubbedMethod(String name) {
return "return stubbed";
}
//here I am actually invoking (and observing this method)
#RequestMapping("/executeObservableBro")
public String executeObservableBro(#RequestParam(value = "name", required = false) String name) throws ExecutionException, InterruptedException {
Observable<String> result= broConsumerService.executeObservableBro(name);
result.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.out.println("completed");
}
#Override
public void onError(Throwable e) {
System.out.printf(e.getMessage());
}
#Override
public void onNext(String s) {
System.out.println("on next..");
}
});
}
But that works synchronously. I want to be able to "listen" to the executeObservableBro before I execute it. and each time it's being executed ill get notified.
Example would be highly appreciated.
Thanks,
ray.
you have to provide schedulers in subscribeOn method like:
public static void main(String[] args) throws InterruptedException {
Observable<Integer> observable2 = Observable.create(subscriber->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Arrays.asList(1, 2, 3).forEach((value)-> subscriber.onNext(value));
subscriber.onCompleted();
subscriber.onError(new RuntimeException("error"));
});
System.out.println("Before");
observable2
.subscribeOn(Schedulers.io()).subscribe(
(next) -> log.info("Next element {}", next),
(error) -> log.error("Got exception", error),
() -> log.info("Finished")//on complete
);
System.out.println("After");
//Thread.sleep(5000); //uncomment this to wait for subscriptions, otherwise main will quit
}
Its not async by default :)