karatelabs/karate

To add support for websocket

4M01 opened this issue · 6 comments

4M01 commented

Karate supports HTTP protocol, it would be great if it can support Websocket.

closing due to inactivity

@4M01 and others - reopening this, and we've made some progress adding websocket helpers to the code. can you provide some examples of test scenarios / requirements

4M01 commented

@ptrthomas I will try to provide some example for this by Sunday. Hope that is fine

commit message was not descriptive enough, so a few notes:

  • doc pending
  • not sure if websocket should be first-class keyword and if clients should be auto-closed at end of scenario
  • two new methods on karate JS API, listen() and signal()
  • the combination of JS and karate works very nicely for callbacks !

EDIT: for those looking for the current example (syntax will change slightly): https://github.com/intuit/karate/blob/cukexit/karate-demo/src/test/java/demo/websocket/websocket.feature

I think the new "native" support for an "await" has turned out really well.
the CDC example which includes a JMS message listener has been revamped in prev commit.

Feature: payment service

Background:
* def QueueConsumer = Java.type('mock.contract.QueueConsumer')
* def queue = new QueueConsumer(queueName)
* def handler = function(msg){ karate.signal(msg) }
* eval queue.listen(handler)
* url paymentServiceUrl + '/payments'

Scenario: create, get, update, list and delete payments
    Given request { amount: 5.67, description: 'test one' }
    When method post
    Then status 200
    And match response == { id: '#number', amount: 5.67, description: 'test one' }
    And def id = response.id
    * json shipment = karate.listen(5000)
    * print '### received:', shipment
    * match shipment == { paymentId: '#(id)', status: 'shipped' }

and something I just found out is that function(o){ } gets converted to java.util.function.Consumer<Object> via the magic of Nashorn / JVM JS interop !

which leads us to the code behind the queue.listen(handler) that you see in the Background above:

    public void listen(java.util.function.Consumer<String> handler) {
        setMessageListener(message -> {
            TextMessage tm = (TextMessage) message;
            try {
                handler.accept(tm.getText());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

and yes, instead of String you should be able to use more complex types as the function argument, and the usual rules of JS conversion (e.g. Map --> JSON) apply

the improved API is designed so the karate.listen(timeout) call will return the value passed to karate.signal(value) which makes things pretty elegant if I may say so

which gives us this beauty that can send as well as receive from a websocket connection while ignoring messages we are not interested in:

    * def handler = function(msg){ if (msg.startsWith('hello')) karate.signal(msg) }
    * def socket = karate.webSocket(demoBaseUrl + '/websocket', handler)
    * eval socket.send('Billie')
    * def result = karate.listen(5000)
    * match result == 'hello Billie !'

0.9.0 released