I am sorry but I actually prefer the other style of specification! The main reason is that I think that encourages me to think about writing some text about what I am testing, and why I am testing it this way (that doesn't mean that I necessarily do it :-)).
I hear you and I indeed simplified all must
expressions to be a must b
, and then a must not(b)
.
I have added a bunch of Matcher.apply
functions but also simplified the Matcher
data type which does not longer
return a MatchResult[T]
but simply a Result
. The reason for having the intermediate MatchResult[T]
data type
was to keep the actual value around and to have both a success and a failure message which could be switched when
calling not(matcher)
.
It turns out that it is really hard to compose failure messages when trying to create matchers from other matchers so I gave up on that. I eventually think that it is easier to reuse the success/failure logic of other matchers but draft your own message for a custom matcher.
specs2 now supports Future[T]
as the body of a test, as long as T
has a AsResult[T]
instance.
But more generally there is an AsExecution
typeclass which can be used to transform the body of any async example
using cats IO
or ZIO
to a specs2 Execution
. The cats IO
integration itself has now be put in a separate
specs2-cats project.
The support for ScalaCheck in specs2 is a bit convoluted and while I think it should be possible to do something for async properties I haven't tried yet.
I would need a concrete test project showing one of the issues. It seems to me that Discipline
is doing to the right thing by creating one example per property. Then everything else should behave like a normal
specification with sequential execution if sequential
is set etc...
The seed issue could be a bug due to the way I am capturing the actual seed being used to report it. That's something I can definitely investigate if you have a way to reliably reproduce the problem.
You can add / remove fragments even with a mutable specification. To do this you can override the inherited
map
or flatMap
functions:
/** modify the whole list of fragments */
def map(fs: =>Fragments): Fragments = fs
/** modify each fragment to expand it into a list of fragments */
def flatMap(f: Fragment): Fragments = Fragments(f)
Would that help for what you'd like to do?
This one might be a naming thing. pending
and skipped
are Results
while pendingUntilFixed
is a way to modify the execution of a Result
.
If that helps I have added the possibility to use the pendingUntilFixed
method as a prefix method
(also available in the next specs2-4.x version):
"example 1" >> pendingUntilFixed {
1 === 2
}
"example 1" >> pendingUntilFixed("ISSUE-123") {
1 === 2
}
Maybe this is "slightly saner" :-)?
Indeed before/after
methods are very "side-effecty". In this new version of specs2
there is a Resource[T]
trait
which can be used to declare a resource available to all the examples a specification.
For example you can mix the following trait to your specification:
trait DbConnection(using ec: ExecutionContext) extends Resource[Connection]:
def acquire: Future[Connection] =
??? // create a new connection
def release(connection: Connection): Execution = Execution.future {
connection.close.map {
case Right(_) => success
case Left(e) => anError(e)
}
}
class MySpec(using ec: ExecutionContext) extends mutable.Specification, DbConnection:
"use a database connection" >> { (c: Connection) =>
c.executeSql("SELECT COUNT(*) FROM TABLE").map { n => n must be_>=(10) }
}
You can even share that resource across several specifications by giving it a resource key:
trait DbConnection(using ec: ExecutionContext) extends Resource[Connection]:
override def resourceKey: Option[String] =
Some("DB connection")
def acquire: Future[Connection] =
...
def release(connection: Connection): Execution =
...
Then the release
method will only be called at the end of a run.