19
Asynchronen Code testen @ndrssmn Andreas Simon

Asynchronen Code testen

  • Upload
    ndrssmn

  • View
    138

  • Download
    1

Embed Size (px)

DESCRIPTION

Viele performante und gut skalierbare Architekturen setzen auf asynchrone Verarbeitung. Das Testen des asynchronen Codes stellt Entwickler allerdings vor neue Herausforderungen. Dieser Vortrag bietet Orientierung für einige typischen Fragestellungen. Am Beispiel von NodeJS und Mocha wird gezeigt, wie das Testen beim Einsatz einer nicht-blockierenden Event Loop funktioniert. Anschließend illustriert der Vortrag, wie asynchroner Code mit JUnit auf der JVM – einer klassischen Multithreading-Plattform – getestet werden kann. Insbesondere wird darauf eingegangen, welche Synchronisationsmechanismen genutzt werden können und wie Race Conditions durch Unit Tests aufgedeckt werden können. Code unter: https://github.com/andreassimon/talk-asynchronen-code-testen

Citation preview

Page 1: Asynchronen Code testen

AsynchronenCode testen

@ndrssmnAndreas Simon

Page 2: Asynchronen Code testen

Synchron

Übertragungszeit

War

teze

it

Bearbeitungszeit

War

teze

itAnt

wor

tzei

t

Page 3: Asynchronen Code testen

AsynchronW

arte

zeit

Ant

wor

tzei

t

Page 4: Asynchronen Code testen

FehlertoleranzVerfügbarkeitParallelisierungPerformance

Event-Driven Architecture

Page 5: Asynchronen Code testen

Listening

Callback

Page 6: Asynchronen Code testen
Page 7: Asynchronen Code testen

Synchronisation

TIMEOUT

Page 8: Asynchronen Code testen

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)); }

Page 9: Asynchronen Code testen

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'} ); }); }); });

Page 10: Asynchronen Code testen

Sampling

POST localhost/

CREATED Location: localhost/30

TIMEOUT

GET localhost/30

NOT FOUND

GET localhost/30

NOT FOUND

GET localhost/30

OK :: 832040

Page 11: Asynchronen Code testen

@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

Page 12: Asynchronen Code testen

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);}

Page 13: Asynchronen Code testen

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

Page 14: Asynchronen Code testen

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

Page 15: Asynchronen Code testen

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)) ); }

Page 16: Asynchronen Code testen

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); } } }

Page 17: Asynchronen Code testen

Aufräumen

@After public void tearDown() throws InterruptedException { executorService.shutdownNow(); scheduler.shutdownNow(); }

Page 18: Asynchronen Code testen

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

Page 19: 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

[email protected]://www.quagilis.de