Executing asynchronous code in test setup

248 views
Skip to first unread message

ya...@m-innovincy.com

unread,
Oct 21, 2016, 12:19:52 AM10/21/16
to scalatest-users
Hi,

I'm migrating to ScalaTest 3.0, with the main target to take advantage of the new async scheme.
My problem is that in some of my tests, the setup of the test is itself asynchronous (because, e.g., the setup deletes a MongoDB collection, which is an asynchronous operation because I use Reactive Mongo).
Previously I used to block in the setup code, and I can do the same in 3.0 as well, but I am hoping there's a way in which I can include setup code that will run truly asynchronously.
Is there such a way?

Thanks
Yaniv

ya...@m-innovincy.com

unread,
Oct 23, 2016, 9:30:14 AM10/23/16
to scalatest-users

UPDATE: I think I found a way to overcome this problem, by overriding withFixture, mapping the Future of my setup code to the result of super.withFixture, and returning a new FutureOutcome.
It seems to work, however I'm not sure it'll always work, or if it's a good idea to call super.withFixture from a within a different thread.
It also feels very boilerplate-ish, although I can easily extract this to a trait if this is the best way to go..
See below an example code. 

package com.mi.infra.spec.async

import org.scalatest.{FutureOutcome, Matchers, AsyncFunSpec}
import scala.concurrent.Future

class AsyncSetupTest extends AsyncFunSpec with Matchers {

override def withFixture(test: NoArgAsyncTest): FutureOutcome = {

val futureTestResult = Future {
// In the real world this would be an asynchronous operation,
// e.g., dropping a database using a reactive API
println("Dropping a table")

} flatMap { result =>
super.withFixture(test).toFuture
}
new FutureOutcome(futureTestResult)
}

describe("Just a test test") {
it ("should succeed in the future") {
Future {
// In the real world this would be an asynchronous operation,
// e.g., querying a database using a reactive API
println("Query DB")

} map { _ =>
true shouldBe true
}
}
}
}

Saad Malik

unread,
Nov 20, 2016, 5:34:32 PM11/20/16
to scalatest-users
Thanks, this worked for me too! Out of curiosity, did you find a cleaner solution?

Yaniv Ben-Yosef

unread,
Nov 21, 2016, 4:44:07 AM11/21/16
to scalate...@googlegroups.com
I'm afraid not yet. Like I said it's possible to make it cleaner by introducing a trait, perhaps call it AsyncBefore or maybe even AsyncBeforeAndAfter to make it even more useful. Underneath it will work the same way as introduced in the previous email, but won't require the test class to override withFixture directly.
I will probably do that the next time I have the need, and will share the code.
Also I'm hoping to get feedback from someone who knows the internals for scalatest more deeply. If this is a viable option I will issue a pull request.


--
You received this message because you are subscribed to the Google
Groups "scalatest-users" group.
To post to this group, send email to scalatest-users@googlegroups.com
To unsubscribe from this group, send email to
scalatest-users+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/scalatest-users?hl=en
ScalaTest itself, and documentation, is available here:
http://www.artima.com/scalatest
---
You received this message because you are subscribed to a topic in the Google Groups "scalatest-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scalatest-users/G8PTv8n9tHM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scalatest-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Best regards,

Yaniv Ben-Yosef
CTO, Milan InnoVincY B.V.
          
M +972(54)670 9743 | S yaniv.benyosef | E yby...@m-innovincy.com 
The Netherlands | Kapteynstraat 1 | 2201BB NOORDWIJK
Rwanda | AgriLift Ltd | KG 536 Axiom Building, behind Telecom House | KIGALI 

Confidential - Do not duplicate or distribute without written permission from Milan Innovincy B.V

© Milan Innovincy B.V, 2015. Any third party right are hereby acknowledged. All rights reserved. 


Please consider the environment – do you really need to print this email?

Bill Venners

unread,
Nov 21, 2016, 11:04:51 AM11/21/16
to scalate...@googlegroups.com
Hi Yaniv,

Just to give you some context, overriding withFixture is intended to be the default way to setup and teardown fixtures in any kind of ScalaTest style trait. It is the one way that doesn't require an explicit mix in. To do beforeEach/afterEach or before/after, you need to mix in a trait. That approach is there mostly because people are used to that coming from xUnit, but also because you may want suites to abort when setup/teardown rails rather than tests to fail. Before & after happen before and after the test, whereas withFixture happens during the test. So any kind of failure that happens with setup and teardown inside a withFixture causes the test to fail.

So you're on the right track with your withFixture solution. The only thing I would probably do differently is transform the Future[Unit] that represents the setup into a FutureOutcome, then map that. If you need to do cleanup, use complete-lastly, which is described here:


I'll do a sanity check tonight to make sure the types line up, but I think that should work. FutureOutcome is the type of withFixture in Async styles, so I'd get into that early on and stay there.

Bill

You received this message because you are subscribed to the Google Groups "scalatest-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scalatest-users+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Bill Venners
Artima, Inc.
http://www.artima.com

Yaniv Ben-Yosef

unread,
Apr 16, 2017, 6:40:51 AM4/16/17
to scalate...@googlegroups.com
Hi Bill,

It's been a while since you answered me, but thanks for your help so far.
I've been working with this implementation for a little while, and it seemed to be working correctly, however I now notice that I may have not tested this well enough.
I've created two example asynchronous suites, the first has a simple setup function (does nothing and returns a Future.successful), and the other suite does not call the setup function.

Please see the code in this Gist: 

Both suites have the same 4 tests, however ScalaTests reports them differently: in the first, the suite aborts after the 2nd test, while the second suite runs all tests and report them correctly.

Report with setup
Inline image 3

Report without setup
Inline image 2

My desired outcome (with and without setup) is that all my tests would run (given that the setup function has a successful outcome). If the setup fails, the suite should abort. But this is clearly not the case here.
I don't see what I'm doing wrong or what is an alternative approach.

Your help would be much appreciated,
Yaniv
 
Reply all
Reply to author
Forward
0 new messages