0
# Advanced Testing Utilities
1
2
ScalaTest provides specialized utilities for complex testing scenarios including bulk collection assertions, pattern matching tests, private method testing, XML comparison, property-based testing integration, and test execution control. These advanced features enable comprehensive testing of sophisticated systems and edge cases.
3
4
## Capabilities
5
6
### Enhanced Assertion Diagrams
7
8
Visual assertion failure reporting that shows expression evaluation trees for better debugging.
9
10
```scala { .api }
11
/**
12
* Enhanced assertion failure reporting with visual diagrams
13
*/
14
trait Diagrams {
15
/**
16
* Enhanced assert with visual failure diagrams showing expression evaluation
17
* @param condition boolean expression to evaluate
18
*/
19
def assert(condition: Boolean): Assertion
20
21
/**
22
* Enhanced assert with clue and visual failure diagrams
23
* @param condition boolean expression to evaluate
24
* @param clue additional context for failures
25
*/
26
def assert(condition: Boolean, clue: Any): Assertion
27
}
28
```
29
30
**Usage Example:**
31
32
```scala
33
import org.scalatest.funsuite.AnyFunSuite
34
import org.scalatest.matchers.should.Matchers
35
import org.scalatest.diagrams.Diagrams
36
37
class DiagramExampleSpec extends AnyFunSuite with Matchers with Diagrams {
38
39
test("enhanced assertion failure reporting") {
40
val users = List(
41
User("Alice", 25),
42
User("Bob", 30),
43
User("Charlie", 35)
44
)
45
46
// When this fails, diagrams show the evaluation tree
47
assert(users.filter(_.age > 40).nonEmpty)
48
// Shows: users.filter(_.age > 40).nonEmpty was false
49
// users.filter(_.age > 40) was List()
50
// users was List(User("Alice", 25), User("Bob", 30), User("Charlie", 35))
51
}
52
53
test("complex expression diagrams") {
54
val x = 5
55
val y = 10
56
val threshold = 20
57
58
// Enhanced failure reporting shows each step
59
assert((x * 2) + (y / 2) > threshold)
60
// Shows: (x * 2) + (y / 2) > threshold was false
61
// (x * 2) + (y / 2) was 15
62
// (x * 2) was 10, (y / 2) was 5
63
// x was 5, y was 10, threshold was 20
64
}
65
}
66
```
67
68
### Inspectors for Bulk Assertions
69
70
Apply assertions to every element in collections with detailed failure reporting.
71
72
```scala { .api }
73
/**
74
* Bulk assertion utilities for collections
75
*/
76
trait Inspectors {
77
/**
78
* Assert that all elements in collection satisfy the assertion
79
* @param xs the collection to inspect
80
* @param fun assertion applied to each element
81
*/
82
def forAll[E](xs: scala.collection.GenTraversable[E])(fun: E => Unit): Unit
83
84
/**
85
* Assert that at least minimum number of elements satisfy assertion
86
* @param min minimum number of elements that must satisfy assertion
87
* @param xs the collection to inspect
88
* @param fun assertion applied to each element
89
*/
90
def forAtLeast[E](min: Int, xs: scala.collection.GenTraversable[E])(fun: E => Unit): Unit
91
92
/**
93
* Assert that at most maximum number of elements satisfy assertion
94
* @param max maximum number of elements that can satisfy assertion
95
* @param xs the collection to inspect
96
* @param fun assertion applied to each element
97
*/
98
def forAtMost[E](max: Int, xs: scala.collection.GenTraversable[E])(fun: E => Unit): Unit
99
100
/**
101
* Assert that between min and max elements (inclusive) satisfy assertion
102
* @param from minimum number of elements (inclusive)
103
* @param upTo maximum number of elements (inclusive)
104
* @param xs the collection to inspect
105
* @param fun assertion applied to each element
106
*/
107
def forBetween[E](from: Int, upTo: Int, xs: scala.collection.GenTraversable[E])(fun: E => Unit): Unit
108
109
/**
110
* Assert that exactly the specified number of elements satisfy assertion
111
* @param num exact number of elements that must satisfy assertion
112
* @param xs the collection to inspect
113
* @param fun assertion applied to each element
114
*/
115
def forExactly[E](num: Int, xs: scala.collection.GenTraversable[E])(fun: E => Unit): Unit
116
117
/**
118
* Assert that every element in collection satisfies assertion (synonym for forAll)
119
* @param xs the collection to inspect
120
* @param fun assertion applied to each element
121
*/
122
def forEvery[E](xs: scala.collection.GenTraversable[E])(fun: E => Unit): Unit
123
}
124
```
125
126
**Usage Examples:**
127
128
```scala
129
import org.scalatest.funsuite.AnyFunSuite
130
import org.scalatest.matchers.should.Matchers
131
import org.scalatest.Inspectors
132
133
class InspectorExampleSpec extends AnyFunSuite with Matchers with Inspectors {
134
135
test("all users should have valid data") {
136
val users = List(
137
User("Alice", 25, "alice@example.com"),
138
User("Bob", 30, "bob@example.com"),
139
User("Charlie", 35, "charlie@example.com")
140
)
141
142
forAll(users) { user =>
143
user.name should not be empty
144
user.age should be > 0
145
user.email should include("@")
146
}
147
}
148
149
test("at least half of scores should be passing") {
150
val scores = List(85, 92, 67, 78, 95, 88, 72)
151
val passingThreshold = 70
152
153
forAtLeast(scores.length / 2, scores) { score =>
154
score should be >= passingThreshold
155
}
156
}
157
158
test("at most two items should be expensive") {
159
val prices = List(10.99, 250.00, 15.50, 300.00, 25.00, 8.75)
160
val expensiveThreshold = 200.00
161
162
forAtMost(2, prices) { price =>
163
price should be > expensiveThreshold
164
}
165
}
166
167
test("between 2 and 4 words should be capitalized") {
168
val words = List("Hello", "world", "This", "Is", "a", "Test")
169
170
forBetween(2, 4, words) { word =>
171
word.head.isUpper should be(true)
172
}
173
}
174
175
test("exactly one user should be admin") {
176
val users = List(
177
User("Alice", Role.USER),
178
User("Bob", Role.ADMIN),
179
User("Charlie", Role.USER)
180
)
181
182
forExactly(1, users) { user =>
183
user.role should equal(Role.ADMIN)
184
}
185
}
186
187
test("detailed failure reporting") {
188
val numbers = List(1, 2, 3, 4, 5, 6)
189
190
// This will fail and show exactly which elements failed
191
forAll(numbers) { num =>
192
num should be < 5 // Will fail for 5 and 6
193
}
194
}
195
}
196
```
197
198
### Inside Pattern Matching
199
200
Use pattern matching in assertions with detailed failure reporting.
201
202
```scala { .api }
203
/**
204
* Pattern matching assertion utilities
205
*/
206
trait Inside {
207
/**
208
* Apply pattern matching assertion to a value
209
* @param value the value to pattern match against
210
* @param pf partial function containing pattern and assertions
211
*/
212
def inside[T](value: T)(pf: PartialFunction[T, Unit]): Unit
213
}
214
```
215
216
**Usage Examples:**
217
218
```scala
219
import org.scalatest.funsuite.AnyFunSuite
220
import org.scalatest.matchers.should.Matchers
221
import org.scalatest.Inside
222
223
class InsideExampleSpec extends AnyFunSuite with Matchers with Inside {
224
225
test("pattern matching on case classes") {
226
val response = ApiResponse(200, "Success", Some(UserData("Alice", 25)))
227
228
inside(response) {
229
case ApiResponse(status, message, Some(userData)) =>
230
status should equal(200)
231
message should equal("Success")
232
userData.name should equal("Alice")
233
userData.age should be > 18
234
}
235
}
236
237
test("pattern matching on collections") {
238
val numbers = List(1, 2, 3, 4, 5)
239
240
inside(numbers) {
241
case head :: second :: tail =>
242
head should equal(1)
243
second should equal(2)
244
tail should have length 3
245
tail should contain allOf(3, 4, 5)
246
}
247
}
248
249
test("pattern matching on nested structures") {
250
val json = JsonObject(Map(
251
"user" -> JsonObject(Map(
252
"name" -> JsonString("Alice"),
253
"age" -> JsonNumber(25)
254
)),
255
"status" -> JsonString("active")
256
))
257
258
inside(json) {
259
case JsonObject(fields) =>
260
inside(fields("user")) {
261
case JsonObject(userFields) =>
262
inside(userFields("name")) {
263
case JsonString(name) => name should equal("Alice")
264
}
265
inside(userFields("age")) {
266
case JsonNumber(age) => age should equal(25)
267
}
268
}
269
inside(fields("status")) {
270
case JsonString(status) => status should equal("active")
271
}
272
}
273
}
274
275
test("pattern matching with guards") {
276
val event = UserEvent("login", timestamp = System.currentTimeMillis())
277
278
inside(event) {
279
case UserEvent(eventType, timestamp) if timestamp > 0 =>
280
eventType should equal("login")
281
timestamp should be > 0L
282
// Additional assertions based on successful pattern match
283
}
284
}
285
}
286
```
287
288
### Checkpoints for Multiple Assertions
289
290
Create multiple assertion checkpoints that all must pass, with detailed reporting of which ones failed.
291
292
```scala { .api }
293
/**
294
* Multiple assertion checkpoint utilities
295
*/
296
trait Checkpoints {
297
/**
298
* Create a checkpoint with a name
299
* @param name descriptive name for the checkpoint
300
*/
301
def checkpoint(name: String): Checkpoint
302
303
/**
304
* Individual checkpoint that can be used in assertions
305
*/
306
final class Checkpoint(name: String) {
307
/**
308
* Apply assertion to this checkpoint
309
* @param assertion the assertion to apply
310
*/
311
def apply(assertion: => Unit): Unit
312
}
313
}
314
```
315
316
**Usage Examples:**
317
318
```scala
319
import org.scalatest.funsuite.AnyFunSuite
320
import org.scalatest.matchers.should.Matchers
321
import org.scalatest.Checkpoints
322
323
class CheckpointExampleSpec extends AnyFunSuite with Matchers with Checkpoints {
324
325
test("user validation with multiple checkpoints") {
326
val user = User("Alice", 25, "alice@example.com", isActive = true)
327
328
val nameCheck = checkpoint("name validation")
329
val ageCheck = checkpoint("age validation")
330
val emailCheck = checkpoint("email validation")
331
val statusCheck = checkpoint("status validation")
332
333
nameCheck {
334
user.name should not be empty
335
user.name should have length (be >= 2 and be <= 50)
336
}
337
338
ageCheck {
339
user.age should be >= 18
340
user.age should be <= 120
341
}
342
343
emailCheck {
344
user.email should include("@")
345
user.email should include(".")
346
user.email should not startWith "@"
347
}
348
349
statusCheck {
350
user.isActive should be(true)
351
}
352
// If any checkpoint fails, all checkpoint results are reported
353
}
354
355
test("API response validation with checkpoints") {
356
val response = makeApiCall("/users/123")
357
358
val statusCheck = checkpoint("HTTP status")
359
val headersCheck = checkpoint("response headers")
360
val bodyCheck = checkpoint("response body")
361
362
statusCheck {
363
response.status should equal(200)
364
}
365
366
headersCheck {
367
response.headers should contain key "Content-Type"
368
response.headers("Content-Type") should include("application/json")
369
}
370
371
bodyCheck {
372
val user = parseUser(response.body)
373
user.id should equal(123)
374
user.name should not be empty
375
}
376
}
377
}
378
```
379
380
### Private Method Testing
381
382
Test private methods using reflection-based invocation.
383
384
```scala { .api }
385
/**
386
* Private method testing utilities
387
*/
388
trait PrivateMethodTester {
389
/**
390
* Create a private method invocation
391
* @param methodName the name of the private method (as Symbol)
392
* @return PrivateMethod instance for invocation
393
*/
394
def PrivateMethod[T](methodName: Symbol): PrivateMethod[T]
395
396
/**
397
* Private method wrapper for invocation
398
*/
399
final class PrivateMethod[T](methodName: Symbol) {
400
/**
401
* Invoke the private method
402
* @param target the object containing the private method
403
* @param args arguments to pass to the method
404
* @return result of the method invocation
405
*/
406
def invokeOn(target: AnyRef, args: Any*): T
407
}
408
409
/**
410
* Alternative syntax for private method invocation
411
* @param target the object containing the private method
412
* @param methodName the private method name (as Symbol)
413
* @param args arguments to pass to the method
414
* @return result of the method invocation
415
*/
416
def invokePrivate[T](target: AnyRef, methodName: Symbol, args: Any*): T
417
}
418
```
419
420
**Usage Examples:**
421
422
```scala
423
import org.scalatest.funsuite.AnyFunSuite
424
import org.scalatest.matchers.should.Matchers
425
import org.scalatest.PrivateMethodTester
426
427
class Calculator {
428
def add(a: Int, b: Int): Int = a + b
429
430
private def validateInput(value: Int): Boolean = value >= 0
431
432
private def complexCalculation(x: Double, y: Double): Double = {
433
val intermediate = x * 2 + y / 3
434
math.sqrt(intermediate)
435
}
436
}
437
438
class PrivateMethodTestSpec extends AnyFunSuite with Matchers with PrivateMethodTester {
439
440
test("testing private validation method") {
441
val calculator = new Calculator()
442
val validateInput = PrivateMethod[Boolean](Symbol("validateInput"))
443
444
// Test private method with valid input
445
val result1 = validateInput.invokeOn(calculator, 5)
446
result1 should be(true)
447
448
// Test private method with invalid input
449
val result2 = validateInput.invokeOn(calculator, -1)
450
result2 should be(false)
451
}
452
453
test("testing private calculation method") {
454
val calculator = new Calculator()
455
val complexCalculation = PrivateMethod[Double](Symbol("complexCalculation"))
456
457
val result = complexCalculation.invokeOn(calculator, 4.0, 6.0)
458
result should be(3.0 +- 0.001) // sqrt(4*2 + 6/3) = sqrt(10) ≈ 3.16
459
}
460
461
test("alternative syntax for private method testing") {
462
val calculator = new Calculator()
463
464
// Using invokePrivate directly
465
val isValid = invokePrivate[Boolean](calculator, Symbol("validateInput"), 10)
466
isValid should be(true)
467
468
val calculation = invokePrivate[Double](calculator, Symbol("complexCalculation"), 1.0, 3.0)
469
calculation should be > 0.0
470
}
471
472
test("private method with multiple parameters") {
473
class StringProcessor {
474
private def processStrings(s1: String, s2: String, delimiter: String): String = {
475
s"$s1$delimiter$s2"
476
}
477
}
478
479
val processor = new StringProcessor()
480
val processStrings = PrivateMethod[String](Symbol("processStrings"))
481
482
val result = processStrings.invokeOn(processor, "Hello", "World", " - ")
483
result should equal("Hello - World")
484
}
485
}
486
```
487
488
### XML Testing Utilities
489
490
Specialized utilities for testing XML content with normalization and comparison.
491
492
```scala { .api }
493
/**
494
* XML testing utilities with normalization
495
*/
496
trait StreamlinedXml {
497
/**
498
* Normalize XML for comparison (whitespace, formatting)
499
*/
500
implicit def convertToXmlWrapper(xml: scala.xml.Node): XmlWrapper
501
502
final class XmlWrapper(xml: scala.xml.Node) {
503
/**
504
* Compare XML content ignoring formatting differences
505
*/
506
def ===(other: scala.xml.Node): Boolean
507
}
508
}
509
510
/**
511
* XML equality with normalization
512
*/
513
trait StreamlinedXmlEquality {
514
// Enables normalized XML comparison in assertions
515
}
516
517
/**
518
* XML normalization methods
519
*/
520
trait StreamlinedXmlNormMethods {
521
/**
522
* Normalize XML node for comparison
523
* @param node the XML node to normalize
524
* @return normalized XML node
525
*/
526
def normalizeXml(node: scala.xml.Node): scala.xml.Node
527
}
528
```
529
530
**Usage Examples:**
531
532
```scala
533
import org.scalatest.funsuite.AnyFunSuite
534
import org.scalatest.matchers.should.Matchers
535
import org.scalatest.StreamlinedXml
536
import scala.xml._
537
538
class XmlTestingSpec extends AnyFunSuite with Matchers with StreamlinedXml {
539
540
test("XML comparison ignoring formatting") {
541
val xml1 = <user><name>Alice</name><age>25</age></user>
542
val xml2 =
543
<user>
544
<name>Alice</name>
545
<age>25</age>
546
</user>
547
548
// Different formatting, same content
549
xml1 should ===(xml2)
550
}
551
552
test("XML content validation") {
553
val userXml = <user id="123"><name>Bob</name><email>bob@example.com</email></user>
554
555
// Test XML structure and content
556
(userXml \ "@id").text should equal("123")
557
(userXml \ "name").text should equal("Bob")
558
(userXml \ "email").text should include("@")
559
}
560
561
test("complex XML comparison") {
562
val expected =
563
<response>
564
<status>success</status>
565
<data>
566
<users>
567
<user id="1">Alice</user>
568
<user id="2">Bob</user>
569
</users>
570
</data>
571
</response>
572
573
val actual = generateXmlResponse()
574
575
actual should ===(expected)
576
577
// Also test specific parts
578
(actual \ "status").text should equal("success")
579
(actual \\ "user").length should equal(2)
580
}
581
582
test("XML with attributes and namespaces") {
583
val soapEnvelope =
584
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
585
<soap:Body>
586
<getUserResponse xmlns="http://example.com/users">
587
<user id="123" active="true">
588
<name>Charlie</name>
589
</user>
590
</getUserResponse>
591
</soap:Body>
592
</soap:Envelope>
593
594
// Test namespace handling
595
(soapEnvelope \ "Body" \ "getUserResponse" \ "user" \ "@id").text should equal("123")
596
(soapEnvelope \ "Body" \ "getUserResponse" \ "user" \ "@active").text should equal("true")
597
}
598
}
599
```
600
601
### Test Execution Control
602
603
Control how tests are executed including parallel execution, ordering, and failure handling.
604
605
```scala { .api }
606
/**
607
* Execute tests in parallel within the suite
608
*/
609
trait ParallelTestExecution extends OneInstancePerTest {
610
// Tests in this suite run in parallel
611
}
612
613
/**
614
* Randomize test execution order
615
*/
616
trait RandomTestOrder extends Suite {
617
// Tests execute in random order each run
618
}
619
620
/**
621
* Force sequential test execution (override parallel settings)
622
*/
623
trait Sequential extends Suite {
624
// Tests execute sequentially even if parallel execution is configured
625
}
626
627
/**
628
* Stop test suite execution on first failure
629
*/
630
trait StopOnFailure extends Suite {
631
// Suite stops executing tests after first failure
632
}
633
634
/**
635
* Cancel remaining tests after first failure
636
*/
637
trait CancelAfterFailure extends Suite {
638
// Remaining tests are cancelled (not failed) after first failure
639
}
640
641
/**
642
* Execute tests before nested suites
643
*/
644
trait TestsBeforeNestedSuites extends Suite {
645
// Tests in this suite run before any nested suites
646
}
647
```
648
649
**Usage Examples:**
650
651
```scala
652
import org.scalatest.funsuite.AnyFunSuite
653
import org.scalatest.matchers.should.Matchers
654
import org.scalatest.{ParallelTestExecution, RandomTestOrder, StopOnFailure}
655
656
class ParallelExecutionSpec extends AnyFunSuite with Matchers with ParallelTestExecution {
657
658
test("parallel test 1") {
659
Thread.sleep(100)
660
1 + 1 should equal(2)
661
}
662
663
test("parallel test 2") {
664
Thread.sleep(50)
665
2 + 2 should equal(4)
666
}
667
668
test("parallel test 3") {
669
Thread.sleep(75)
670
3 + 3 should equal(6)
671
}
672
// These tests run concurrently
673
}
674
675
class RandomOrderSpec extends AnyFunSuite with Matchers with RandomTestOrder {
676
677
test("test A") {
678
println("Executing test A")
679
succeed
680
}
681
682
test("test B") {
683
println("Executing test B")
684
succeed
685
}
686
687
test("test C") {
688
println("Executing test C")
689
succeed
690
}
691
// Execution order varies between runs
692
}
693
694
class FailFastSpec extends AnyFunSuite with Matchers with StopOnFailure {
695
696
test("this test passes") {
697
1 should equal(1)
698
}
699
700
test("this test fails") {
701
fail("Intentional failure")
702
}
703
704
test("this test would be skipped") {
705
// This test won't execute due to StopOnFailure
706
2 should equal(2)
707
}
708
}
709
```
710
711
### Test Communication and Documentation
712
713
Add runtime information, alerts, and documentation to tests.
714
715
```scala { .api }
716
/**
717
* Provide runtime information during test execution
718
*/
719
trait Informing {
720
/**
721
* Add information to test output
722
* @param informer the informer instance
723
*/
724
protected def info: Informer
725
}
726
727
trait Informer {
728
/**
729
* Provide information during test execution
730
* @param message information message
731
*/
732
def apply(message: String): Unit
733
}
734
735
/**
736
* Send alert messages during test execution
737
*/
738
trait Alerting {
739
protected def alert: Alerter
740
}
741
742
trait Alerter {
743
def apply(message: String): Unit
744
}
745
746
/**
747
* Add documentation to tests
748
*/
749
trait Documenting {
750
protected def markup: Documenter
751
}
752
753
trait Documenter {
754
def apply(message: String): Unit
755
}
756
757
/**
758
* BDD-style test documentation
759
*/
760
trait GivenWhenThen {
761
/**
762
* Document test preconditions
763
* @param message description of given conditions
764
*/
765
def Given(message: String): Unit
766
767
/**
768
* Document test actions
769
* @param message description of actions taken
770
*/
771
def When(message: String): Unit
772
773
/**
774
* Document expected outcomes
775
* @param message description of expected results
776
*/
777
def Then(message: String): Unit
778
779
/**
780
* Document additional conditions or actions
781
* @param message description of additional context
782
*/
783
def And(message: String): Unit
784
}
785
```
786
787
**Usage Examples:**
788
789
```scala
790
import org.scalatest.funsuite.AnyFunSuite
791
import org.scalatest.matchers.should.Matchers
792
import org.scalatest.GivenWhenThen
793
794
class DocumentedTestSpec extends AnyFunSuite with Matchers with GivenWhenThen {
795
796
test("user registration process") {
797
Given("a new user wants to register")
798
val userData = UserRegistrationData("Alice", "alice@example.com", "password123")
799
800
And("the email is not already taken")
801
val userService = new UserService()
802
userService.isEmailAvailable(userData.email) should be(true)
803
804
When("the user submits registration")
805
val result = userService.registerUser(userData)
806
807
Then("the user should be created successfully")
808
result.isSuccess should be(true)
809
result.user.name should equal("Alice")
810
811
And("the user should receive a confirmation email")
812
val emailService = new EmailService()
813
emailService.wasConfirmationSent(result.user.email) should be(true)
814
815
info("Registration completed with user ID: " + result.user.id)
816
}
817
818
test("shopping cart workflow with detailed documentation") {
819
Given("an empty shopping cart")
820
val cart = new ShoppingCart()
821
cart.items should be(empty)
822
823
When("items are added to the cart")
824
val book = Product("Scala Programming", 39.99)
825
val pen = Product("Blue Pen", 2.50)
826
827
cart.addItem(book)
828
cart.addItem(pen)
829
830
Then("the cart should contain the items")
831
cart.items should have size 2
832
cart.items should contain allOf(book, pen)
833
834
And("the total should be calculated correctly")
835
cart.total should equal(42.49)
836
837
info(s"Cart total: ${cart.total}")
838
markup("This test demonstrates the basic shopping cart functionality")
839
}
840
}
841
```
842
843
## Common Advanced Patterns
844
845
### Comprehensive Collection Validation
846
847
```scala
848
test("validate entire user collection") {
849
val users = loadUsers()
850
851
forAll(users) { user =>
852
inside(user) {
853
case User(name, age, email, profile) =>
854
name should not be empty
855
age should (be >= 18 and be <= 120)
856
email should fullyMatch(emailRegex)
857
858
inside(profile) {
859
case Some(UserProfile(bio, location)) =>
860
bio should have length (be <= 500)
861
location should not be empty
862
case None => // OK for profile to be empty
863
}
864
}
865
}
866
}
867
```
868
869
### Complex Data Structure Testing
870
871
```scala
872
test("validate API response structure") {
873
val response = callApi("/users/123")
874
875
val statusCheck = checkpoint("status validation")
876
val dataCheck = checkpoint("data validation")
877
val metaCheck = checkpoint("metadata validation")
878
879
statusCheck {
880
response.status should be(200)
881
}
882
883
inside(response.body) {
884
case ApiResponse(data, metadata, links) =>
885
dataCheck {
886
inside(data) {
887
case UserData(id, name, email, preferences) =>
888
id should equal(123)
889
name should not be empty
890
email should include("@")
891
}
892
}
893
894
metaCheck {
895
metadata.version should equal("1.0")
896
metadata.timestamp should be > 0L
897
}
898
}
899
}
900
```
901
902
### Private Method Integration Testing
903
904
```scala
905
test("internal processing pipeline") {
906
val processor = new DataProcessor()
907
908
// Test individual private methods
909
val validateData = PrivateMethod[Boolean](Symbol("validateData"))
910
val transformData = PrivateMethod[ProcessedData](Symbol("transformData"))
911
val persistData = PrivateMethod[String](Symbol("persistData"))
912
913
val rawData = RawData("test data")
914
915
validateData.invokeOn(processor, rawData) should be(true)
916
917
val transformed = transformData.invokeOn(processor, rawData)
918
transformed.isValid should be(true)
919
920
val id = persistData.invokeOn(processor, transformed)
921
id should not be empty
922
}
923
```