JVM de pepino - não apenas BDD

Olá a todos! Existem muitos artigos sobre o Pepino no Habré e na Internet, mas quero inserir meus cinco centavos.



Muitos usam o Pepino, e eu mesmo acho uma biblioteca maravilhosa. Minha opinião pessoal é que é útil que todo desenvolvedor Java esteja familiarizado com esta biblioteca, juntamente com a estrutura JUnit.


Se o seu conhecimento sobre Pepino é igual a zero, existem excelentes artigos sobre Habré com os quais é melhor começar a conhecer esta ferramenta



. , , , 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