Post on 13-Dec-2014
description
AsynchronenCode testen
@ndrssmnAndreas Simon
Synchron
Übertragungszeit
War
teze
it
Bearbeitungszeit
War
teze
itAnt
wor
tzei
t
AsynchronW
arte
zeit
Ant
wor
tzei
t
FehlertoleranzVerfügbarkeitParallelisierungPerformance
Event-Driven Architecture
Listening
Callback
Synchronisation
TIMEOUT
Listening in JUnit
@Test public void
should_reply_with_Fibonacci_numbers() throws Exception { // Arrange NotificationTrace<Integer> trace = new NotificationTrace<>(TIMEOUT); String replyQueue = channel.queueDeclare().getQueue(); FibonacciCalculator.create(connection.createChannel());
new IntegerConsumer( connection.createChannel(), trace::append) .consumeQueue(replyQueue);
// Act publishNumbers(MIN, MAX, replyQueue);
// Assert trace.containsNotification(equalTo(FIB_MIN)); trace.containsNotification(equalTo(FIB_MAX)); }
Listening in Mocha
describe('AMQP Fibonacci service', function() { it('calculates fib(' + MIN + ')', function(done) { connection.on('ready', function () { connection.queue('my-queue', function(q) { q.subscribe(function (message) { try { // Assert message.data.toString().should.eql(FIB_MIN); done(); } catch(e) { done(e); } });
// Act connection.publish( 'calculate-fibonacci', MIN, { replyTo: 'my-queue'} ); }); }); });
Sampling
POST localhost/
CREATED Location: localhost/30
TIMEOUT
GET localhost/30
NOT FOUND
GET localhost/30
NOT FOUND
GET localhost/30
OK :: 832040
@Test public void calculates_fib_30() throws Exception { // Act connection = POST("http://localhost:3000/", "30"); fibLocation = connection.getHeaderField("Location");
// Assert Probe probe = responseTo( fibLocation, equalTo(Integer.toString(FIB_30)) ); new Poller(TIMEOUT, POLL_DELAY).check(probe); }
Sampling in JUnit
public class Poller { […] public void check(Probe probe) { […] while (!probe.isSatisfied()) { […] Thread.sleep(pollDelayMillis); probe.sample(); } }}
public interface Probe { void sample(); boolean isSatisfied(); void describeAcceptanceCriteriaTo(Description d); void describeFailureTo(Description d);}
describe('Fibonacci server', function() { it('should calculate fib(20)', function(done) { var req = http.request(POST_fib, function(res) { res.setEncoding('utf8'); res.statusCode.should.eql(201); res.headers.location.should.be.ok; pollGET(res.headers.location, done); }).on('error', done);
req.setHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8'); req.write('n=20\n'); req.end(); });
Sampling in Mocha
function pollGET(url, done) { http.get(url, function(res) { if(200 != res.statusCode) { setTimeout(pollGET, POLL_DELAY, url, done); } res.on('data', function (chunk) { chunk.toString().should.eql('6765'); done(); }); }).on('error', done);}
Sampling in Mocha
Test the test @Test public void is_thread_safe() throws Exception { latch = startStressing(STRESSING_THREADS, () -> { for (int i = 0; i < ITERATIONS; i++) { trace.append("NOT-WANTED"); Thread.sleep(SLEEPTIME); } latch.countDown(); }); scheduler.schedule( () -> trace.append("WANTED"), 100, TimeUnit.MILLISECONDS );
trace.containsNotification(equalTo("WANTED")); latch.await(); assertThat( trace.getAppendCount(), is(equalTo((long) STRESSING_THREADS * ITERATIONS + 1)) ); }
Thread-sicher implementierenpublic class NotificationTrace<T> {
public void append(T message) { synchronized (traceLock) { trace.add(message); traceLock.notifyAll(); } }
public void containsNotification(Matcher<? super T> criteria) throws AssertionError, InterruptedException { Timeout timeout = new Timeout(timeoutMs);
synchronized (traceLock) { stream = new NotificationStream<>(trace, criteria); while (!stream.hasMatched()) { if (timeout.hasTimedOut()) { throw new AssertionError(); } timeout.waitOn(traceLock); } } }
Aufräumen
@After public void tearDown() throws InterruptedException { executorService.shutdownNow(); scheduler.shutdownNow(); }
Fazit
Listening vs. Sampling
Synchronisierungsmechanismen kapseln (und durch Unit-Tests validieren)
Brian Goetz: "Java Concurrency in Practice"
Nat Pryce, Steve Freeman: "Growing Object-Oriented Software"
https://github.com/andreassimon/talk-asynchronen-code-testen
Quality in Agile.
QuagilisAndreas Simon
Lazarettstr. 948147 Münster
Fon +49 (0) 251 - 590 491 55-0Fax +49 (0) 251 - 590 491 55-9
a.simon@quagilis.dehttp://www.quagilis.de