Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
36: Testing with ScalaTest
I've seen this happen to almost every developer transitioning into Scala: you write a piece of logic that involves asynchronous calls—usually Futures—and then you write a test for it. You run the test, it turns green, and you feel great. Then, you intentionally break the production code to see if the test catches it, and the test still turns green. It's a frustrating moment, but it's a rite of passage.
case class User(id: Int, name: String)
class UserRepository {
def findUserById(id: Int): Future[Option[User]] = {
// Simulating a DB call that might fail or return nothing
Future {
if (id == 1) Some(User(1, "Alice")) else None
}
}
}
class UserRepositorySpec extends AnyFlatSpec with Matchers {
val repo = new UserRepository()
"UserRepository" should "find a user by id" in {
val result = repo.findUserById(1)
// This looks right, but it's actually a bug!
result should be (a [Future[_]])
}
}
The Silent Success of Unresolved Futures
Look closely at that test. You're asserting that result is a Future. Technically, that's true! The method returns a Future immediately, and the test finishes before the code inside that Future ever actually executes. You aren't testing the value inside the box; you're just testing that the box exists. If you changed the repository to always return None, this test would still pass because it's not waiting for the asynchronous operation to complete.
Waiting for the Truth with ScalaFutures
To fix this, we need to tell ScalaTest to actually wait for the Future to resolve before evaluating the assertion. The most common way to do this without switching your entire class hierarchy is by mixing in ScalaFutures. This gives you the whenReady block, which suspends the test until the Future completes (or times out).
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.time.{PatienceConfig, Span}
class UserRepositorySpec extends AnyFlatSpec with Matchers with ScalaFutures {
// I usually set a custom timeout because the default can be a bit aggressive
// depending on your CI environment.
implicit val patience: PatienceConfig = PatienceConfig(timeout = Span(5, seconds))
val repo = new UserRepository()
"UserRepository" should "return Alice when searching for ID 1" in {
val result = repo.findUserById(1)
whenReady(result) { userOpt =>
userOpt should be (Some(User(1, "Alice")))
}
}
it should "return None for a non-existent user" in {
val result = repo.findUserById(99)
whenReady(result) { userOpt =>
userOpt should be (None)
}
}
}
By using whenReady, we've forced the test to synchronize. Now, if the repository returns the wrong user or fails, the test will actually fail. I prefer this approach over manually calling Await.result because it integrates better with ScalaTest's reporting and allows for cleaner timeout configurations.
Writing Expressive Tests with Matchers
One of the best parts of ScalaTest is the Matchers trait. It allows us to move away from the rigid assert(x == y) style and toward something that reads like a sentence. This isn't just about aesthetics; when a test fails, Matchers provides much more descriptive error messages (e.g., "Expected Some(User(1, 'Alice')) but got None") compared to a simple "false was not true".
Depending on what you're testing, you have a few powerful options:
should be (value): The bread and butter for equality.should contain (element): Perfect for checking if a List or Set has a specific item.should not be (value): For verifying the absence of a state.intercept[Exception] { ... }: This is how you test that your code correctly throws an error when given bad input.
I'll leave you with a tip: stick to one style. ScalaTest offers FunSpec, WordSpec, FlatSpec, and others. They all do basically the same thing, but mixing them in a single project creates a cognitive load for anyone else reading your code. Pick one (I personally lean toward AnyFlatSpec for its balance of brevity and clarity) and be consistent.
📋 Practical Task
Test Suite for a Shopping Cart Validator
You are tasked with testing a CartValidator class. The validator ensures that a shopping cart meets certain business rules before it can be checked out.
The Requirements:
- The cart cannot be empty.
- The total price must be greater than $0.
- If the cart contains a "Hazardous" item, it must have a "Safety Permit" item included.
Your Task:
- Create a
CartItemcase class (withname: Stringandprice: Double). - Implement a
CartValidatorclass with a methodvalidate(items: List[CartItem]): Either[String, Boolean]. It should returnLeft("Error Message")if a rule is broken, orRight(true)if the cart is valid. - Write a ScalaTest suite using
AnyFlatSpecandMatchersthat covers:- A successful validation of a normal cart.
- A failure when the cart is empty.
- A failure when a "Hazardous" item is present without a "Safety Permit".
- A success when both "Hazardous" and "Safety Permit" items are present.
There are no comments for now.