Test Driven app development with streamhub?

5 views
Skip to first unread message

wayneseymour

unread,
Nov 25, 2009, 9:01:30 AM11/25/09
to StreamHub Comet Server Community
Hello All,

I am working on a solution that uses streamhub for communication
between web apps.
Before I get too far with it, I'd like to ask if anyone has info on
"tdd-ing" it.
Bye the way, I'm working with the java adapter, soon to be working
with the gwt adapter once I solve all my business logic problems.

Specifically, I want to test my override for onSubscribe(). Right
now, when I run any tests that activate the PushServer, it ties up my
test thread because its a long running thread like any web server.

I am looking for a way to interact with the StreamHub server via
jUnit, kinda like this:

<fake_code>

@Test
public void onSubscribe() throws Exception {
MyStreamHubServer server = new MyStreamHubServer();
assertNotNull("# server didnt come up", server");

MyMockClient client = new MyMockClient();
client.connect(SERVER INFO);
client.subscribe(TOPIC INFO);
}

</fake_code>

Basically, I am trying to mock the client side code from java.
I am working on the idea of channels, which I hope is somewhere close
to the way google lets multiple users modify google docs in the cloud
at the same time.

Finally, any help will be appreciated, and please excuse my lack of
good junit 4.5 tdd skills.

Thanks folks.

StreamHub Team

unread,
Dec 29, 2009, 8:37:36 AM12/29/09
to StreamHub Comet Server Community
Here's some information on testing StreamHub from Java, might be
useful:

We often write a lot of tests using Selenium and JUnit 4. So we start
up a StreamHub server, using the @BeforeClass and @AfterClass Junit 4
annotations to start and stop the server before and after all the test
cases. Here's a TestSubscriptionListener class we commonly use:

public class TestSubscriptionListener implements SubscriptionListener
{
private static final Logger log = Logger.getLogger
(TestSubscriptionListener.class);
private final PushServer streamingServer;
private Map<String, Payload> topicToPayload;
private List<String> unsubscribed = new ArrayList<String>();
private List<String> subscribed = new ArrayList<String>();

public TestSubscriptionListener(PushServer streamingServer) {
this.streamingServer = streamingServer;
SubscriptionManager subscriptionManager =
streamingServer.getSubscriptionManager();
subscriptionManager.addSubscriptionListener(this);
}

public void onSubscribe(final String topic, Client client) {
subscribed.add(topic);
Payload payload = topicToPayload.get(topic);

if (payload != null) {
client.send(topic, payload);
} else {
log.warn("No response for topic: '" + topic + "'");
}
}

public void onUnSubscribe(String topic, Client client) {
unsubscribed.add(topic);
}

public void setSubscriptionResponses(Map<String, Payload>
topicToPayload) {
this.topicToPayload = topicToPayload;
}

public void publish(String topic, Payload payload) {
streamingServer.publish(topic, payload);
}

public boolean hasUnSubscribed(String topic) {
return unsubscribed.contains(topic);
}

public boolean hasSubscribed(String topic) {
return subscribed.contains(topic);
}

public void clear() {
unsubscribed.clear();
subscribed.clear();
}
}

This allows us to check whether a client has subscribed/unsubscribed
and setup canned subscription responses (via setSubscriptionResponses)
that we can assert expectations on. Its essentially a class for
setting up and returning test data. For this kind of setup you will
need an actual client to subscribe to data - so its more of an
acceptance/integration test than a unit test. We script the Ajax SDK
via a page we add to the server via NIOServer.addStaticContent(...),
but you could also use the Java Client SDK which would save you using
selenium. The Java Client SDK is available here:
http://www.stream-hub.com/downloads/streamhub-java-client-sdk-1.0.22.zip.
If you want to use Selenium the following page is really useful:

<html>
<head>
<title>Test Page</title>
<style type="text/css">
div {
font-family: Arial;
font-size: 10pt;
}
</style>
</head>
<body>
<h3>Test page</h3>

<p>Server URL: http://127.0.0.1:7654/ <input type="text" id="url"
value="http://127.0.0.1:8888/"></p>
<p>
<input type="button" id="connect" value="Connect" onclick="runTest()">
<input type="button" id="disconnect" value="Disconnect"
onclick="disconnect()">
<input type="button" id="clearText" value="Clear Text"
onclick="clearText()">
<input type="button" id="turnLoggingOff" value="Turn Logging Off"
onclick="turnLoggingOff()">
<input type="button" id="turnLoggingOn" value="Turn Logging On"
onclick="turnLoggingOn()">
<input type="button" id="switchToCountLogger" value="Switch To Count
Logger" onclick="switchToCountLogger()">
<input type="button" id="addConnectionListener" value="Add Connection
Listener" onclick="addConnectionListener()">
</p>
<p>
Proxy base URL: <input type="text" id="proxyRootUrl" value="http://
127.0.0.1:8156">
<input type="button" id="stopProxy" value="Stop Proxy"
onclick="stopProxy()">
<input type="button" id="startProxy" value="Start Proxy"
onclick="startProxy()">
</p>
<p>
Topic: <input type="text" id="topic" value="AAPL">
<input type="button" id="subscribe" value="Subscribe"
onclick="subscribe()">
</p>
<p>
Json: <input type="text" id="json" value="{'key':'value'}">
<input type="button" id="publish" value="Publish" onclick="publish()">
</p>
<p>
Server List: <input type="text" id="serverList" value="http://
localhost.stream-hub.com:7654/,http://localhost.stream-hub.com:8888/"
size="50">
<select id="failoverAlgorithm">
<option value="ordered">ordered</option>
<option value="priority">priority</option>
<option value="random">random</option>
</select>
<input type="button" id="connectAdvanced" value="Connect Advanced"
onclick="runTestAdvanced()"><br/>
Initial Reconnect Delay: <input type="text"
id="initialReconnectDelayMillis" value="1000" size="5">
Max Reconnect Delay: <input type="text" id="maxReconnectDelayMillis"
value="-1" size="5">
Max Reconnect Attempts: <input type="text" id="maxReconnectAttempts"
value="-1" size="2"><br/>
Exponential BackOff: <input type="checkbox" id="useExponentialBackOff"
value="useExponentialBackOff">
BackOff Multiplier: <input type="text" id="backOffMultiplier"
value="0" size="3">

</p>
<div id="logMessages"></div>

<script type="text/javascript" src="javascript/XMLHttpRequest.js"></
script>
<script type="text/javascript" src="javascript/streamhub.js"></script>
<script type="text/javascript">
var oLogger = new StreamHub.ElementLogger({elementId :
"logMessages"});

hub = new StreamHub();
hub.setLogger(oLogger);

function runTest() {
var sServerUrl = document.getElementById("url").value;
hub.connect({
serverList: [sServerUrl],
staticUID: "1"
});
}

function runTestAdvanced() {
var sServerList = document.getElementById("serverList").value;
var pServerList = sServerList.split(",");
var nInitialReconnectDelayMillis = parseInt(document.getElementById
("initialReconnectDelayMillis").value);
var nMaxReconnectDelayMillis = parseInt(document.getElementById
("maxReconnectDelayMillis").value);
var nMaxReconnectAttempts = parseInt(document.getElementById
("maxReconnectAttempts").value);
var bUseExponentialBackOff = (document.getElementById
("useExponentialBackOff").checked == true);
var nBackOffMultiplier = parseFloat(document.getElementById
("backOffMultiplier").value);
var eFailoverAlgorithm = document.getElementById
("failoverAlgorithm");
var sFailoverAlgorithm = eFailoverAlgorithm.options
[eFailoverAlgorithm.selectedIndex].value;
hub.connect({
serverList: pServerList,
initialReconnectDelayMillis: nInitialReconnectDelayMillis,
maxReconnectDelayMillis: nMaxReconnectDelayMillis,
maxReconnectAttempts: nMaxReconnectAttempts,
useExponentialBackOff: bUseExponentialBackOff,
backOffMultiplier: nBackOffMultiplier,
failoverAlgorithm: sFailoverAlgorithm,
staticUID: "1"
});
}

function disconnect() {
if (window.hub) {
hub.disconnect();
}
}

function clearText() {
var eLogDiv = document.getElementById("logMessages");
eLogDiv.innerHTML = "";
}

function startProxy() {
var sProxyRootUrl = document.getElementById("proxyRootUrl").value;
asyncGet(sProxyRootUrl + "/proxy/start");
}

function stopProxy() {
var sProxyRootUrl = document.getElementById("proxyRootUrl").value;
asyncGet(sProxyRootUrl + "/proxy/stop");
}

function asyncGet(sUrl) {
var oXMLHttpRequest = new XMLHttpRequest();
oXMLHttpRequest.open("GET", sUrl, true);
oXMLHttpRequest.onreadystatechange = function() {
if (this.readyState == XMLHttpRequest.DONE) {
oLogger.log("XHR: [" + this.responseText + "]");
}
}
oXMLHttpRequest.send(null);
}

function priceChangeListener(sTopic, oData) {
oLogger.log("priceChangeListener response for topic '" + sTopic + "'
is Name: '" + oData.Name + "' Last: '" + oData.Last + "'");
}

function subscribe() {
var sTopic = document.getElementById("topic").value;
hub.subscribe(sTopic, priceChangeListener);
}

function publish() {
var sTopic = document.getElementById("topic").value;
var sJson = document.getElementById("json").value;
hub.publish(sTopic, sJson);
}

function turnLoggingOff() {
hub.setLogger(new StreamHub.NullLogger());
}

function turnLoggingOn() {
hub.setLogger(oLogger);
}

function switchToCountLogger() {
hub.setLogger(new StreamHub.CountLogger({elementId :
"logMessages"}));
}

function addConnectionListener() {
var connectionListener = new StreamHub.ConnectionListener();
connectionListener.onConnectionEstablished = function () {
oLogger.log("ConnectionListener: Connection Established");
};
connectionListener.onConnectionLost = function () {
oLogger.log("ConnectionListener: Connection Lost");
};
hub.addConnectionListener(connectionListener);
}
</script>
</body>
</html>

The proxy stuff ("/proxy/start") is a RESTful servlet we use for
testing connection-loss scenarios, you might not need it.

All that should give you a good integration/acceptance testing
framework. But if you're after more of a unit testing framework,
there are a lot of public interfaces that you can mock via a mocking
framework such as JMock or EZMock. The main interfaces you'll need
are PushServer, Client, SubscriptionListener and PublishListener. The
main thing you'll probably want to test is the interaction with your
'streamer' or the class that will provide data through the server.

Hope this helps, let us know if you want anything more specific.

Reply all
Reply to author
Forward
0 new messages