0
# Test Execution
1
2
Test runners and executors for running test suites with configurable behavior and reporting.
3
4
## Capabilities
5
6
### TestRunner
7
8
Main component for executing test specifications.
9
10
```scala { .api }
11
/**
12
* Test runner that executes test specifications
13
* @tparam R - Environment requirements
14
* @tparam E - Error type
15
*/
16
case class TestRunner[+R, -E](executor: TestExecutor[R, E]) {
17
/** Executes a test specification */
18
def run[R1 <: R, E1 >: E](spec: ZSpec[R1, E1]): URIO[R1, ExecutedSpec[E1]]
19
20
/** Creates new runner with different executor */
21
def withExecutor[R1, E1](executor: TestExecutor[R1, E1]): TestRunner[R1, E1]
22
}
23
24
object TestRunner {
25
/** Default test runner with standard test environment */
26
def default[R](testEnvironment: ZLayer[Any, Nothing, R]): TestRunner[R, Any]
27
}
28
```
29
30
### TestExecutor
31
32
Core execution engine for running tests with reporting.
33
34
```scala { .api }
35
/**
36
* Executes test specifications with configurable behavior
37
* @tparam R - Environment requirements
38
* @tparam E - Error type
39
*/
40
abstract class TestExecutor[-R, -E] {
41
/** Executes specification with reporter */
42
def run[R1 <: R, E1 >: E](
43
spec: ZSpec[R1, E1],
44
reporter: TestReporter[E1]
45
): URIO[R1, ExecutedSpec[E1]]
46
47
/** Executes specification with environment layer */
48
def runSpec[R1 <: R, E1 >: E](
49
spec: ZSpec[R1, E1],
50
environmentLayer: ZLayer[Any, Nothing, R1]
51
): UIO[ExecutedSpec[E1]]
52
}
53
54
object TestExecutor {
55
/** Default executor with standard configuration */
56
def default[R](testEnvironment: ZLayer[Any, Nothing, R]): TestExecutor[R, Any]
57
}
58
```
59
60
### Runnable Spec Base Classes
61
62
Abstract base classes for creating executable test specifications.
63
64
```scala { .api }
65
/**
66
* Base class for runnable test specifications
67
* @tparam R - Environment requirements
68
* @tparam E - Error type
69
*/
70
abstract class RunnableSpec[-R, +E] {
71
/** The test specification to run */
72
def spec: ZSpec[R, E]
73
74
/** Test runner configuration */
75
def runner: TestRunner[R, E]
76
77
/** Test aspects to apply to all tests */
78
def aspects: List[TestAspect[Nothing, R, Nothing, Any]]
79
80
/** Platform-specific configuration */
81
def platform: TestPlatform
82
83
/** Executes the specification */
84
def runSpec(spec: ZSpec[R, E]): URIO[R, ExecutedSpec[E]]
85
}
86
87
/**
88
* Default runnable spec with test environment
89
*/
90
abstract class DefaultRunnableSpec extends RunnableSpec[TestEnvironment, Any] {
91
/** Test specification with standard test environment */
92
def spec: ZSpec[TestEnvironment, Any]
93
94
/** Default aspects including timeout warning */
95
override def aspects: List[TestAspect[Nothing, TestEnvironment, Nothing, Any]] =
96
List(TestAspect.timeoutWarning(60.seconds))
97
98
/** Default test runner */
99
override def runner: TestRunner[TestEnvironment, Any] = defaultTestRunner
100
}
101
102
/**
103
* Mutable runnable spec for dynamic test creation
104
*/
105
abstract class MutableRunnableSpec extends RunnableSpec[TestEnvironment, Any] {
106
/** Mutable test specification */
107
def spec: ZSpec[TestEnvironment, Any]
108
}
109
```
110
111
### ExecutedSpec
112
113
Result of test execution with hierarchical structure.
114
115
```scala { .api }
116
/**
117
* Result of executing a test specification
118
* @tparam E - Error type
119
*/
120
case class ExecutedSpec[+E](
121
caseValue: SpecCase[Any, TestFailure[E], TestSuccess, ExecutedSpec[E]]
122
) {
123
/** Folds over the execution tree */
124
def fold[Z](f: ExecutedSpec[E] => Z): Z
125
126
/** Checks if any spec matches predicate */
127
def exists(f: ExecutedSpec[E] => Boolean): Boolean
128
129
/** Gets all failed specs */
130
def failures: List[ExecutedSpec[E]]
131
132
/** Gets all successful specs */
133
def successes: List[ExecutedSpec[E]]
134
135
/** Gets all ignored specs */
136
def ignored: List[ExecutedSpec[E]]
137
138
/** Counts total number of tests */
139
def size: Int
140
141
/** Transforms the execution result */
142
def transform[E1](f: SpecCase[Any, TestFailure[E], TestSuccess, ExecutedSpec[E]] =>
143
SpecCase[Any, TestFailure[E1], TestSuccess, ExecutedSpec[E1]]): ExecutedSpec[E1]
144
}
145
```
146
147
### Test Reporting
148
149
System for reporting test results with customizable output.
150
151
```scala { .api }
152
/**
153
* Reporter for test execution results
154
* @tparam E - Error type
155
*/
156
type TestReporter[-E] = (Duration, ExecutedSpec[E]) => URIO[TestLogger, Unit]
157
158
object TestReporter {
159
/** Silent reporter that produces no output */
160
val silent: TestReporter[Any]
161
}
162
163
/**
164
* Default test reporter with console output
165
*/
166
object DefaultTestReporter {
167
/** Standard console reporter */
168
def apply[E](duration: Duration, executedSpec: ExecutedSpec[E]): URIO[TestLogger, Unit]
169
}
170
```
171
172
### Summary and Results
173
174
Summary information about test execution.
175
176
```scala { .api }
177
/**
178
* Summary of test execution results
179
*/
180
case class Summary(
181
success: Int, // Number of successful tests
182
fail: Int, // Number of failed tests
183
ignore: Int, // Number of ignored tests
184
summary: String // Textual summary
185
) {
186
/** Total number of tests */
187
def total: Int = success + fail + ignore
188
189
/** Whether all tests passed */
190
def isSuccess: Boolean = fail == 0
191
}
192
193
object Summary {
194
/** Creates summary from executed spec */
195
def fromExecutedSpec[E](executedSpec: ExecutedSpec[E]): Summary
196
}
197
198
/**
199
* Builder for constructing test summaries
200
*/
201
class SummaryBuilder {
202
/** Adds successful test */
203
def addSuccess(): SummaryBuilder
204
205
/** Adds failed test */
206
def addFailure(): SummaryBuilder
207
208
/** Adds ignored test */
209
def addIgnored(): SummaryBuilder
210
211
/** Builds final summary */
212
def build(): Summary
213
}
214
```
215
216
### Platform Configuration
217
218
Platform-specific test execution configuration.
219
220
```scala { .api }
221
/**
222
* Platform-specific test configuration
223
*/
224
trait TestPlatform {
225
/** Whether platform is JVM */
226
def isJVM: Boolean
227
228
/** Whether platform is JavaScript */
229
def isJS: Boolean
230
231
/** Whether platform is Native */
232
def isNative: Boolean
233
234
/** Platform-specific test execution timeout */
235
def timeout: Duration
236
}
237
238
object TestPlatform {
239
/** Default platform configuration */
240
val default: TestPlatform
241
242
/** Check if running on JVM */
243
def isJVM: Boolean
244
245
/** Check if running on JavaScript */
246
def isJS: Boolean
247
248
/** Check if running on Native */
249
def isNative: Boolean
250
}
251
```
252
253
## Usage Examples
254
255
### Basic Runnable Spec
256
257
```scala
258
import zio.test._
259
import zio.test.environment.TestEnvironment
260
261
object BasicSpec extends DefaultRunnableSpec {
262
def spec = suite("Basic Tests")(
263
test("simple test") {
264
assert(2 + 2)(equalTo(4))
265
},
266
267
testM("effectful test") {
268
assertM(ZIO.succeed(42))(equalTo(42))
269
}
270
)
271
}
272
```
273
274
### Custom Runner Configuration
275
276
```scala
277
import zio.test._
278
import zio.duration._
279
280
object CustomRunnerSpec extends RunnableSpec[TestEnvironment, Any] {
281
def spec = suite("Custom Runner Tests")(
282
// test definitions...
283
)
284
285
override def aspects = List(
286
TestAspect.timeout(30.seconds),
287
TestAspect.parallel,
288
TestAspect.retryN(3)
289
)
290
291
override def runner = TestRunner(
292
TestExecutor.default(testEnvironment)
293
)
294
}
295
```
296
297
### Mutable Spec for Dynamic Tests
298
299
```scala
300
object DynamicSpec extends MutableRunnableSpec {
301
def spec = {
302
val dynamicTests = (1 to 5).map { i =>
303
test(s"dynamic test $i") {
304
assert(i * 2)(isGreaterThan(i))
305
}
306
}
307
308
suite("Dynamic Tests")(dynamicTests: _*)
309
}
310
}
311
```
312
313
### Custom Test Reporter
314
315
```scala
316
import zio.test._
317
318
val customReporter: TestReporter[Any] = (duration, executedSpec) => {
319
for {
320
summary <- ZIO.succeed(Summary.fromExecutedSpec(executedSpec))
321
_ <- TestLogger.logLine(s"Tests completed in ${duration.toMillis}ms")
322
_ <- TestLogger.logLine(s"Passed: ${summary.success}")
323
_ <- TestLogger.logLine(s"Failed: ${summary.fail}")
324
_ <- TestLogger.logLine(s"Ignored: ${summary.ignore}")
325
} yield ()
326
}
327
328
object CustomReporterSpec extends DefaultRunnableSpec {
329
def spec = suite("Custom Reporter Tests")(
330
// test definitions...
331
)
332
333
override def runner = TestRunner(
334
TestExecutor.default(testEnvironment)
335
).withReporter(customReporter)
336
}
337
```
338
339
### Platform-Specific Tests
340
341
```scala
342
object PlatformSpec extends DefaultRunnableSpec {
343
def spec = suite("Platform Tests")(
344
test("JVM only test") {
345
assertTrue(TestPlatform.isJVM)
346
} @@ TestAspect.jvmOnly,
347
348
test("JS only test") {
349
assertTrue(TestPlatform.isJS)
350
} @@ TestAspect.jsOnly,
351
352
suite("Cross-platform tests")(
353
test("works everywhere") {
354
assert(2 + 2)(equalTo(4))
355
}
356
)
357
)
358
}
359
```
360
361
### Running Tests Programmatically
362
363
```scala
364
import zio.test._
365
366
val mySpec = suite("Programmatic Tests")(
367
test("test 1")(assert(true)(isTrue)),
368
test("test 2")(assert(false)(isFalse))
369
)
370
371
val program = for {
372
runner <- ZIO.succeed(defaultTestRunner)
373
result <- runner.run(mySpec)
374
summary = Summary.fromExecutedSpec(result)
375
_ <- console.putStrLn(s"Tests: ${summary.total}, Passed: ${summary.success}")
376
} yield summary
377
378
// Run with test environment
379
program.provideLayer(testEnvironment ++ Console.live)
380
```
381
382
### Test Execution with Timeout
383
384
```scala
385
object TimeoutSpec extends DefaultRunnableSpec {
386
def spec = suite("Timeout Tests")(
387
testM("fast test") {
388
assertM(ZIO.succeed(42))(equalTo(42))
389
},
390
391
testM("slow test") {
392
for {
393
_ <- ZIO.sleep(10.seconds)
394
result <- ZIO.succeed(42)
395
} yield assert(result)(equalTo(42))
396
} @@ TestAspect.timeout(5.seconds)
397
)
398
399
override def aspects = List(
400
TestAspect.timeoutWarning(2.seconds)
401
)
402
}
403
```