Reloading Spring Application Context with Each Test

1,302 views
Skip to first unread message

Replicator

unread,
Oct 20, 2011, 7:30:24 PM10/20/11
to scalatest-users
In Java/JUnit/Spock/etc.. world this can be done by easily adding a
class level "DirtiesContext" annotation:

@DirtiesContext( classMode=ClassMode.AFTER_CLASS )
@ContextConfiguration( ...)
class SomeTest { ... ... }

In ScalaTest this annotation does nothing.

Spring tests run fine individually, but fail to run all together,
because testing involves AKKA actors defined in Spring configs, and
once test A stops that actor ( it has to ), test B, that runs after
the test A, gets: "Can't restart an actor that has been shut down with
'stop' or 'exit'" => would not happen, if the Spring context was
reloaded, and actors were recreated.

But this is not much about AKKA, as it is about reloading (fresh)
Spring context with each test.

Thank you!

Bill Venners

unread,
Oct 20, 2011, 7:37:37 PM10/20/11
to scalate...@googlegroups.com
Hi Replicator,

Unfortunately, I'm not very familiar with Spring. I looked at this:

http://static.springsource.org/spring/docs/3.1.0.RC1/javadoc-api/org/springframework/test/annotation/DirtiesContext.html

It looks like Spring may be assuming JUnit, or at least JUnit and
TestNG. But if Spock works that probably you need a JUnit RunWith
annotation. Did you try putting a RunWith(classOf[JUnitRunner])
annotation on your ScalaTest class, as shown here:

http://www.scalatest.org/scaladoc/1.6.1/#org.scalatest.junit.JUnitRunner

I think you'd need to ask JUnit to run your ScalaTest suites as well
instead of having ScalaTest do it solo. If you have trouble with that
let me know.

Bill

> --
> You received this message because you are subscribed to the Google
> Groups "scalatest-users" group.
> To post to this group, send email to scalate...@googlegroups.com
> To unsubscribe from this group, send email to
> scalatest-use...@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
>

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

Replicator

unread,
Oct 21, 2011, 9:10:21 AM10/21/11
to scalatest-users
Bill,

Thanks for a quick response.

I have tried @RunWith(classOf[JUnitRunner]) as well as
@RunWith(classOf[SpringJUnit4ClassRunner]), but it does not seem to do
it.

When you mention "JUnit to run your ScalaTest suites as well
instead of having ScalaTest do it solo" => do you mean all my test
will have to extend "JUnitSuite"? Would that mean I would need to
change the way the tests are written ( e.g. would not be able to use
WordSpec, etc.. )?

Thank you!


On Oct 20, 7:37 pm, Bill Venners <b...@artima.com> wrote:
> Hi Replicator,
>
> Unfortunately, I'm not very familiar with Spring. I looked at this:
>
> http://static.springsource.org/spring/docs/3.1.0.RC1/javadoc-api/org/...

Bill Venners

unread,
Oct 21, 2011, 11:02:13 AM10/21/11
to scalate...@googlegroups.com
Hi Replicator,

On Fri, Oct 21, 2011 at 6:10 AM, Replicator <weba...@gmail.com> wrote:
> Bill,
>
>    Thanks for a quick response.
>
>    I have tried @RunWith(classOf[JUnitRunner]) as well as
> @RunWith(classOf[SpringJUnit4ClassRunner]), but it does not seem to do
> it.
>
>    When you mention "JUnit to run your ScalaTest suites as well
> instead of having ScalaTest do it solo" => do you mean all my test
> will have to extend "JUnitSuite"? Would that mean I would need to
> change the way the tests are written ( e.g. would not be able to use
> WordSpec, etc..  )?
>

No need to extend JUnitSuite. What I mean is how you run it. There are
lots of ways to run, so I'm not sure how you are running it. But for
example from the command line, you can ask ScalaTest to run something
like this:

scala -cp scalatest-1.5.jar:junit-4.4.jar org.scalatest.tools.Runner
-p . -o -s ExampleSuite

Or JUnit to run it like this:

scala -cp .:scalatest-1.5.jar:junit-4.4.jar org.junit.runner.JUnitCore
ExampleSuite

Probably you're doing the latter somehow through Spring. How are you running?

The other thing, though, is if you use JUnitSuite, that's just a way
to write @Test methods in JUnit, so I would think for sure that should
work with Spring. But if you want to use something more native like
FreeSpec or FlatSpec, then JUnitSuite won't satisfy you.

Bill

Replicator

unread,
Oct 21, 2011, 11:32:22 AM10/21/11
to scalatest-users
Hi Bill,

Thanks for clarifying "JUnitSuite" confusion => I am using
WordSpec, I like it a lot better than "@Test"

I am running tests via "ScalaTestAntTask" ( Gradle ):

