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:
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) {
       
       if(dataTable.get(0).get(0) != null && dataTable.get(0).get(0).trim().equals("bid")){
           dataTable = dataTable.subList(1, dataTable.size());
       }
       
       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 , , , , - . .
— . - . - .
.
, .