tools4j / fix4j-assert

Open source fix testing framework for Java (was fix4j-test)
GNU General Public License v2.0
10 stars 3 forks source link

Build Status Maven Central

fix4j-assert

fix4j-assert is a library to assist in testing of applications using FIX protocol.

It's written in Java and is designed to be used along side your test frameworks of choice. e.g. JUnit, Spock, TestNG

Contact: fix4j.assert@gmail.com

Table of Contents

Using fix4j-assert in your project

via maven:

        <dependency>
            <groupId>org.fix4j</groupId>
            <artifactId>fix4j-assert-all</artifactId>
            <version>1.6</version>
            <scope>test</scope>
        </dependency>

via gradle:

        testCompile group: 'org.fix4j', name: 'fix4j-assert-all', version:'1.6'

Sending a fix message to your application

The example below shows how to send fix messages to your application using fix4j-assert:

session.send("35=V|262=AASDJKG790|263=0|264=20|267=2|269=0|269=1|146=3|55=GBP/USD|64=SP|55=AUD/USD|64=SP|55=USD/JPY|64=SP|");

Receiving a fix message from your application

In the example below, expect will throw an AssertionError if the next message to arrive does not match the given pattern. Note, the regular expression to match the requestId. Regular expressions begin and end in forward slashes.

