الخيار JVM - ليس فقط BDD

تحية للجميع! هناك العديد من المقالات حول الخيار على حبري وعلى الإنترنت ، لكني أريد إدراج سنتي الخمسة.



يستخدم الكثيرون الخيار ، وأجده بنفسي مكتبة رائعة جدًا. رأيي الشخصي هو أنه من المفيد لكل مطور جافا التعرف على هذه المكتبة جنبًا إلى جنب مع إطار عمل JUnit.


إذا كانت معرفتك حول الخيار تساوي الصفر ، فهناك مقالات ممتازة عن حبري من الأفضل أن تبدأ في التعرف على هذه الأداة



. , , , Cucumber .


Cucumber BDD Cucumber. , Cucumber , BDD. . Cucumber .


: matching engine


, , - open source ( matching engine), , Cucumber.


An order matching system – , / -.


Github . , JUnit.


basicFullCycleTest.


, ( / ).


- 100 , , , .


Cucumber.


Cucumber Maven. 5.4.2.


<dependency>
   <groupId>io.cucumber</groupId>
   <artifactId>cucumber-java</artifactId>
   <version>${cucumber.version}</version>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>io.cucumber</groupId>
   <artifactId>cucumber-junit</artifactId>
   <version>${cucumber.version}</version>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>io.cucumber</groupId>
   <artifactId>cucumber-picocontainer</artifactId>
   <version>${cucumber.version}</version>
   <scope>test</scope>
</dependency>

JUnit :



