Junit with Reactive Programming in Android project - java

I'm trying to use JUnit4 in an Android project, where I also use RxAndroid/RxJava.
What I do is calling REST API from UUID generator using retrofit
UUIDApi.java just an interface for retrofit calls (now is just one)
public interface UUIDApi {
static final String BASE_URL = "https://www.uuidgenerator.net";
#GET("/api/version4")
Observable<String> getUUID();
}
UUIDModel.java where retrofit is initialized and where the interface written above is implemented
public class UUIDModel implements UUIDApi{
private Retrofit retrofit;
private UUIDApi uuidApi;
UUIDObserver uuidObserver = new UUIDObserver();
public UUIDModel() {
retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(new ToStringConverterFactory())
.baseUrl(UUIDApi.BASE_URL)
.build();
uuidApi = retrofit.create(UUIDApi.class);
}
#Override
public Observable<String> getUUID() {
return uuidApi.getUUID();
}
public void generateUUID(){
Observable<String> observable = this.getUUID();
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(uuidObserver);
}
}
Than I have the UUIDObserver that is just a class that implements Observer.
Note: new ToStringConverterFactory is a class I found here
Executing this code using emulator, I know for sure that it works fine. The problem is that I don't understand how to junit this code since using rxAndroid/rxJava it gets executed in another thread.
I read that:
The official way to test an observable is by using a TestSubscriber, an helper subscriber provided directly by the RxJava library.
so I tried
#Test
public void test_uuid() throws Exception {
UUIDApi uuidApi = new UUIDModel();
Observable<String> observable = uuidApi.getUUID();
TestSubscriber<String> testSubscriber = new TestSubscriber<>();
observable.subscribe(testSubscriber);
}
but at observable.subscribe(testSubscriber); I get the error 'cannot resolve method 'subscribe(io.reactivex.subscribers.TestSubscriber)'
What am I doing wrong? How should I cope with rxProgramming and JUnit?

TestSubscriber is relevant for Flowable while TestObserver is used with Observable. But for both you don't actually have to subscribe anything manually. You just use the test() method instead.
observable.test()
.assertNoErrors()
.assertValue(expected)
.assertComplete();

I am testing RxJava + Retrofit like that :
First of all I am using Schedulers.trampoline() for testing purposes.
Then in a basic unit test :
#Mock
ResponseBody mockResponseBody;
#Test
public void testCondition(){
when(mService.operation()).thenReturn(Observable.just(response));
mPresenter.handleOperation();
verify(mView).operationResult();
}
I am testing 3 case : Success response(between 200-300),error response,fail
For testing success : response = Response.success(your_body);
For testing error : response= Response.error(any_error_code,mockResponseBody);
For testing fail : Just return Observable.error(); inside thenReturn
In this unit test we are mocking our webservice calls and it never depends on webservice so you can run your unit tests even when you offline.
But if you want to see real webservice calls you can write integration tests.

You could utilize test operator to subscribe your Observable with TestObserver, which records events and allows you to make assertions about them:
observable
.test()
.assertValue(...)
.assertComplete()
//etc
To deal with async operations in your reactive stream you could try these approaches:
utilize TestObserver's handy await operator, wich awaits until the TestObserver receives an onError or onComplete events.
provide Schedulers via dependency injection (e.g. with constructor injection), so you could substitute them in tests with Schedulers.trampoline.
change your async Scheduler's default implementation with one of RxJava's hook functions (e.g. RxJavaPlugins.setInitIoSchedulerHandler).

Related

How to get to a WireMockServer from Junit 5 WireMockTest test case

Wire mock has a addMockServiceRequestListener function available on the JUnit4 Rule or on a wiremock server instance.
How do I get to that function from a test class annotated with JUnit 5's #WireMockTest annotation?
More generally, how do I get an instance of the WireMockServer from a test in a class that uses #WireMockTest ?
There's a better option in newer versions of WireMock than calling addMockServiceRequestListener, which is to register a PostServeAction implementation as an extension when configuring JUnit:
#RegisterExtension
static WireMockExtension wm =
WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort()
.extensions(new PostServeAction() {
#Override
public String getName() {
return "my-action";
}
#Override
public void doGlobalAction(ServeEvent serveEvent, Admin admin) {
// Do something
}
}))
.build();
PostServeAction implementations are the "proper" way to listen for events and will still work in future versions, whereas listeners will be deprecated and removed eventually. They also are given more context about the request than listeners.
Would retrieving the DSL from WireMockRunTimeInfo sufficient in your case?
https://wiremock.org/docs/junit-jupiter/
https://javadoc.io/doc/com.github.tomakehurst/wiremock-jre8/latest/com/github/tomakehurst/wiremock/junit5/WireMockRuntimeInfo.html#getWireMock--
From WireMockRunTimeInfo , there is a getWireMock() method which returns WireMock.
Example:
#WireMockTest
public class DeclarativeWireMockTest {
#Test
void test_something_with_wiremock(WireMockRuntimeInfo wmRuntimeInfo) {
// The static DSL will be automatically configured for you
stubFor(get("/static-dsl").willReturn(ok()));
// Instance DSL can be obtained from the runtime info parameter
WireMock wireMock = wmRuntimeInfo.getWireMock();
wireMock.register(get("/instance-dsl").willReturn(ok()));
// Info such as port numbers is also available
int port = wmRuntimeInfo.getHttpPort();
// Do some testing...
}
}

Why context is not propagated to (RxJava) Single from (Reactor) Mono?

For example, let's say I have a WebFilter that writes some Context
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange)
.contextWrite(Context.of("my-context", "foobar"));
}
Downstream, my controller does this
#GetMapping(path = "test")
public Mono<String> test() throws Exception {
final Mono<ContextView> contextMono = Mono.deferContextual(Mono::just);
return contextMono.flatMap(ctx -> Mono.just(ctx.get("my-context")));
}
The above all works fine.
What if I wanted to return a Single from the controller method? I tried using RxJava3Adapter.monoToSingle() but it breaks the reactor chain.
#GetMapping(path = "test")
public Single<String> test() throws Exception {
final Mono<ContextView> contextMono = Mono.deferContextual(Mono::just);
return RxJava3Adapter.monoToSingle(
contextMono.flatMap(ctx -> Mono.just(ctx.get("my-context"))));
}
My guess is that since I'm not returning the Mono, nothing subscribes to this contextMono inside of the RxJava3Adapter. Is that the right explanation?
Is there any way to return a Single while having the Context be passed in?
The subscription itself works fine. The problem is that Context is a Reactor specific feature which is not part of the Reactive Streams standard. So when you convert a Mono to Single, the Context is lost.
In the code you attached you should just simply omit the Rx part to make it work but I imagine that your real world use case might be more convoluted. A good approach can be to convert the Rx code to Reactor at the earliest possible place (e.g. when you call the third-party library which returns the Rx type) and use Reactor in the rest of the codebase including the controller return type.

How to transfer data via reactor's subscriber context?