session.expect("35=X|262=/AASDJKG\d+/);

Quickstart/Example

See here for an example showing the sending and receiving of FIX messages..

Using more descriptive message formats

You may have noticed that the format for sending a message and matching an incoming message is quite similar. They actually use the same underlying code base. fix4j-assert provides more descriptive message formats to make our tests more readable.

Below example with descriptors mixed in with fix tags and values:

session.send("[MsgType]35=V[MARKETDATAREQUEST]|" +
             "[MDReqID]262=AASDJKG790|" +
             "[SubscriptionRequestType]263=0[SNAPSHOT]|" +
             "[MarketDepth]264=20|" +
             "[NoMDEntryTypes]267=2|" +
                 "[MDEntryType]269=0[BID]|" +
                 "[MDEntryType]269=1[OFFER]|" +
             "[NoRelatedSym]146=3|" +
                 "[Symbol]55=GBP/USD|" +
                 "[SettlDate]64=SP|" +
                 "[Symbol]55=AUD/USD|" +
                 "[SettlDate]64=SP|" +
                 "[Symbol]55=USD/JPY|" +
                 "[SettlDate]64=SP|");

This example has some extra syntax next to group repeats:

session.expect("[MsgType]35=V[MARKETDATAREQUEST]|" +
               "[MDReqID]262=AASDJKG790|\n" +
               "[SubscriptionRequestType]263=0[SNAPSHOT]|" +
               "[MarketDepth]264=20|" +
               "[NoMDEntryTypes]267=2|" +
               "   1. [MDEntryType]269=0[BID]|" +
               "   2. [MDEntryType]269=1[OFFER]|" +
               "[NoRelatedSym]146=3|" +
               "   1. [Symbol]55=GBP/USD|" +
               "      [SettlDate]64=SP|" +
               "   2. [Symbol]55=AUD/USD|" +
               "      [SettlDate]64=SP|" +
               "   3. [Symbol]55=USD/JPY|" +
               "      [SettlDate]64=SP|");

For a more old school approach, you can use straight up ascii 0x0001 delimiters:

session.send("35=V\u0001262=AASDJKG790\u0001263=0\u0001264=20\u0001267=2\u0001269=0\u0001269=1\u0001146=3\u000155=GBP/USD\u000164=SP\u000155=AUD/USD\u000164=SP\u000155=USD/JPY\u000164=SP");

Or Ctrl-A delimiters:

session.send("35=V^A262=AASDJKG790^A263=0^A264=20^A267=2^A269=0^A269=1^A146=3^A55=GBP/USD^A64=SP^A55=AUD/USD^A64=SP^A55=USD/JPY^A64=SP");

See here for an example showing the usage of various message formats..

Debugging your tests

Debugging fix applications can often be difficult, given the asynchronous nature of the messaging combined with the obscure format of fix. fix4j-assert attempts to address this issue by providing detailed error messages when a test fails. In the example below, we were expecting a MarketDataRequest with one of the symbols being GBP/XXX.

FAILURE: FIX.4.4:SERVER_COMP_ID->CLIENT_COMP_ID: 
    1. Found message that matches: MsgTypeMatcher{expectedMsgType=V[MarketDataRequest]}
    2. But message does not match: [MDReqID]262=AASDJKG790|[SubscriptionRequestType]263=0[SNAPSHOT]|[NoRelatedSym]146=3|[Symbol]55=GBP/XXX|[Symbol]55=AUD/USD|[Symbol]55=USD/JPY|
    MATCH DETAILS::
        OK   [MDReqID]262: AASDJKG790 == AASDJKG790
        OK   [SubscriptionRequestType]263: 0[SNAPSHOT] == 0[SNAPSHOT]
        OK   [NoRelatedSym]146: 3 == 3
        FAIL [Symbol]55: GBP/USD != GBP/XXX
        OK   [Symbol]55: AUD/USD == AUD/USD
        OK   [Symbol]55: USD/JPY == USD/JPY
    MESSAGE RECEIVED::
        ------------------------------------------------------
        MarketDataRequest
        ------------------------------------------------------
        Header
            [BeginString]8=FIX.4.4
            [BodyLength]9=156
            [MsgSeqNum]34=2
            [MsgType]35=V[MARKETDATAREQUEST]
            [SenderCompID]49=CLIENT_COMP_ID
            [SendingTime]52=20141222-22:28:57.286
            [TargetCompID]56=SERVER_COMP_ID
        Body
            [MDReqID]262=AASDJKG790
            [SubscriptionRequestType]263=0[SNAPSHOT]
            [MarketDepth]264=20
            [NoRelatedSym]146=3
              1.[Symbol]55=GBP/USD
              2.[Symbol]55=AUD/USD
              3.[Symbol]55=USD/JPY
            [NoMDEntryTypes]267=2
              1.[MDEntryType]269=0[BID]
              2.[MDEntryType]269=1[OFFER]
        Trailer
            [CheckSum]10=156
RECENT INBOUND MESSAGES
    1. [BeginString]8=FIX.4.4|[BodyLength]9=83|[MsgSeqNum]34=1|[MsgType]35=A[LOGON]|[SenderCompID]49=CLIENT_COMP_ID|[SendingTime]52=20141222-22:28:57.230|[TargetCompID]56=SERVER_COMP_ID|[EncryptMethod]98=0[NONE__OTHER]|[HeartBtInt]108=10|[CheckSum]10=134|
    2. [BeginString]8=FIX.4.4|[BodyLength]9=156|[MsgSeqNum]34=2|[MsgType]35=V[MARKETDATAREQUEST]|[SenderCompID]49=CLIENT_COMP_ID|[SendingTime]52=20141222-22:28:57.286|[TargetCompID]56=SERVER_COMP_ID|[MDReqID]262=AASDJKG790|[SubscriptionRequestType]263=0[SNAPSHOT]|[MarketDepth]264=20|[NoRelatedSym]146=3|[Symbol]55=GBP/USD|[Symbol]55=AUD/USD|[Symbol]55=USD/JPY|[NoMDEntryTypes]267=2|[MDEntryType]269=0[BID]|[MDEntryType]269=1[OFFER]|[CheckSum]10=156|
RECENT OUTBOUND MESSAGES
    1. [MsgType]35=X[MARKETDATAINCREMENTALREFRESH]|[MDReqID]262=requestABCD|[NoMDEntries]268=4|[MDUpdateAction]279=0[NEW]|[MDEntryType]269=0[BID]|[Symbol]55=AUD/USD|[MDEntryPx]270=1.12345|[MDUpdateAction]279=0[NEW]|[MDEntryType]269=1[OFFER]|[Symbol]55=AUD/USD|[MDEntryPx]270=1.12355|[MDUpdateAction]279=0[NEW]|[MDEntryType]269=0[BID]|[Symbol]55=AUD/USD|[MDEntryPx]270=1.12335|[MDUpdateAction]279=0[NEW]|[MDEntryType]269=1[OFFER]|[Symbol]55=AUD/USD|[MDEntryPx]270=1.12365|
    2. [MsgType]35=8[EXECUTIONREPORT]|[Symbol]55=CVS|[OrderID]37=ORD10001/03232009|[ClOrdID]11=ORD10001|[ExecID]17=12345678|[ExecType]150=2|[OrdStatus]39=2[FILLED]|[Side]54=1[BUY]|[OrderQty]38=1000|[OrdType]40=1[MARKET]|[TimeInForce]59=1[GOOD_TILL_CANCEL_GTC]|[LastPx]31=1.12355|[LastQty]32=1000|[CumQty]14=0|[AvgPx]6=0|[LeavesQty]151=0|[TransactTime]60=20070123-19:01:17|[Text]58=Fill|[LastMkt]30=N|[SecurityExchange]207=N|[SettlType]63=0[REGULAR__FX_SPOT_SETTLEMENT_T1_OR_T2_DEPENDING_ON_CURRENCY]|

Project Structure

module description
fix4j-assert-core Contains the core fix4j-assert code
fix4j-assert-all Include this artifact if you wish to use the 'default' fix4j-assert setup (QuickFix4j engine with FIX-5.0SP2)
fix4j-assert-examples Examples to browse at your lesuire.
fix4j-assert-acceptance Acceptance tests. These tests will attempt to bind to ports on the test machine.
fix4j-assert-integration Integration tests. Will not attempt to listen to any ports on the test machine.
fix4j-assert-quickfix Provides the QuickFix integration for fix4j-assert.
fix4j-assert-codegen Generates fix4j-assert FIX spec code from FIX xml specs/data-dictionaries.
fix4j-assert-fixspec-50sp2 fix4j-assert FIX spec code for the FIX-5.0SP2 data-dictionary.
fix4j-assert-testcommon Common library that fix4j-assert's own tests use. (You don't need to include this directly