try (final ExchangeTestContainer container = new ExchangeTestContainer()) {
    container.initBasicSymbols();
    container.initBasicUsers();

- JUnit matching engine. Cucumber , .



import io.cucumber.java.After;
import io.cucumber.java.Before;

@Slf4j
public class OrderStepdefs {

   private final ExchangeTestContainer container;

   private List<MatcherTradeEvent> matcherEvents;
   private Map<Long, ApiPlaceOrder> orders = new HashMap<>();

   public OrderStepdefs(ExchangeTestContainer container) {
       this.container = container;
   }

   @Before
   public void before() throws Exception{
       log.info("before");
       container.initBasicSymbols();
       container.initBasicUsers();
   }

   @After
   public void after(){
       if(container != null){
           container.close();
       }
   }


Cucumber (, , feature , ) , . dependency injection, , , . .


- , . - , . , .


JUnit:


// ### 1. first user places limit orders
final ApiPlaceOrder order101 = ApiPlaceOrder.builder().uid(UID_1).id(101).price(1600)
    .size(7).action(ASK).orderType(GTC).symbol(symbolSpec.symbolId).build();

log.debug("PLACE 101: {}", order101);
container.submitCommandSync(order101, cmd -> {
   assertThat(cmd.resultCode, is(CommandResultCode.SUCCESS));
   assertThat(cmd.orderId, is(101L));
   assertThat(cmd.uid, is(UID_1));
   assertThat(cmd.price, is(1600L));
   assertThat(cmd.size, is(7L));
   assertThat(cmd.action, is(ASK));
   assertThat(cmd.orderType, is(GTC));
   assertThat(cmd.symbol, is(symbolSpec.symbolId));
   assertNull(cmd.matcherEvent);
});

, matching engine, , , match .


. , , , .


final int reserve102 = symbolSpec.type == SymbolType.CURRENCY_EXCHANGE_PAIR ? 1561 : 0;

final ApiPlaceOrder order102 = ApiPlaceOrder.builder().uid(UID_1).id(102).price(1550)
   .reservePrice(reserve102).size(4).action(OrderAction.BID).orderType(GTC)
    .symbol(symbolSpec.symbolId).build();

log.debug("PLACE 102: {}", order102);

container.submitCommandSync(order102, cmd -> {
   assertThat(cmd.resultCode, is(CommandResultCode.SUCCESS));
   assertNull(cmd.matcherEvent);
});

, .


final L2MarketDataHelper l2helper = new L2MarketDataHelper()
    .addAsk(1600, 7).addBid(1550, 4);

assertEquals(l2helper.build(), container.requestCurrentOrderBook(symbolSpec.symbolId));

, Cucumber


Scenario Outline: basic full cycle test

When A client 1440001 places an ASK order 101 at 1600@7 (type: GTC, symbol: <symbol>)
And A client 1440001 places an BID order 102 at 1550@4 (type: GTC, symbol: <symbol>, reservePrice: 1561)
Then An "<symbol>" order book is:
  |  bid | price  | ask  |
  |      | 1600  |  7    |
  |  4   | 1550  |       |
And No trade events

, JUnit , Cucumber Scenario Outline.


, <symbol>, , .


Examples:
| symbol        |
| EUR_USD   |
| ETH_XBT      |

Glue-


@When(value = "A client {long} places an {word} order {long} at {long}@{long} \\(type: {word}, symbol: {symbol})")
public void aClientPlacesAnOrderAtTypeGTCSymbolEUR_USD(long clientId, String side, long orderId, long price, long size, String orderType, CoreSymbolSpecification symbol) throws InterruptedException {
   aClientPassAnOrder(clientId, side, orderId, price, size, orderType, symbol, 0);
}

@When(value = "A client {long} places an {word} order {long} at {long}@{long} \\(type: {word}, symbol: {symbol}, reservePrice: {long})")
public void aClientPlacesAnOrderAtTypeGTCSymbolEUR_USD(long clientId, String side, long orderId, long price, long size, String orderType, CoreSymbolSpecification symbol, long reservePrice) throws InterruptedException {
   aClientPassAnOrder(clientId, side, orderId, price, size, orderType, symbol, reservePrice);
}

private void aClientPassAnOrder(long clientId, String side, long orderId, long price, long size, String orderType, CoreSymbolSpecification symbol, long reservePrice) throws InterruptedException {

   ApiPlaceOrder.ApiPlaceOrderBuilder builder = ApiPlaceOrder.builder().uid(clientId)
      .id(orderId)   .price(price).size(size).action(OrderAction.valueOf(side))
      .orderType(OrderType.valueOf(orderType)).symbol(symbol.symbolId);

   if(reservePrice > 0){
       builder.reservePrice(reservePrice);
   }

   final ApiPlaceOrder order = builder.build();

   orders.put(orderId, order);

   log.debug("PLACE : {}", order);

   container.submitCommandSync(order, cmd -> {
       assertThat(cmd.resultCode, is(CommandResultCode.SUCCESS));
       assertThat(cmd.orderId, is(orderId));
       assertThat(cmd.uid, is(clientId));
       assertThat(cmd.price, is(price));
       assertThat(cmd.size, is(size));
       assertThat(cmd.action, is(OrderAction.valueOf(side)));
       assertThat(cmd.orderType, is(OrderType.valueOf(orderType)));
       assertThat(cmd.symbol, is(symbol.symbolId));

       OrderStepdefs.this.matcherEvents = cmd.extractEvents();
   });
}

, matcher events, JUnit, .


@And("No trade events")
public void noTradeEvents() {
   assertEquals(0, matcherEvents.size());
}

, , ( , ) Cucumber dependency injection.


.


Cucumber - . .


(, :-), ) — .


container . , OrderStepdefs. Cucumber - . Cucumber ExchangeTestContainer, OrderStepdefs OrderBookStepdefs ExchangeTestContainer.


public class OrderBookStepdefs {

   private final ExchangeTestContainer container;

   public OrderBookStepdefs(ExchangeTestContainer container) {
       this.container = container;
   }

   @Then("An {symbol} order book is:")
   public void an_order_book_is(CoreSymbolSpecification symbol, List<List<String>> dataTable) {

       //skip a header if it presents
       if(dataTable.get(0).get(0) != null && dataTable.get(0).get(0).trim().equals("bid")){
           dataTable = dataTable.subList(1, dataTable.size());
       }

       //format | bid | price | ask |
       final L2MarketDataHelper l2helper = new L2MarketDataHelper();
       for(List<String> row : dataTable){
           int price = Integer.parseInt(row.get(1));

           String bid = row.get(0);
           if(bid != null && bid.length() > 0){
               l2helper.addBid(price, Integer.parseInt(bid));
           } else {
               l2helper.addAsk(price, Integer.parseInt(row.get(2)));
           }
       }
       assertEquals(l2helper.build(), container.requestCurrentOrderBook(symbol.symbolId));
   }

Cucumber :


Feature: An exchange accepts bid\ask orders, manage and publish order book and match cross orders

 Scenario Outline: basic full cycle test

   When A client 1440001 places an ASK order 101 at 1600@7 (type: GTC, symbol: <symbol>)
   And A client 1440001 places an BID order 102 at 1550@4 (type: GTC, symbol: <symbol>, reservePrice: 1561)
   Then An "<symbol>" order book is:
     | bid | price  | ask |
     |     | 1600   |  7  |
     |  4  | 1550   |     |
   And No trade events

   When A client 1440002 places an BID order 201 at 1700@2 (type: IOC, symbol: <symbol>, reservePrice: 1800)
   Then The order 101 is partially matched. LastPx: 1600, LastQty: 2
   And An "<symbol>" order book is:
     |     |  1600   |  5  |
     |  4  |  1550   |     |

   When A client 1440002 places an BID order 202 at 1583@4 (type: GTC, symbol: <symbol>, reservePrice: 1583)
   Then An "<symbol>" order book is:
     |     |  1600   |  5  |
     |  4  |  1583   |     |
     |  4  |  1550   |     |
   And No trade events

   When A client 1440001 moves a price to 1580 of the order 101
   Then The order 202 is fully matched. LastPx: 1583, LastQty: 4
   And An "<symbol>" order book is:
     |     |  1580  |  1  |
     |  4  |  1550  |     |

   Examples:
   | symbol     |
   | EUR_USD    |
   | ETH_XBT    |

- — , {symbol}. github exchange-core Pull Request, .



, ucumber , JUnit. , QA .


Cucumber JUnit. .


Java , , .


Cucumber , - API, .


API, REST API. glue- REST API. Cucumber .


Cucumber — IDE, JetBrains Idea. .


, Idea , :


  • Ctrl + B — Java
  • , regexp. Idea ,
  • — , . , .

- , Cucumber , , , , - . .


— . - . - .


.


, .




All Articles