org.scalatest.fixture.AsyncTestSuite.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.fixture 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 * supertraitfixture.Suite, because thewithFixture(OneArgTest)lifecycle * method assumes synchronous testing. Here is its signature: ** def withFixture(test: OneArgTest): Outcome ** ** The test function interface,
* *OneArgTest, offers anapplymethod * that takes aFixtureParamand 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
* *runTestmethod. 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
* *OutcomelikewithFixture, thewithFixturemethod * returns aFutureOutcome. Similarly, theapplymethod of test function interface, *OneArgAsyncTest, returnsFutureOutcome: ** // In trait OneArgAsyncTest: * def apply(fixture: FixtureParam): FutureOutcome ** ** The
* *withFixturemethod 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-lastlysyntax, 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-lastlywill 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 theOneArgAsyncTestto aNoArgAsyncTest. You can do that by passing * the fixture object to thetoNoArgAsyncTestmethod 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
* *withFixtureimplementation 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
* *withFixturelike 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
* *Futureusing * one ofFutureregistration 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 *maportransformmethods 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 AsyncTestSuite extends org.scalatest.fixture.Suite 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 => AsyncOutcome = (fixture: FixtureParam) => { val futureUnit = testFun(fixture) InternalFutureOutcome( 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'sapplymethod will only return aFailureif * 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 aFutureOutcomerepresenting * the future outcome of this asynchronous test. * * @param fixture theFixtureParam* @return an instance ofFutureOutcome*/ def apply(fixture: FixtureParam): FutureOutcome /** * Convert thisOneArgAsyncTestto aNoArgAsyncTestwhose *nameandconfigMapmethods return the same values * as thisOneArgAsyncTest, and whoseapplymethod invokes * thisOneArgAsyncTest's apply method, * passing in the givenfixture. * ** This method makes it easier to invoke the
* *withFixturemethod * that takes aNoArgAsyncTest. * Here's how that might look in afixture.AsyncTestSuite* whoseFixtureParamisStringBuilder: ** def withFixture(test: OneArgAsyncTest) = { * withFixture(test.toNoArgAsyncTest(new StringBuilder)) * } ** ** Invoking this method has no side effect. It just returns a
* * @param fixture theNoArgAsyncTestwhose *applymethod invokesapplyon thisOneArgAsyncTest, passing * in theFixtureParampassed 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 theFutureOutcomereturned by the test function. * For more detail and examples, see the * main documentation for this trait. *OneArgAsyncTestto invoke, passing in a fixture * @return an instance ofFutureOutcome*/ def withFixture(test: OneArgAsyncTest): FutureOutcome }