ant.taskdef( name: 'scalatest',
classname: 'org.scalatest.tools.ScalaTestAntTask',
classpath: sourceSets.test.runtimeClasspath.asPath
)

ant.scalatest( runpath: sourceSets.test.classesDir,
haltonfailure: 'true',
fork: 'false' ) {

reporter( type: 'stdout' ) }

Is there a way to provide a JUnit runner there? I see that
[java.setClassname("org.scalatest.tools.Runner")] is hardcoded in
"ScalaTestAntTask". Or should/can I try something else?

Thank you Bill!


On Oct 21, 11:02 am, Bill Venners <b...@artima.com> wrote:
> Hi Replicator,
>

Bill Venners

unread,
Oct 21, 2011, 1:36:58 PM10/21/11
to scalate...@googlegroups.com
Hi Replicator,

If you're using ScalaTestAntTask, then that's ScalaTest running the
tests. What I'd try instead is use the junit ant task to run them. I
don't know if that will fix your problem, because I don't know what
the Spring annotations are doing. But my guess was, that if it works
with Spock, then it should work with a JUnitRunner annotation on a
ScalaTest WordSpec. I may be wrong though, because Spock also has
tests as methods. It may be that Spring needs to see test methods and
test functions won't do. Try running a WordSpec through the JUnit ant
task and let me know if that works. If not, I'll dig deeper into the
Spring stuff.

Bill

Replicator

unread,
Oct 21, 2011, 3:27:39 PM10/21/11
to scalatest-users
Hi Bill,

Thanks for your thoughts. I don't want to change much of the build/
test piping, and switching from "ScalaTestAntTask" will require me to
do that, since that is the only sane way Gradle can integrate with
ScalaTest.

Instead, I implemented my own "ApplicationContextReloader" that I
call in "beforeAll":

override def beforeAll()
{ contextReloader.reinjectDependencies( this ) ... }

All it does it closes the context, and calls into
AutowireCapableBeanFactory to reinject the dependencies for the test
instance.

I also call "override def afterAll() { Actors.kill() .. }" to
ensure AKKA follows Spring's tests lifecycle.

What I would like to do now is to encapsulate these two in a trait
that I can enrich my Spring tests with:

e.g. class MySpringTest extends WordSpec with ... with
SpringAkkaLifecycle { ... }

What would be the best/cleanest ScalaTest way to do that? Things
to consider:

1. "contextReloader.reinjectDependencies( this )" needs to be
the first call in beforeAll()
2. "this" in above call needs to be the instance of the
actual test
3. context reloader is currently injected as
@Resource( name="refreshApplicationContext" ) ...

Thank you!



On Oct 21, 1:36 pm, Bill Venners <b...@artima.com> wrote:
> Hi Replicator,
>

Bill Venners

unread,
Oct 21, 2011, 3:54:28 PM10/21/11
to scalate...@googlegroups.com
Hi Replicator,

On Fri, Oct 21, 2011 at 12:27 PM, Replicator <weba...@gmail.com> wrote:
> Hi Bill,
>

I think you can just make a SpringAkkaLifecycle trait that extends
BeforeAndAfterAll and mix that into your WordSpec subclasses, and/or
you can make a trait that extends WordSpec and BeforeAndAfterAll and
then that becomes your SpringAkkaWordSpec. The tricky part is the
contextReloader. I'm guessing that's a ClassLoader. The first thing
I'd try is simply putting it into SpringAkkaLifecycle. You probably
have to annotate each test class with @Resource, rather than just
annotating the trait. That should work, but if not let me know and I
have another idea. It depends on how Spring works together with vars
brought in from traits.

import org.scalatest.Suite
import org.scalatest.BeforeAndAfterAll

trait SpringAkkaLifecycle extends BeforeAndAfterAll { this: Suite =>

var contextReloader: ClassLoader

override def beforeAll() {
// contextReloader.reinjectDependencies(this)
// ...
}

override def afterAll() {
// Actors.kill()
// ...
}
}

Also, not sure if you can make that var private and Spring can still
access it, but if so I'd make it private. You will need the "this:
Suite =>" self-type to get it to compile. It means this can only be
mixed into a org.scalatest.Suite (which WordSpec extends). Let me know
if you have any trouble getting this to work.

Replicator

unread,
Oct 21, 2011, 5:45:45 PM10/21/11
to scalatest-users
"contextReloader" is not a classloader, but rather a simple component
that I created. It expends "ApplicationContextAware", which means it
gets a reference to the Spring application context => from which I
derive an "AutowireCapableBeanFactory" to reinject the dependencies.

I really appreciate your thoughts and examples. I'll try to polish it
up with a "SpringAkkaLifecycle" trait, and'll let you know how it
goes.

Thank you Bill!
> > For more options, visit this group at...
>
> read more »
Reply all
Reply to author
Forward
0 new messages