WebSocket handling #3221
|
I'm looking for guidance on how to properly handle WebSocket messages in Serenity/JS. Let’s say I send a WebSocket message in an Interaction with a specific ID. Then, in a separate Question, I wait for the corresponding response message. The problem is:
One possible solution would be introducing a message cache that stores incoming WebSocket messages so they can be matched later. However, I’m wondering whether this is the right architectural approach. For example, CallAnApi's Send interaction waits for the HTTP response internally. Would it make sense for a SendWebSocketMessage interaction to also wait for the matching response message? The complication is that the matcher logic is not always a simple ID comparison. Sometimes the matching criteria are more complex, which would require passing a matcher function into the Task. That starts to feel like an antipattern, since it mixes orchestration logic with message-matching logic. What is the recommended way in Serenity/JS to:
|
Replies: 1 comment 1 reply
|
That sounds like a good candidate for a custom In Serenity/JS, abilities are the right place to encapsulate any state or integrations your actors need. Instead of trying to pass messages around indirectly, you can give the actor an ability that records incoming messages, and then have your 1. Custom abilityimport { Ability } from '@serenity-js/core';
export class ReceiveMessages extends Ability {
private messages: any[] = [];
static using(source: SomeMessageSource) {
const ability = new ReceiveMessages();
source.onMessage(message => ability.record(message));
return ability;
}
private record(message: any) {
this.messages.push(message);
}
latest() {
return this.messages[this.messages.length - 1];
}
all() {
return [...this.messages];
}
}The key idea is that this ability becomes the single place responsible for collecting messages.
actorCalled('Alice').whoCan(
ReceiveMessages.using(myMessageSource),
);
import { Question } from '@serenity-js/core';
export const LastReceivedMessage = () =>
Question.about('last received message', actor =>
ReceiveMessages.as(actor).latest()
);
Why this works well
If your use case involves filtering (e.g. “message with type X”), you can extend the ability with query methods or keep the filtering logic in the Question, depending on how reusable you want it to be. |
That sounds like a good candidate for a custom
Ability.In Serenity/JS, abilities are the right place to encapsulate any state or integrations your actors need. Instead of trying to pass messages around indirectly, you can give the actor an ability that records incoming messages, and then have your
Questionread from it.1. Custom ability