The Groovy language provides a lot of additions to existing Java classes that can make our jobs easier.
A couple of them we were able to take advantage of lately that use closures to make our coding simpler were:
Our use case: We have a service whose responsibility is to send "records" over a TCP/IP connection to a remote server.
Of course we want to include a test for this in our integration tests, but how can we do it? We cannot expect a client to leave a server available open to receive records sent by our tests. We could create our test server to accept and output the sent record.
We'll write a test server using Groovy that can be started at the beginning of the test run and shutdown at the end:
class TestServer {
static defaultPort = 6666
static ServerSocket start_on_default_port() {
start_on defaultPort
}
static ServerSocket start_on(port) {
def server = new ServerSocket(port)
String receivedRecord ''
def done = false
Thread.start { t ->
while (!done) {
server.accept { socket ->
socket.withStreams { input, output ->
done = false
while (!done) {
def block = new byte[128]
int size = input.read block
if (size == -1) done = true // there is nothing left to have been received
block = trim(block as List)
receivedRecord += new String(block)
}
}
}
}
}
server
}
/** Remove any empty or non-set bytes at the end of the array. */
private static def trim(List block) {
block.reverse().findAll { b -> b != 0 }.reverse() as byte[]
}
}
In our integration tests class, written with Spock, we start and stop the server at the beginning of the run:
@ContextConfiguration
class RecordDeliveryServiceIntegrationSpec extends Specification {
@Autowired
RecordDeliveryService service
static def server
def setupSpec() {
server = TestServer.start_on_default_port()
}
def cleanupSpec() {
server.close()
}
def 'delivers a record to a server'() {
expect:
expectedResponse == service.send 'localhost', TestServier.defaultPort, record
}
//...
}
For reference to complete the example, here's what a simplified service implementation could look like, using a methodology similar to that the test server uses to accept the record:
dclass RecordDeliveryService implements DeliveryService {
public def send(String host, long port, Record record) {
def socket = new Socket(host, port)
socket.withStreams { input, output ->
output << record.toString()
output.flush()
output.close()
}
socket.close()
}
} //...
}
If you can think of any improvements, or a better approach to the problem, I would be glad to hear from you!
No comments:
Post a Comment