class NestedSpec extends Spec with BeforeAndAfter {
describe("First level") {
before {
// initialize first level
}
describe("Second level") {
before {
// initialize second level
}
}
}
}
Unfortunately doing so with ScalaTest-1.6.1 gives this runtime error:
org.scalatest.NotAllowedException: You are only allowed to call before
once in each Suite that mixes in BeforeAndAfter.
Poking around the source for BeforeAndAfter:
I notice that the BeforeAndAfter trait uses a single reference to the
before function.
Is there any reason why BeforeAndAfter couldn't support multiple
before and after blocks, each nested in its own describe block?
Thanks,
Rich
Interesting request. Yes, before is just designed to be used at the
top level. A before invocation registers code you want to run before
each test in the suite (or spec). I have had two other people suggest
an enhancement for before, but not the one you are talking about.
Others wanted to be able to stack before invocations like you can
stack beforeEach in BeforeAndAfterEach. BeforeAndAfter is really just
a replacement for JUnit's setup and teardown that has minimal
boilerplate, but that can't (currently at least) be stacked. If you
don't know what I mean by "stacking," it is described here:
http://www.scalatest.org/user_guide/sharing_fixtures#composingFixtures
Can I ask whether you'd want each test to see fresh copies of all the
mutations made in the before clauses? For example:
class StackSpec extends Spec with MustMatchers {
val stack = new scala.collection.mutable.Stack[String]
describe("A stack") {
describe("when empty") {
it("is empty") {
stack must be ('empty)
}
it("complains on pop") {
evaluating { stack.pop() } must produce [NoSuchElementException]
}
}
describe("when two objects have been pushed onto a stack") {
before {
stack.push("pushed first")
stack.push("pushed last")
}
it("should pop the last value pushed first") {
val poppedFirst = stack.pop()
poppedFirst must be ("pushed last")
}
// For this test to work, it must not see side effects of the previous test
it("should pop the first object pushed second") {
stack.pop()
val poppedLast = stack.pop()
poppedLast must be ("pushed first")
}
// For this test to work, it must not see side effects of the
previous two tests
it("should again be empty after popping both pushed objects") {
stack.pop()
stack.pop()
stack must be 'empty
}
}
}
Would you expect it to work the way shown above, with that kind of
isolation, or would you not expect or prefer that? By the way, what
other test frameworks are you talking about when you say "other BDD
frameworks." Does RSpec do this perhaps? Do they isolate variables
like I described?
Thanks.
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
Well then you're the second official request for that sort of
behavior. The first one came in about a year ago. I did a lot of
thinking about it, my main concern being it might be too non-obvious
what was going on behind the scenes. Ultimately decided it was
worthwhile to support so long as I could come up with a way that makes
the execution model easy to understand. What I came up with is three
separate style classes (not traits) in a new package,
org.scalatest.path, that exhibited this behavior. The three would
correspond to the three style traits that allow nesting: Spec,
FreeSpec, and WordSpec.
The special way these would execute is that they by definition would
run each test in its own instance of the class (extending
OneInstancePerTest), but would only execute the "path" to the test and
nothing else. This is like your statement, "Every 'it' block should
start with a clean scope built up from successively applying any
'before' blocks defined above it," except in this case, you wouldn't
need to say "before," and "after" would work as well. If you place a
statement textually before a test, it will execute before. If you
place a statement textually after a test, it will execute after.
Anything that does not appear textually before or after a test will
*not* be executed in the instance that executes that test.
For this to work, the initial instance of the class must know to
record all the paths, and the subsequent instances (one per test) must
execute just one path (so they need to know which path to execute).
The most obvious way to get that path to the instance is to pass it
into a constructor, which is why these would be style *classes*
instead of traits.
The code would look like:
import org.scalatest.path
import org.scalatest.matchers.MustMatchers
class StackSpec(p: Path = All) extends path.Spec(p) with MustMatchers {
describe("A stack") {
val stack = new scala.collection.mutable.Stack[String]
describe("when two objects have been pushed onto a stack") {
stack.push("pushed first")
stack.push("pushed last")
it("should pop the last value pushed first") {
val poppedFirst = stack.pop()
poppedFirst must be ("pushed last")
}
it("should pop the first object pushed second") {
stack.pop()
val poppedLast = stack.pop()
poppedLast must be ("pushed first")
}
it("should again be empty after popping both pushed objects") {
stack.pop()
stack.pop()
stack must be 'empty
}
}
describe("when empty") {
it("is empty") {
stack must be ('empty)
}
it("complains on pop") {
evaluating { stack.pop() } must produce [NoSuchElementException]
}
}
}
}
Would this approach work for you?
Bill
On Thu, Dec 1, 2011 at 10:37 AM, Richard Apodaca
<rapo...@metamolecular.com> wrote:
> Hello Bill,
>
> I hadn't thought of eliminating 'before' blocks... now that I have, I kind
> of like it.
>
As far as I know, I think this approach was first suggested for Scala
testing by Esko Luontola. He created the specsy framework to do
something like this. You may want to take a look at that as well:
https://github.com/orfjackal/specsy
> And yes, the rest of the approach you've outlined is spot-on with how I'd
> want to use ScalaTest.
>
> I'm not sure about the beginning of the class declaration:
>
> class StackSpec(p: Path = All)
>
> I'm new to Scala, but it looks like 'p' is not used in StackSpec. Is there
> any way a ScalaTest user would use the Path when writing a test? If not, it
> should ideally not be seen in StackSpec.
>
The p is used only to pass the Path up to the superclass Spec:
class StackSpec(p: Path = All) extends Spec(p) { ...
It is relatively ugly boilerplate. There's a way to get rid of it,
which would also allow me to use a trait not a class, but it would
mean that there would be an exception to the rule that "if you want it
to happen before, put it before; if you want it to happen after, put
it after". I'd have to tack on, "*unless* you put it at the top level,
in which case it will happen before the test, even if you place it
after." At least I couldn't figure out a way to do it without that
caveat. So choosing between boilerplate and caveat, I thought it
better to go with boilerplate, because it makes the behavior more
consistent and I think may help people understand the execution model.
They can see a path is being passed up to the supertype, with the
default of All. That means the first time it is instantiated, it will
execute all paths to discover them, then it will pass a specific path
to each instance created to run a test. That was my thought anyway.
And there may be some way to do it that gives the best of both worlds
that I haven't thought of yet. Once I got to implement it, I may learn
more. If anyone has some other ideas let me know.
Thanks.
Bill
Hi Richard,On Thu, Dec 1, 2011 at 10:37 AM, Richard Apodaca
<rapo...@metamolecular.com> wrote:
> Hello Bill,
>
> I hadn't thought of eliminating 'before' blocks... now that I have, I kind
> of like it.
>
As far as I know, I think this approach was first suggested for Scala
testing by Esko Luontola. He created the specsy framework to do
something like this. You may want to take a look at that as well:
The p is used only to pass the Path up to the superclass Spec:
class StackSpec(p: Path = All) extends Spec(p) { ...
It is relatively ugly boilerplate.
The p is used only to pass the Path up to the superclass Spec:
class StackSpec(p: Path = All) extends Spec(p) { ...
It is relatively ugly boilerplate. There's a way to get rid of it,
which would also allow me to use a trait not a class, but it would
mean that there would be an exception to the rule that "if you want it
to happen before, put it before; if you want it to happen after, put
it after". I'd have to tack on, "*unless* you put it at the top level,
in which case it will happen before the test, even if you place it
after." At least I couldn't figure out a way to do it without that
caveat.
--
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
I'm heading to the airport and will be flying most of the day. When I get back online I will follow Esko's links. My guess (as I'm unable to look at the code yet) Esko is that you are using a "global" thread local variable that your Spec traits look at to determine the path. Is that correct?
--
To post to this group, send email to scalatest-users@googlegroups.com
To unsubscribe from this group, send email to
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 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.
Based on that approach it would appear I cannot perform before/after each/all. In the absence of such things I would be unable to keep code dry. Am I missing something?
--
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 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.
var x = 3
describe("some other things that just need an appropriate value for x") {
...
}
describe("when x is huge") {
x = 1000
it("does asdf") { .... }
it("does qwer") { .... }
describe("when y is small") {
y = 1
it("...") { ...}
}
describe("when y is huge") {
y = 1000
...
}
}
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 the Google Groups "scalatest-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scalate...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.