All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalatest.junit.JUnit3Suite.scala Maven / Gradle / Ivy

There is a newer version: 2.0.M6-SNAP27
Show newest version
/*
 * Copyright 2001-2008 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.junit

import collection.immutable.TreeSet
import java.lang.reflect.{Method, Modifier}
import org.scalatest._
import _root_.junit.framework.TestCase
import _root_.junit.framework.TestResult
import _root_.junit.framework.TestSuite
import _root_.junit.framework.TestListener
import _root_.junit.framework.Test
import _root_.junit.framework.AssertionFailedError
import scala.collection.mutable.HashSet
import org.scalatest.events.TestStarting
import org.scalatest.events.TestSucceeded
import org.scalatest.events.TestFailed
import org.scalatest.events.MotionToSuppress
import Suite.getIndentedText
import exceptions._

/**
 * A Suite that is also a junit.framework.TestCase. 
 *
 * 

* A JUnit3Suite may be run by either JUnit 3 (such as JUnit 3.8) or ScalaTest's runner. You write it the way * you write a JUnit 3 TestCase. Tests are methods that start with test, take no parameters, and * have a Unit return type. You manage fixtures with methods setUp and tearDown. * Here's an example: *

* *
 * import org.scalatest.junit.JUnit3Suite
 * import scala.collection.mutable.ListBuffer
 *
 * class BlastFromThePastSuite extends JUnit3Suite {
 *
 *   var sb: StringBuilder = _
 *   var lb: ListBuffer[String] = _
 *
 *   override def setUp() {
 *     sb = new StringBuilder("ScalaTest is ")
 *     lb = new ListBuffer[String]
 *   }
 *
 *   def testEasy() { // Uses JUnit-style assertions
 *     sb.append("easy!")
 *     assertEquals("ScalaTest is easy!", sb.toString)
 *     assertTrue(lb.isEmpty)
 *     lb += "sweet"
 *   }
 *
 *   def testFun() { // Uses ScalaTest assertions
 *     sb.append("fun!")
 *     assert(sb.toString === "ScalaTest is fun!")
 *     assert(lb.isEmpty)
 *   }
 * }
 * 
* *

* You can use either JUnit's assertions, inherited from TestCase, or ScalaTest's, inherited from AssertionsForJUnit. * You can also mix in ShouldMatchersForJUnit or MustMatchersForJUnit if you want to use ScalaTests's matchers DSL. * Here's an example: *

* *
 * import org.scalatest.junit.JUnit3Suite
 * import org.scalatest.junit.MustMatchersForJUnit
 * import scala.collection.mutable.ListBuffer
 *
 * class BlastFromThePastSuite extends JUnit3Suite with MustMatchersForJUnit {
 *
 *   var stringBuilder: StringBuilder = _
 *   var listBuffer: ListBuffer[String] = _
 *
 *   override def setUp() {
 *     stringBuilder = new StringBuilder("ScalaTest is ")
 *     listBuffer = new ListBuffer[String]
 *   }
 *
 *   def testEasy() {
 *     stringBuilder.append("easy!")
 *     stringBuilder.toString must be ("ScalaTest is easy!")
 *     listBuffer must be ('empty)
 *     listBuffer += "sweet"
 *   }
 *
 *   def testFun() {
 *     stringBuilder.append("fun!")
 *     stringBuilder.toString must be ("ScalaTest is fun!")
 *     listBuffer must be ('empty)
 *   }
 * }
 * 
* *

* The reason you would ordinarily want to mix in MustMatchersForJUnit or ShouldMatchersForJUnit rather than MustMatchers * or ShouldMatchers is that MustMatchersForJUnit and ShouldMatchersForJUnit throw * junit.framework.AssertionFailedErrors, which JUnit 3 will report as failures, not errors. *

* *

* When writing JUnit 3 tests in Scala, you should keep in mind that JUnit 3 will not run tests that have a return type other than * Unit. Thus it is best to leave off the equals sign before the curly braces of the body of the test, like this: *

* *
 * def testGoodIdea() { // result type will be Unit
 *   // ...
 * }
 * 
* *

* Instead of this: *

* *
 * def testBadIdea() = { // result type will be inferred
 *   // ...
 * }
 * 
* *

* If the testBadIdea method ends in an expression that has a result type other than Unit, the Scala * compiler will infer a result type to the testBadIdea method to be the same non-Unit type. As a "result," * JUnit 3 will not discover or run the testBadIdea method at all. *

* *

* See also: Getting started with JUnit 3 and ScalaTest. *

* * @author Bill Venners */ class JUnit3Suite extends TestCase with Suite with AssertionsForJUnit { // This is volatile, because who knows what Thread JUnit will fire through this. @volatile private var theTracker = new Tracker /** * Returns the set of test names that will be executed by JUnit when run is invoked * on an instance of this class, or the instance is passed directly to JUnit for running. * *

* The iterator obtained by invoking elements on this * returned Set will produce the test names in their natural order, as determined by String's * compareTo method. Nevertheless, this method is not consulted by JUnit when it * runs the tests, and JUnit may run the tests in any order. *

*/ override def testNames: Set[String] = { def isTestMethod(m: Method) = { val isInstanceMethod = !Modifier.isStatic(m.getModifiers()) // name must have at least 4 chars (minimum is "test") val simpleName = m.getName val firstFour = if (simpleName.length >= 4) simpleName.substring(0, 4) else "" val paramTypes = m.getParameterTypes val hasNoParams = paramTypes.length == 0 val hasVoidReturnType = m.getReturnType == Void.TYPE // won't discover testNames because it has a non-Unit return type isInstanceMethod && (firstFour == "test") && hasNoParams && hasVoidReturnType } val testNameArray = for (m <- getClass.getMethods; if isTestMethod(m)) yield m.getName TreeSet[String]() ++ testNameArray } /** * Returns an empty Map, because tags are not supported by JUnit 3. */ override def tags = Map() /** * Returns the number of tests expected to be run by JUnit when run is invoked * on this Suite. * *

* If tagsToInclude in the passed Filter is defined, this class's * implementation of this method returns 0. Else this class's implementation of this method * returns the size of the set returned by testNames on the current instance. *

*/ override def expectedTestCount(filter: Filter) = if (filter.tagsToInclude.isDefined) 0 else testNames.size /** * Throws UnsupportedOperationException, because this method is unused by this * class, given this class's run method delegates to JUnit to run * its tests. * *

* The main purpose of this method implementation is to render a compiler error an attempt * to mix in a trait that overrides withFixture. Because this * trait does not actually use withFixture, the attempt to mix * in behavior would very likely not work. *

* * * @param test the no-arg test function to run with a fixture */ override final protected def withFixture(test: NoArgTest) { throw new UnsupportedOperationException } /** * Throws UnsupportedOperationException, because this method is unused by this * class, given this class's run method delegates to JUnit to run * its tests. * *

* The main purpose of this method implementation is to render a compiler error an attempt * to mix in a trait that overrides runNestedSuites. Because this * trait does not actually use runNestedSuites, the attempt to mix * in behavior would very likely not work. *

* * @param reporter the Reporter to which results will be reported * @param stopper the Stopper that will be consulted to determine whether to stop execution early. * @param filter a Filter with which to filter tests based on their tags * @param configMap a Map of key-value pairs that can be used by the executing Suite of tests. * @param distributor an optional Distributor, into which to put nested Suites to be run * by another entity, such as concurrently by a pool of threads. If None, nested Suites will be run sequentially. * @param tracker a Tracker tracking Ordinals being fired by the current thread. * * @throws UnsupportedOperationException always. */ override final protected def runNestedSuites(reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { throw new UnsupportedOperationException } /** * Throws UnsupportedOperationException, because this method is unused by this * class, given this class's run method delegates to JUnit to run * its tests. * *

* The main purpose of this method implementation is to render a compiler error an attempt * to mix in a trait that overrides runTests. Because this * trait does not actually use runTests, the attempt to mix * in behavior would very likely not work. *

* * @param testName an optional name of one test to run. If None, all relevant tests should be run. * I.e., None acts like a wildcard that means run all relevant tests in this Suite. * @param reporter the Reporter to which results will be reported * @param stopper the Stopper that will be consulted to determine whether to stop execution early. * @param filter a Filter with which to filter tests based on their tags * @param configMap a Map of key-value pairs that can be used by the executing Suite of tests. * @param distributor an optional Distributor, into which to put nested Suites to be run * by another entity, such as concurrently by a pool of threads. If None, nested Suites will be run sequentially. * @param tracker a Tracker tracking Ordinals being fired by the current thread. * @throws UnsupportedOperationException always. */ override protected final def runTests(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { throw new UnsupportedOperationException } /** * Throws UnsupportedOperationException, because this method is unused by this * class, given this class's run method delegates to JUnit to run * its tests. * *

* The main purpose of this method implementation is to render a compiler error an attempt * to mix in a trait that overrides runTest. Because this * trait does not actually use runTest, the attempt to mix * in behavior would very likely not work. *

* * @param testName the name of one test to run. * @param reporter the Reporter to which results will be reported * @param stopper the Stopper that will be consulted to determine whether to stop execution early. * @param configMap a Map of key-value pairs that can be used by the executing Suite of tests. * @param tracker a Tracker tracking Ordinals being fired by the current thread. * @throws UnsupportedOperationException always. */ override protected final def runTest(testName: String, reporter: Reporter, stopper: Stopper, configMap: Map[String, Any], tracker: Tracker) { throw new UnsupportedOperationException } override def run(testName: Option[String], reporter: Reporter, stopper: Stopper, filter: Filter, configMap: Map[String, Any], distributor: Option[Distributor], tracker: Tracker) { theTracker = tracker if (!filter.tagsToInclude.isDefined) { val testResult = new TestResult testResult.addListener(new MyTestListener(reporter, tracker)) testName match { case None => new TestSuite(this.getClass).run(testResult) case Some(tn) => if (!testNames.contains(tn)) throw new IllegalArgumentException(Resources("testNotFound", testName)) setName(tn) run(testResult) } } } /** * Suite style name. */ final override val styleName: String = "JUnit3Suite" } private[scalatest] class MyTestListener(report: Reporter, tracker: Tracker) extends TestListener { // TODO: worry about threading private val failedTestsSet = scala.collection.mutable.Set[Test]() private def getSuiteNameForTestCase(testCase: Test) = testCase match { case junit3Suite: JUnit3Suite => junit3Suite.suiteName case _ => Suite.getSimpleNameOfAnObjectsClass(testCase) // Should never happen, but just in case } def getMessageGivenThrowable(throwable: Throwable, isAssertionFailedError: Boolean) = if (throwable.getMessage == null) "A JUnit3Suite test failed with an " + (if (isAssertionFailedError) "AssertionFailedError" else "exception") // Hopefully will never happen else throwable.getMessage // The Test passed to these methods is an instance of the JUnit3Suite class, Calling // test.getClass.getName on it gets the fully qualified name of the class // test.asInstanceOf[TestCase].getName gives you the name of the test method, without any parens // Calling test.toSring gives you testError(org.scalatestexamples.junit.JUnit3ExampleSuite) // So that's that old JUnit-style test name thing. def startTest(testCase: Test) { if (testCase == null) throw new NullPointerException("testCase was null") report(TestStarting(tracker.nextOrdinal(), getSuiteNameForTestCase(testCase), Some(testCase.getClass.getName), testCase.toString, Some(MotionToSuppress), None)) } def addError(testCase: Test, throwable: Throwable) { if (testCase == null) throw new NullPointerException("testCase was null") if (throwable == null) throw new NullPointerException("throwable was null") val formatter = getIndentedText(testCase.toString, 1, true) val payload = throwable match { case optPayload: PayloadField => optPayload.payload case _ => None } report(TestFailed(tracker.nextOrdinal(), getMessageGivenThrowable(throwable, false), getSuiteNameForTestCase(testCase), Some(testCase.getClass.getName), testCase.toString, Some(throwable), None, Some(formatter), None, payload)) failedTestsSet += testCase } def addFailure(testCase: Test, assertionFailedError: AssertionFailedError) { if (testCase == null) throw new NullPointerException("testCase was null") if (assertionFailedError == null) throw new NullPointerException("throwable was null") val formatter = getIndentedText(testCase.toString, 1, true) report(TestFailed(tracker.nextOrdinal(), getMessageGivenThrowable(assertionFailedError, true), getSuiteNameForTestCase(testCase), Some(testCase.getClass.getName), testCase.toString, Some(assertionFailedError), None, Some(formatter), None)) failedTestsSet += testCase } def endTest(testCase: Test) { val testHadFailed = failedTestsSet.contains(testCase) if (!testHadFailed) { if (testCase == null) throw new NullPointerException("testCase was null") val formatter = getIndentedText(testCase.toString, 1, true) report(TestSucceeded(tracker.nextOrdinal(), getSuiteNameForTestCase(testCase), Some(testCase.getClass.getName), testCase.toString, None, Some(formatter), None)) } else { failedTestsSet -= testCase } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy