Skip to content

Commit 7e7bcd4

Browse files
committed
Implement method level test filtering
1 parent 6d09e93 commit 7e7bcd4

File tree

2 files changed

+80
-8
lines changed

2 files changed

+80
-8
lines changed

src/main/scala/org/scalatestplus/junit/JUnitRunner.scala

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
*/
1717
package org.scalatestplus.junit
1818

19-
import org.scalatest._
19+
import org.scalatest.{Args, ConfigMap, DynaTags, Stopper, Suite, Tracker, Filter}
2020
import org.junit.runner.notification.RunNotifier
2121
import org.junit.runner.notification.Failure
2222
import org.junit.runner.Description
2323
import org.junit.runner.manipulation.{Filter => TestFilter, Filterable, NoTestsRemainException}
2424

25+
import scala.collection.JavaConverters._
26+
import scala.collection.mutable
27+
2528
/*
2629
I think that Stopper really should be a no-op, like it is, because the user has
2730
no way to stop it. This is wierd, because it will call nested suites. So the tests
@@ -71,6 +74,8 @@ final class JUnitRunner(suiteClass: java.lang.Class[_ <: Suite]) extends org.jun
7174
*/
7275
val getDescription = createDescription(suiteToRun)
7376

77+
private val excludedTests: mutable.Set[String] = mutable.Set()
78+
7479
private def createDescription(suite: Suite): Description = {
7580
val description = Description.createSuiteDescription(suite.getClass)
7681
// If we don't add the testNames and nested suites in, we get
@@ -96,12 +101,29 @@ final class JUnitRunner(suiteClass: java.lang.Class[_ <: Suite]) extends org.jun
96101
*/
97102
def run(notifier: RunNotifier): Unit = {
98103
try {
104+
val includedTests: Set[String] = suiteToRun.testNames.diff(excludedTests)
105+
val testTags: Map[String, Map[String, Set[String]]] = Map(
106+
suiteToRun.suiteId ->
107+
includedTests.map(test => test -> Set("INCLUDE")).toMap
108+
)
109+
val filter = Filter(
110+
tagsToInclude = Some(Set("INCLUDE")),
111+
dynaTags = DynaTags(suiteTags = Map.empty, testTags = testTags)
112+
)
99113
// TODO: What should this Tracker be?
100-
suiteToRun.run(None, Args(new RunNotifierReporter(notifier),
101-
Stopper.default, Filter(), ConfigMap.empty, None,
102-
new Tracker))
103-
}
104-
catch {
114+
suiteToRun.run(
115+
None,
116+
Args(
117+
new RunNotifierReporter(notifier),
118+
Stopper.default,
119+
filter,
120+
ConfigMap.empty,
121+
None,
122+
new Tracker,
123+
Set.empty
124+
)
125+
)
126+
} catch {
105127
case e: Exception =>
106128
notifier.fireTestFailure(new Failure(getDescription, e))
107129
}
@@ -113,10 +135,16 @@ final class JUnitRunner(suiteClass: java.lang.Class[_ <: Suite]) extends org.jun
113135
*
114136
* @return the expected number of tests that will run when this suite is run
115137
*/
116-
override def testCount() = suiteToRun.expectedTestCount(Filter())
138+
override def testCount(): Int = suiteToRun.expectedTestCount(Filter())
117139

140+
@throws(classOf[NoTestsRemainException])
118141
override def filter(filter: TestFilter): Unit = {
119-
if (!filter.shouldRun(getDescription)) throw new NoTestsRemainException
142+
getDescription.getChildren.asScala
143+
.filter(child => !filter.shouldRun(child))
144+
.foreach(child => excludedTests.add(child.getMethodName))
145+
if (getDescription.getChildren.isEmpty) {
146+
throw new NoTestsRemainException()
147+
}
120148
}
121149

122150
}

src/test/scala/org/scalatestplus/junit/JUnitRunnerSuite.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,30 @@ package org.scalatestplus.junit {
103103
assert(1 === 1)
104104
}
105105
}
106+
107+
class MethodExclusionFilter(methodNamePattern: String) extends TestFilter {
108+
override def shouldRun(description: Description): Boolean = {
109+
!description.getMethodName().contains(methodNamePattern)
110+
}
111+
112+
override def describe(): String = s"Excludes tests with method names containing: '$methodNamePattern'"
113+
}
114+
115+
@RunWith(classOf[JUnitRunner])
116+
class MethodFilterTargetSuite extends funsuite.AnyFunSuite {
117+
118+
test("JUnit ran this OK!") {
119+
assert(1 === 1)
120+
}
121+
122+
test("should pass filter and execute") {
123+
assert(2 === 2)
124+
}
125+
126+
test("should be filtered out") {
127+
assert(3 === 5)
128+
}
129+
}
106130
}
107131

108132
import org.junit.runner.Description
@@ -112,6 +136,7 @@ package org.scalatestplus.junit {
112136
import org.scalatestplus.junit.helpers.EasySuite
113137
import org.scalatestplus.junit.helpers.KerblooeySuite
114138
import org.scalatestplus.junit.helpers.{FilteredInSuite, FilteredOutSuite, NameFilter}
139+
import org.scalatestplus.junit.helpers.{MethodExclusionFilter, MethodFilterTargetSuite}
115140
import scala.util.Try
116141

117142
class JUnitRunnerSuite extends funsuite.AnyFunSuite {
@@ -190,5 +215,24 @@ package org.scalatestplus.junit {
190215
assert(runNotifier.ran.head.getDisplayName ===
191216
"JUnit ran this OK!(org.scalatestplus.junit.helpers.FilteredInSuite)")
192217
}
218+
219+
test("Should execute only methods that don't match the filter pattern") {
220+
class ExecutedTestsNotifier extends RunNotifier {
221+
var executedTests: List[Description] = Nil
222+
223+
override def fireTestFinished(description: Description): Unit = {
224+
executedTests = description :: executedTests
225+
}
226+
}
227+
val runNotifier = new ExecutedTestsNotifier()
228+
val runner = new JUnitRunner(classOf[MethodFilterTargetSuite])
229+
230+
val excludeMethodFilter = new MethodExclusionFilter("should be filtered out")
231+
232+
runner.filter(excludeMethodFilter)
233+
runner.run(runNotifier)
234+
235+
assert(runNotifier.executedTests.size === 2) // Verifies that only 2 tests ran (one was filtered out)
236+
}
193237
}
194238
}

0 commit comments

Comments
 (0)