Petersoj / alpaca-java

A Java API for Alpaca, the commission free, algo friendly, stock trading broker. https://alpaca.markets
https://petersoj.github.io/alpaca-java/
MIT License
197 stars 82 forks source link

Testing advice for alpaca-java #116

Closed Kurru closed 3 months ago

Kurru commented 2 years ago

Writing tests for the java library is rather wordy. Is it possible to improve this or is it limited by the code generator?

Specifically, this is the code to setup a single API integration using a little bit of mockito:

StockBarsResponse stockBarsResponse = new StockBarsResponse();
StockBar bar1 = new StockBar();
bar1.setTimestamp(LocalDateTime.of(2022, 2, 2, 9, 0).atZone(NewYorkBroker.TIME_ZONE));
bar1.setOpen(10.0);
bar1.setHigh(11.0);
bar1.setLow(9.0);
bar1.setClose(10.5);
bar1.setTradeCount(100L);
bar1.setVolume(100L);
bar1.setVwap(10.1);

stockBarsResponse.setBars(new ArrayList<>(ImmutableList.of(bar1, bar2)));
when(alpacaAPI.stockMarketData().getBars(eq("TWTR"),
    any(ZonedDateTime.class),
    any(ZonedDateTime.class),
    eq(10000),
    isNull(),
    eq(5),
    eq(BarTimePeriod.MINUTE),
    eq(BarAdjustment.SPLIT),
    eq(BarFeed.SIP))).thenReturn(stockBarsResponse);

Some possible improvements:

Looking at jsonschema2pojo, seems that generate-builders property can provide a method chaining interface. Documentation

For Long vs long, perhaps you could use the usePrimitives option? Documentation

Kurru commented 2 years ago

Oh, separately, when reading bars for a time period that does not have bars, then we receive an unexpected null from StockBarsResponse.getBars(). It would be expected this would return an empty list. Is this something that can be changed or documented?

the return value of 
 "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBarsResponse.getBars()" is null
Petersoj commented 2 years ago

@Kurru sorry for the late response. Thanks for your feedback! Unit testing is certainly on the TODO list, but I haven't managed to tackle all of it quite yet, but this library appears to be pretty stable anyways (though unit testing is always good to have of course). Thank you for looking into this.

To address some of the possible improvements you suggest:

Regarding your second comment about getBars() returning null, I receive a response of {"bars":null,"symbol":"AAPL","next_page_token":null} when attempting to query an empty bar data range for AAPL in the following piece of code:

StockBarsResponse aaplBarsResponse = alpacaAPI.stockMarketData().getBars(
                "AAPL",
                ZonedDateTime.of(2021, 7, 6, 0, 0, 0, 0, ZoneId.of("America/New_York")),
                ZonedDateTime.of(2021, 7, 6, 1, 0, 0, 0, ZoneId.of("America/New_York")),
                null,
                null,
                1,
                BarTimePeriod.HOUR,
                BarAdjustment.SPLIT,
                BarFeed.SIP);
        aaplBarsResponse.getBars().forEach(System.out::println);

This code throws an NPE as expected because the value of bars is null in the returned JSON object. While I think it makes more sense for this JSON response to return an empty array [], perhaps the null value is intentional sent by Alpaca and there is some undocumented instances where an empty bar array [] and a null response mean different things (not sure if that's true, but I want this library to be as transparent as possible when transforming JSON to POJOs). So it's likely better that null is returned and this library doesn't transform null into an empty returned List. I can document this in the Javadocs in the next release.

Thanks for your feedback!

Kurru commented 2 years ago

Thanks for the detailed review! I find it rather frustrating when Java code returns nulls for empty lists though, as this complicates the code for processing this scenario, but I can understand your rationale for not making any assumptions about the underlying API.

Another option, could be to review how the official API implementations handle these json null in their client libraries. Perhaps there they don't always use the primitive types because they know something is not null or they don't expose nulls for lists as they know thats just how the alpaca json api library exposes empty lists.

I think the null json list to empty list could in general be acceptable expectation though. I'd personally say any API that has different behavior for empty list vs null list is a poorly defined API.

Petersoj commented 3 months ago

Some of this has been addressed in 10.0.0. Testing frameworks have been removed for now. Will re-open later potentially.