I'm a new for a project reactor, but i have task to send some information from classic spring rest controller to some service, which is interacts with different system. Whole project developed with project reactor.
Here is my rest controller:
#RestController
public class Controller {
#Autowired
Service service;
#PostMapping("/path")
public Mono<String> test(#RequestHeader Map<String, String> headers) throws Exception {
testService.saveHeader(headers.get("header"));
return service.getData();
}
And here is my service:
#Service
public class Service {
private Mono<String> monoHeader;
private InteractionService interactor;
public Mono<String> getData() {
return Mono.fromSupplier(() -> interactor.interact(monoHeader.block()));
}
public void saveHeader(String header) {
String key = "header";
monoHeader = Mono.just("")
.flatMap( s -> Mono.subscriberContext()
.map( ctx -> s + ctx.get(key)))
.subscriberContext(ctx -> ctx.put(key, header));
}
Is it acceptable solution?
Fisrt off, I don't think you need the Context here. It is useful to implicitly pass data to a Flux or a Mono that you don't create (eg. one that a database driver creates for you). But here you're in charge of creating the Mono<String>.
Does the service saveHeader really achieve something? The call seem transient in nature: you always immediately call the interactor with the last saved header. (there could be a side effect there where two parallel calls to your endpoint end up overwriting each other's headers).
If you really want to store the headers, you could add a list or map in your service, but the most logical path would be to add the header as a parameter of getData().
This eliminates monoHeader field and saveHeader method.
Then getData itself: you don't need to ever block() on a Mono if you aim at returning a Mono. Adding an input parameter would allow you to rewrite the method as:
public Mono<String> getData(String header) {
return Mono.fromSupplier(() -> interactor.interact(header));
}
Last but not least, blocking.
The interactor seems to be an external service or library that is not reactive in nature. If the operation involves some latency (which it probably does) or blocks for more than a few milliseconds, then it should run on a separate thread.
Mono.fromSupplier runs in whatever thread is subscribing to it. In this case, Spring WebFlux will subscribe to it, and it will run in the Netty eventloop thread. If you block that thread, it means no other request can be serviced in the whole application!
So you want to execute the interactor in a dedicated thread, which you can do by using subscribeOn(Schedulers.boundedElastic()).
All in all:
#RestController
public class Controller {
#Autowired
Service service;
#PostMapping("/path")
public Mono<String> test(#RequestHeader Map<String, String> headers) throws Exception {
return service.getData(headers.get("header"));
}
}
#Service
public class Service {
private InteractionService interactor;
public Mono<String> getData(String header) {
return Mono.fromSupplier(() -> interactor.interact(header))
.subscribeOn(Schedulers.boundedElastic());
}
}
How to transfer data via reactor's subscriber context?
Is it acceptable solution?
No.
Your code of saveHeader() method is an equivalent of simple
public void saveHeader(String header) {
monoHeader = Mono.just(header);
}
A subscriberContext is needed if you consume the value elsewhere - if the mono is constructed elsewhere. In your case (where you have all code before your eyes in the same method) just use the actual value.
BTW, there are many ways to implement your getData() method.
One is as suggested by Simon Baslé to get rid of a separate saveHeader() method.
One other way, if you have to keep your monoHeader field, could be
public Mono<String> getData() {
return monoHeader.publishOn(Schedulers.boundedElastic())
.map(header -> interactor.interact(header));
}

Presenter unit test with RxJava CompositeSubscription

I would like to create a test for my Presenter class but I'm having problems with the CompositeSubscription instance inside the Presenter itself. When I run the test I'm getting this error:
java.lang.NullPointerException
at rx.subscriptions.CompositeSubscription.add(CompositeSubscription.java:60)
at com.example.Presenter.addSubscription(Presenter.java:67)
at com.example.Presenter.getGummyBears(Presenter.java:62)
This is roughly my Presenter class:
public class Presenter {
CompositeSubscription compositeSubscription = new CompositeSubscription();
//creation methods...
public void addSubscription(Subscription subscription) {
if (compositeSubscription == null || compositeSubscription.isUnsubscribed()) {
compositeSubscription = new CompositeSubscription();
}
compositeSubscription.add(subscription);
}
public void getGummyBears() {
addSubscription(coreModule.getGummyBears());
}
}
The CoreModule is an interface (part of a different module) and there is another class CoreModuleImpl in which are located all retrofit API calls and their conversion to Subscriptions.
Something like:
#Override public Subscription getGummyBears() {
Observable<GummyBears> observable = api.getGummyBears();
//a bunch of flatMap, map and other RxJava methods
return observable.subscribe(getDefaultSubscriber(GummyBear.class));
//FYI the getDefaultSubscriber method posts a GummyBear event on EventBus
}
Now what I want to do is to test the getGummyBears() method.
My test method looks like this:
#Mock EventBus eventBus;
#Mock CoreModule coreModule;
#InjectMock CoreModuleImpl coreModuleImpl;
private Presenter presenter;
#Before
public void setUp() {
presenter = new Presenter(coreModule, eventBus);
coreModuleImpl = new CoreModuleImpl(...);
}
#Test
public void testGetGummyBears() {
List<GummyBears> gummyBears = MockBuilder.newGummyBearList(30);
//I don't know how to set correctly the coreModule subscription and I'm trying to debug the whole CoreModuleImpl but there are too much stuff to Mock and I always end to the NullPointerException
presenter.getGummyBears(); //I'm getting the "null subscription" error here
gummyBears.setCode(200);
presenter.onEventMainThread(gummyBears);
verify(gummyBearsView).setGummyBears(gummyBears);
}
I already saw many test examples from different projects but no one is using this Subscription approach. They just return the Observable which is consumed directly inside the presenter. And in that case I know how a test has to be written.
What's the correct way to test my situation?
Looks like coreModule.getGummyBears() is returning null. Just step through with debug and it should be pretty clear. When using mocking frameworks you can get null returned from method calls on a mocked object when you haven't specified what the method call should return on that mocked object.
As Dave mentioned, you need to mock the return value of CoreModule.getGummyBears. One strange thing is that you are not using the CoreModuleImpl that is being created. Instead, you're passing coreModule to the presenter's constructor.
You can mock getGummyBears() by doing something like this:
when(coreModule.getGummyBears()).thenReturn(MockBuilder.newGummyBearList(30);
Then the specific error you are encountering should be resolved. It doesn't look like you need CoreModuleImpl for this specific test case.

By using a dynamic proxy, is there a way I can return an object whose type doesn't match the method signature of the proxied interface?

I think the short answer may be no, but I'm hoping I can get alternative suggestions. Assume I have a data object and a data service. The data service is an interface and has the following method.
public Data getData();
I'm creating a proxy for the service using the following invocation handler plus Netty to do what I'd call asynchronous rpc. The proxy is on the client side.
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Convert the call into an async request that returns a ListenableFuture
APCRequest request = new APCRequest(serviceType, method, args);
ListenableFuture future = apcClient.asyncMessage(request);
// This blocks until the future finishes
return future.get();
}
This works fine. However, if my client is a UI, I end up wrapping the service call in something like a SwingWorker. I'd prefer to come up with a way of returning the ListenableFuture that I already have sitting there. Is there any way I can accomplish that without creating a separate, asynchronous service API. For example:
public ListenableFuture<Data> getData();
If I could have my InvocationHandler return the wrong type, I could use something like this.
public abstract class AsyncServiceCall<S, D> { // S = service type, D = expected doCall return type
protected final S service;
protected AsyncServiceCall(Class<S> serviceType, APCClient client) {
ProxyFactory proxyFactory = new ProxyFactory(client);
// The true tells the proxyFactory we're expecting a ListenableFuture<D>
// rather than the real return type.
service = proxyFactory.createProxy(serviceType, true);
}
// Sub-classes would make a normal method call using this. For
// example, service.getData()
public abstract Object doCall();
#SuppressWarnings("unchecked")
public ListenableFuture<D> execute() {
return (ListenableFuture<D>) doCall();
}
Is there another way of accomplishing what I want? Performance isn't an issue for me, so blocking until the proxy can get the return value from the future is still an option if there's no simple way of doing what I want. It just seems like a waste since I want an asynchronous call in the UI anyway.
Keeping my service API simple is more of a priority than anything. I want to be able to prototype using a simple service provider that instantiates service implementations directly and plug in my remoting protocol / server that's using dynamic proxies / Netty late in the development cycle.
If you want to keep your API simple then I would suggest providing only the async API in the interface - it's much easier to wrap up a synchronous implementation in an asynchronous API than vice-versa.
public interface DataService {
public ListenableFuture<Data> getData();
}
public abstract class LocalDataService implements DataService {
public ListenableFuture<Data> getData() {
SettableFuture<Data> result = SettableFuture.create();
try {
Data theData = computeData();
result.set(theData);
} catch(Throwable t) {
result.setException(e);
}
return result;
}
protected abstract Data computeData() throws Throwable;
}

Categories

Resources