org.scalatest.FixtureAsyncTestSuite.scala Maven / Gradle / Ivy
/* * Copyright 2001-2014 Artima, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.scalatest import org.scalatest._ import scala.concurrent.Future // TODO: Scaladoc /** * The base trait of ScalaTest's "fixture" async testing styles, which enable you to pass fixture objects into tests. * *
Pending. This is * also true when* This trait provides a final override of
* *withFixture(OneArgTest)
, declared in * supertraitFixtureSuite
, because thewithFixture(OneArgTest)
lifecycle * method assumes synchronous testing. Here is its signature: ** def withFixture(test: OneArgTest): Outcome ** ** The test function interface,
* *OneArgTest
, offers anapply
method * that takes aFixtureParam
and returnsOutcome
: ** // In trait OneArgTest: * def apply(fixture: FixtureParam): Outcome ** ** Because the result of a test is an
Outcome
, when the test function returns, the test body must have determined an outcome already. It * will already be one ofSucceeded
,Failed
,Canceled
, orwithFixture(OneArgTest)
returns: because the result type ofwithFixture(OneArgTest)
isOutcome
, * the test body has by definition has already finished execution. * * ** This trait overrides and makes abstract the
* *runTest
method. Subtraits must * must implement this method to callwithFixture(OneArgAsyncTest)
instead ofwithFixture(OneArgTest)
, * wherewithFixture(OneArgAsyncTest)
is a new method declared in this trait with the following * signature and implementation: ** def withFixture(test: OneArgAsyncTest): FutureOutcome = { * test() * } ** ** Instead of returning
* *Outcome
likewithFixture
, thewithFixture
method * returns aFutureOutcome
. Similarly, theapply
method of test function interface, *OneArgAsyncTest
, returnsFutureOutcome
: ** // In trait OneArgAsyncTest: * def apply(fixture: FixtureParam): FutureOutcome ** ** The
* *withFixture
method supports async testing, because when the test function returns, * the test body has not necessarily finished execution. ** The recommended way to ensure cleanup is performed after a test body finishes execution is * to use the
* *complete
-lastly
syntax, defined in supertrait *org.scalatest.CompleteLastly
, which will ensure that * cleanup will occur whether future-producing code completes abruptly by throwing an exception, or returns * normally yielding a future. In the latter case,complete
-lastly
will register the cleanup code * to execute asynchronously when the future completes. ** To enable the stacking of traits that define
* *withFixture(NoArgAsyncTest)
, it is a good idea to let *withFixture(NoArgAsyncTest)
invoke the test function instead of invoking the test * function directly. To do so, you'll need to convert theOneArgAsyncTest
to aNoArgAsyncTest
. You can do that by passing * the fixture object to thetoNoArgAsyncTest
method ofOneArgAsyncTest
. In other words, instead of * writing “test(theFixture)
”, you'd delegate responsibility for * invoking the test function to thewithFixture(NoArgAsyncTest)
method of the same instance by writing: ** withFixture(test.toNoArgAsyncTest(theFixture)) ** ** Thus, the recommended structure of a
* *withFixture
implementation that performs cleanup looks like this: ** // Your implementation * override def withFixture(test: OneArgAsyncTest) = { * * // Perform setup here * val theFixture = ... * * complete { * withFixture(test.toNoArgAsyncTest(theFixture)) // Invoke the test function * } lastly { * // Perform cleanup here * } * } ** ** If you have no cleanup to perform, you can write
* *withFixture
like this instead: ** // Your implementation * override def withFixture(test: OneArgAsyncTest) = { * * // Perform setup here * val theFixture = ... * * withFixture(test.toNoArgAsyncTest(theFixture)) // Invoke the test function * } ** ** If you want to perform an action only for certain outcomes, you'll need to * register code performing that action as a callback on the
* *Future
using * one ofFuture
registration methods:onComplete
,onSuccess
, * oronFailure
. Note that if a test fails, that will be treated as a *scala.util.Success(org.scalatest.Failure)
. So if you want to perform an * action if a test fails, for example, you'd register the callaback usingonSuccess
, * like this: ** // Your implementation * override def withFixture(test: OneArgAsyncTest) = { * * // Perform setup here * val theFixture = ... * * val futureOutcome = * withFixture(test.toNoArgAsyncTest(theFixture)) // Invoke the test function * * futureOutcome onFailedThen { _ => * // perform action that you want to occur * // only if a test fails here * } * } ** ** Lastly, if you want to transform the outcome in some way in
* *withFixture
, you'll need to use either the *map
ortransform
methods ofFuture
, like this: ** // Your implementation * override def withFixture(test: OneArgAsyncTest) = { * * // Perform setup here * val theFixture = ... * * val futureOutcome = * withFixture(test.toNoArgAsyncTest(theFixture)) // Invoke the test function * * futureOutcome change { outcome => * // transform the outcome into a new outcome here * } * } ** ** Note that a
*/ trait FixtureAsyncTestSuite extends org.scalatest.FixtureSuite with org.scalatest.AsyncTestSuite { /** * Transform the test outcome, `Registration` type to `AsyncOutcome`. * * @param testFun test function * @return function that returns `AsyncOutcome` */ private[scalatest] def transformToOutcome(testFun: FixtureParam => Future[compatible.Assertion]): FixtureParam => AsyncTestHolder = (fixture: FixtureParam) => { val futureUnit = testFun(fixture) FutureAsyncTestHolder( futureUnit.map(u => Succeeded).recover { case ex: exceptions.TestCanceledException => Canceled(ex) case _: exceptions.TestPendingException => Pending case tfe: exceptions.TestFailedException => Failed(tfe) case ex: Throwable if !Suite.anExceptionThatShouldCauseAnAbort(ex) => Failed(ex) } ) } /** * A test function taking no arguments and returning anNoArgAsyncTest
'sapply
method will only return aFailure
if * the test completes abruptly with an exception (such asOutOfMemoryError
) that should * cause the suite to abort rather than the test to fail. Thus usually you would usemap
* to transform future outcomes, nottransform
, so that such suite-aborting exceptions pass through * unchanged. The suite will abort asynchronously with any exception returned in aFailure
. *FutureOutcome
. * ** For more detail and examples, see the relevant section in the * documentation for trait
*/ trait OneArgAsyncTest extends (FixtureParam => FutureOutcome) with TestData { thisOneArgAsyncTest => /** * Using the passedfixture.AsyncFlatSpec
. *FixtureParam
, produces aFutureOutcome
representing * the future outcome of this asynchronous test. * * @param fixture theFixtureParam
* @return an instance ofFutureOutcome
*/ def apply(fixture: FixtureParam): FutureOutcome /** * Convert thisOneArgAsyncTest
to aNoArgAsyncTest
whose *name
andconfigMap
methods return the same values * as thisOneArgAsyncTest
, and whoseapply
method invokes * thisOneArgAsyncTest
's apply method, * passing in the givenfixture
. * ** This method makes it easier to invoke the
* *withFixture
method * that takes aNoArgAsyncTest
. * Here's how that might look in aFixtureAsyncTestSuite
* whoseFixtureParam
isStringBuilder
: ** def withFixture(test: OneArgAsyncTest) = { * withFixture(test.toNoArgAsyncTest(new StringBuilder)) * } ** ** Invoking this method has no side effect. It just returns a
* * @param fixture theNoArgAsyncTest
whose *apply
method invokesapply
on thisOneArgAsyncTest
, passing * in theFixtureParam
passed totoNoArgAsyncTest
. *FixtureParam
* @return an new instance ofNoArgAsyncTest
*/ def toNoArgAsyncTest(fixture: FixtureParam): NoArgAsyncTest = new NoArgAsyncTest { val name = thisOneArgAsyncTest.name val configMap = thisOneArgAsyncTest.configMap def apply(): FutureOutcome = { thisOneArgAsyncTest(fixture) } val scopes = thisOneArgAsyncTest.scopes val text = thisOneArgAsyncTest.text val tags = thisOneArgAsyncTest.tags val pos = thisOneArgAsyncTest.pos } } /** * Run the passed test function with a fixture created by this method. * ** This method should create the fixture object needed by the tests of the * current suite, invoke the test function (passing in the fixture object), * and if needed, register any clean up needed after the test completes as * a callback on the
* * @param test theFutureOutcome
returned by the test function. * For more detail and examples, see the * main documentation for this trait. *OneArgAsyncTest
to invoke, passing in a fixture * @return an instance ofFutureOutcome
*/ def withFixture(test: OneArgAsyncTest): FutureOutcome }