Skip to content

Commit 587dce0

Browse files
committed
Add helpful 🚨 error messages to test assertions
Each expect() now includes a message that guides learners when tests fail, helping them understand what might be wrong without giving away the answer.
1 parent b1558ec commit 587dce0

File tree

10 files changed

+73
-73
lines changed

10 files changed

+73
-73
lines changed

exercises/01.classes/01.solution.class-basics/index.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@ import { Product, ShoppingCart } from './index.ts'
33

44
await testStep('Product class should create instances with name and price', async () => {
55
const laptop = new Product('Laptop', 999.99)
6-
expect(laptop.name).toBe('Laptop')
7-
expect(laptop.price).toBe(999.99)
6+
expect(laptop.name, '🚨 Product.name should be "Laptop" - check your class property definition').toBe('Laptop')
7+
expect(laptop.price, '🚨 Product.price should be 999.99 - check your class property definition').toBe(999.99)
88
})
99

1010
await testStep('Product getDescription should return formatted string', async () => {
1111
const mouse = new Product('Mouse', 29.99)
1212
const description = mouse.getDescription()
13-
expect(description).toBe('Product: Mouse - $29.99')
13+
expect(description, '🚨 getDescription() should return "Product: Mouse - $29.99" - check your method implementation and formatting').toBe('Product: Mouse - $29.99')
1414
})
1515

1616
await testStep('ShoppingCart should initialize with empty items array', async () => {
1717
const cart = new ShoppingCart()
18-
expect(cart.items).toEqual([])
19-
expect(cart.items.length).toBe(0)
18+
expect(cart.items, '🚨 ShoppingCart.items should be an empty array - check your class property initialization').toEqual([])
19+
expect(cart.items.length, '🚨 ShoppingCart.items.length should be 0 - check your class property initialization').toBe(0)
2020
})
2121

2222
await testStep('ShoppingCart addItem should add products to cart', async () => {
@@ -25,12 +25,12 @@ await testStep('ShoppingCart addItem should add products to cart', async () => {
2525
const mouse = new Product('Mouse', 29.99)
2626

2727
cart.addItem(laptop)
28-
expect(cart.items.length).toBe(1)
29-
expect(cart.items[0]).toBe(laptop)
28+
expect(cart.items.length, '🚨 After adding one item, items.length should be 1 - check your addItem method implementation').toBe(1)
29+
expect(cart.items[0], '🚨 items[0] should be the laptop product - check your addItem method implementation').toBe(laptop)
3030

3131
cart.addItem(mouse)
32-
expect(cart.items.length).toBe(2)
33-
expect(cart.items[1]).toBe(mouse)
32+
expect(cart.items.length, '🚨 After adding two items, items.length should be 2 - check your addItem method implementation').toBe(2)
33+
expect(cart.items[1], '🚨 items[1] should be the mouse product - check your addItem method implementation').toBe(mouse)
3434
})
3535

3636
await testStep('ShoppingCart getTotal should calculate sum of all item prices', async () => {
@@ -42,5 +42,5 @@ await testStep('ShoppingCart getTotal should calculate sum of all item prices',
4242
cart.addItem(mouse)
4343

4444
const total = cart.getTotal()
45-
expect(total).toBe(1029.98)
45+
expect(total, '🚨 getTotal() should return 1029.98 (sum of 999.99 + 29.99) - check your method implementation and price calculation').toBe(1029.98)
4646
})

exercises/01.classes/02.solution.constructors/index.test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,42 @@ import { User, BankAccount, Config } from './index.ts'
33

44
await testStep('User constructor should set name, email, and default role', async () => {
55
const user = new User('Alice', '[email protected]')
6-
expect(user.name).toBe('Alice')
7-
expect(user.email).toBe('[email protected]')
8-
expect(user.role).toBe('user')
6+
expect(user.name, '🚨 User.name should be "Alice" - check your constructor parameter assignment').toBe('Alice')
7+
expect(user.email, '🚨 User.email should be "[email protected]" - check your constructor parameter assignment').toBe('[email protected]')
8+
expect(user.role, '🚨 User.role should default to "user" - check your constructor default parameter value').toBe('user')
99
})
1010

1111
await testStep('User constructor should accept custom role', async () => {
1212
const admin = new User('Bob', '[email protected]', 'admin')
13-
expect(admin.name).toBe('Bob')
14-
expect(admin.email).toBe('[email protected]')
15-
expect(admin.role).toBe('admin')
13+
expect(admin.name, '🚨 User.name should be "Bob" - check your constructor parameter assignment').toBe('Bob')
14+
expect(admin.email, '🚨 User.email should be "[email protected]" - check your constructor parameter assignment').toBe('[email protected]')
15+
expect(admin.role, '🚨 User.role should be "admin" when provided as third parameter - check your constructor parameter handling').toBe('admin')
1616
})
1717

1818
await testStep('BankAccount constructor should set accountNumber and default balance', async () => {
1919
const account = new BankAccount('12345')
20-
expect(account.accountNumber).toBe('12345')
21-
expect(account.getBalance()).toBe(0)
20+
expect(account.accountNumber, '🚨 BankAccount.accountNumber should be "12345" - check your constructor parameter assignment').toBe('12345')
21+
expect(account.getBalance(), '🚨 BankAccount.getBalance() should return 0 initially - check your constructor initialization').toBe(0)
2222
})
2323

2424
await testStep('BankAccount deposit should increase balance', async () => {
2525
const account = new BankAccount('12345')
2626
account.deposit(100)
27-
expect(account.getBalance()).toBe(100)
27+
expect(account.getBalance(), '🚨 After depositing 100, getBalance() should return 100 - check your deposit method implementation').toBe(100)
2828
account.deposit(50)
29-
expect(account.getBalance()).toBe(150)
29+
expect(account.getBalance(), '🚨 After depositing another 50, getBalance() should return 150 - check your deposit method accumulates correctly').toBe(150)
3030
})
3131

3232
await testStep('Config constructor should use default values', async () => {
3333
const config = new Config()
34-
expect(config.host).toBe('localhost')
35-
expect(config.port).toBe(3000)
36-
expect(config.debug).toBe(false)
34+
expect(config.host, '🚨 Config.host should default to "localhost" - check your constructor default parameter values').toBe('localhost')
35+
expect(config.port, '🚨 Config.port should default to 3000 - check your constructor default parameter values').toBe(3000)
36+
expect(config.debug, '🚨 Config.debug should default to false - check your constructor default parameter values').toBe(false)
3737
})
3838

3939
await testStep('Config constructor should accept custom values', async () => {
4040
const customConfig = new Config('example.com', 8080, true)
41-
expect(customConfig.host).toBe('example.com')
42-
expect(customConfig.port).toBe(8080)
43-
expect(customConfig.debug).toBe(true)
41+
expect(customConfig.host, '🚨 Config.host should be "example.com" when provided - check your constructor parameter assignment').toBe('example.com')
42+
expect(customConfig.port, '🚨 Config.port should be 8080 when provided - check your constructor parameter assignment').toBe(8080)
43+
expect(customConfig.debug, '🚨 Config.debug should be true when provided - check your constructor parameter assignment').toBe(true)
4444
})

exercises/02.access-modifiers/01.solution.public-private/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ import { User } from './index.ts'
33

44
await testStep('User should have public username property', async () => {
55
const user = new User('alice', 'secret123')
6-
expect(user.username).toBe('alice')
6+
expect(user.username, '🚨 User.username should be "alice" - check your public property definition').toBe('alice')
77
})
88

99
await testStep('User login should return true for correct password', async () => {
1010
const user = new User('alice', 'secret123')
1111
const result = user.login('secret123')
12-
expect(result).toBe(true)
12+
expect(result, '🚨 login() should return true for correct password - check your login method implementation and password comparison').toBe(true)
1313
})
1414

1515
await testStep('User login should return false for incorrect password', async () => {
1616
const user = new User('alice', 'secret123')
1717
const result = user.login('wrong')
18-
expect(result).toBe(false)
18+
expect(result, '🚨 login() should return false for incorrect password - check your login method implementation and password comparison').toBe(false)
1919
})

exercises/02.access-modifiers/02.solution.protected/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { Car } from './index.ts'
44
await testStep('Car should inherit from Vehicle and access protected fields', async () => {
55
const car = new Car('Toyota', 'Camry')
66
const info = car.getInfo()
7-
expect(info).toBe('Toyota Camry')
7+
expect(info, '🚨 getInfo() should return "Toyota Camry" - check that Car extends Vehicle and accesses protected properties').toBe('Toyota Camry')
88
})
99

1010
await testStep('Car should be instance of Vehicle', async () => {
1111
const car = new Car('Honda', 'Civic')
12-
expect(car instanceof Car).toBe(true)
12+
expect(car instanceof Car, '🚨 car should be an instance of Car - check your class definition').toBe(true)
1313
})

exercises/03.interfaces-and-classes/01.solution.implementing-interfaces/index.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@ import { CreditCard, PayPal } from './index.ts'
33

44
await testStep('CreditCard should implement PaymentMethod interface', async () => {
55
const creditCard = new CreditCard('1234-5678-9012-3456')
6-
expect(creditCard.cardNumber).toBe('1234-5678-9012-3456')
6+
expect(creditCard.cardNumber, '🚨 CreditCard.cardNumber should be "1234-5678-9012-3456" - check your class property definition').toBe('1234-5678-9012-3456')
77
const result = creditCard.pay(100)
8-
expect(result).toBe('Paid $100 with credit card 1234-5678-9012-3456')
8+
expect(result, '🚨 pay() should return "Paid $100 with credit card 1234-5678-9012-3456" - check your PaymentMethod interface implementation').toBe('Paid $100 with credit card 1234-5678-9012-3456')
99
})
1010

1111
await testStep('PayPal should implement PaymentMethod interface', async () => {
1212
const paypal = new PayPal('[email protected]')
13-
expect(paypal.email).toBe('[email protected]')
13+
expect(paypal.email, '🚨 PayPal.email should be "[email protected]" - check your class property definition').toBe('[email protected]')
1414
const result = paypal.pay(50)
15-
expect(result).toBe('Paid $50 with PayPal [email protected]')
15+
expect(result, '🚨 pay() should return "Paid $50 with PayPal [email protected]" - check your PaymentMethod interface implementation').toBe('Paid $50 with PayPal [email protected]')
1616
})
1717

1818
await testStep('Both payment methods should have pay method', async () => {
1919
const creditCard = new CreditCard('1234-5678-9012-3456')
2020
const paypal = new PayPal('[email protected]')
2121

22-
expect(typeof creditCard.pay).toBe('function')
23-
expect(typeof paypal.pay).toBe('function')
22+
expect(typeof creditCard.pay, '🚨 CreditCard.pay should be a function - check that you implemented the PaymentMethod interface').toBe('function')
23+
expect(typeof paypal.pay, '🚨 PayPal.pay should be a function - check that you implemented the PaymentMethod interface').toBe('function')
2424
})

exercises/03.interfaces-and-classes/02.solution.programming-to-abstractions/index.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { CreditCard, PayPal, processPayment } from './index.ts'
44
await testStep('processPayment should work with CreditCard', async () => {
55
const creditCard = new CreditCard('1234-5678-9012-3456')
66
const result = processPayment(creditCard, 100)
7-
expect(result).toBe('Paid $100 with credit card 1234-5678-9012-3456')
7+
expect(result, '🚨 processPayment() should return "Paid $100 with credit card 1234-5678-9012-3456" - check your function accepts PaymentMethod type').toBe('Paid $100 with credit card 1234-5678-9012-3456')
88
})
99

1010
await testStep('processPayment should work with PayPal', async () => {
1111
const paypal = new PayPal('[email protected]')
1212
const result = processPayment(paypal, 50)
13-
expect(result).toBe('Paid $50 with PayPal [email protected]')
13+
expect(result, '🚨 processPayment() should return "Paid $50 with PayPal [email protected]" - check your function accepts PaymentMethod type').toBe('Paid $50 with PayPal [email protected]')
1414
})
1515

1616
await testStep('processPayment should accept any PaymentMethod implementation', async () => {
@@ -20,6 +20,6 @@ await testStep('processPayment should accept any PaymentMethod implementation',
2020
const result1 = processPayment(creditCard, 75)
2121
const result2 = processPayment(paypal, 25)
2222

23-
expect(result1).toContain('credit card')
24-
expect(result2).toContain('PayPal')
23+
expect(result1, '🚨 CreditCard payment result should contain "credit card" - check your processPayment function accepts PaymentMethod interface').toContain('credit card')
24+
expect(result2, '🚨 PayPal payment result should contain "PayPal" - check your processPayment function accepts PaymentMethod interface').toContain('PayPal')
2525
})

exercises/04.inheritance/01.solution.extends/index.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,25 @@ import { Circle, Rectangle, Shape } from './index.ts'
33

44
await testStep('Circle should inherit color from Shape', async () => {
55
const circle = new Circle('red', 5)
6-
expect(circle.color).toBe('red')
7-
expect(circle.radius).toBe(5)
6+
expect(circle.color, '🚨 Circle.color should be "red" - check that Circle extends Shape and inherits the color property').toBe('red')
7+
expect(circle.radius, '🚨 Circle.radius should be 5 - check your Circle constructor parameter assignment').toBe(5)
88
})
99

1010
await testStep('Rectangle should inherit color from Shape', async () => {
1111
const rectangle = new Rectangle('blue', 10, 20)
12-
expect(rectangle.color).toBe('blue')
13-
expect(rectangle.width).toBe(10)
14-
expect(rectangle.height).toBe(20)
12+
expect(rectangle.color, '🚨 Rectangle.color should be "blue" - check that Rectangle extends Shape and inherits the color property').toBe('blue')
13+
expect(rectangle.width, '🚨 Rectangle.width should be 10 - check your Rectangle constructor parameter assignment').toBe(10)
14+
expect(rectangle.height, '🚨 Rectangle.height should be 20 - check your Rectangle constructor parameter assignment').toBe(20)
1515
})
1616

1717
await testStep('Circle should be instance of Shape', async () => {
1818
const circle = new Circle('green', 3)
19-
expect(circle instanceof Circle).toBe(true)
20-
expect(circle instanceof Shape).toBe(true)
19+
expect(circle instanceof Circle, '🚨 circle should be an instance of Circle - check your class definition').toBe(true)
20+
expect(circle instanceof Shape, '🚨 circle should be an instance of Shape - check that Circle extends Shape').toBe(true)
2121
})
2222

2323
await testStep('Rectangle should be instance of Shape', async () => {
2424
const rectangle = new Rectangle('yellow', 5, 5)
25-
expect(rectangle instanceof Rectangle).toBe(true)
26-
expect(rectangle instanceof Shape).toBe(true)
25+
expect(rectangle instanceof Rectangle, '🚨 rectangle should be an instance of Rectangle - check your class definition').toBe(true)
26+
expect(rectangle instanceof Shape, '🚨 rectangle should be an instance of Shape - check that Rectangle extends Shape').toBe(true)
2727
})

exercises/04.inheritance/02.solution.method-overriding/index.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@ import { Circle, Rectangle, Shape } from './index.ts'
33

44
await testStep('Shape getArea should return 0', async () => {
55
const shape = new Shape('red')
6-
expect(shape.getArea()).toBe(0)
6+
expect(shape.getArea(), '🚨 Shape.getArea() should return 0 - check your base class method implementation').toBe(0)
77
})
88

99
await testStep('Circle should override getArea to calculate circle area', async () => {
1010
const circle = new Circle('red', 5)
1111
const area = circle.getArea()
12-
expect(area).toBeCloseTo(Math.PI * 25, 2)
13-
expect(area).toBeCloseTo(78.54, 2)
12+
expect(area, '🚨 Circle.getArea() should be approximately Math.PI * 25 - check that you override getArea() with the circle area formula').toBeCloseTo(Math.PI * 25, 2)
13+
expect(area, '🚨 Circle.getArea() should be approximately 78.54 - check your circle area calculation (π * radius²)').toBeCloseTo(78.54, 2)
1414
})
1515

1616
await testStep('Rectangle should override getArea to calculate rectangle area', async () => {
1717
const rectangle = new Rectangle('blue', 10, 20)
1818
const area = rectangle.getArea()
19-
expect(area).toBe(200)
19+
expect(area, '🚨 Rectangle.getArea() should return 200 (10 * 20) - check that you override getArea() with the rectangle area formula').toBe(200)
2020
})
2121

2222
await testStep('Different shapes should have different area calculations', async () => {
@@ -26,7 +26,7 @@ await testStep('Different shapes should have different area calculations', async
2626
const circleArea = circle.getArea()
2727
const rectangleArea = rectangle.getArea()
2828

29-
expect(circleArea).toBeGreaterThan(0)
30-
expect(rectangleArea).toBeGreaterThan(0)
31-
expect(circleArea).not.toBe(rectangleArea)
29+
expect(circleArea, '🚨 Circle area should be greater than 0 - check your getArea() override implementation').toBeGreaterThan(0)
30+
expect(rectangleArea, '🚨 Rectangle area should be greater than 0 - check your getArea() override implementation').toBeGreaterThan(0)
31+
expect(circleArea, '🚨 Circle and Rectangle areas should be different - check that each class overrides getArea() with its own calculation').not.toBe(rectangleArea)
3232
})

exercises/05.polymorphism/01.solution.substitutability/index.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ await testStep('MediaPlayer should accept MediaFile instances', async () => {
55
const mediaFile = new MediaFile('file.mp3')
66
const player = new MediaPlayer()
77
const result = player.playFile(mediaFile)
8-
expect(result).toBe('Playing file.mp3')
8+
expect(result, '🚨 playFile() should return "Playing file.mp3" - check your MediaPlayer.playFile method accepts MediaFile type').toBe('Playing file.mp3')
99
})
1010

1111
await testStep('MediaPlayer should accept AudioFile instances (polymorphism)', async () => {
1212
const audio = new AudioFile('song.mp3')
1313
const player = new MediaPlayer()
1414
const result = player.playFile(audio)
15-
expect(result).toBe('Playing audio: song.mp3')
15+
expect(result, '🚨 playFile() should return "Playing audio: song.mp3" - check that AudioFile extends MediaFile and playFile accepts the base type').toBe('Playing audio: song.mp3')
1616
})
1717

1818
await testStep('MediaPlayer should accept VideoFile instances (polymorphism)', async () => {
1919
const video = new VideoFile('movie.mp4')
2020
const player = new MediaPlayer()
2121
const result = player.playFile(video)
22-
expect(result).toBe('Playing video: movie.mp4')
22+
expect(result, '🚨 playFile() should return "Playing video: movie.mp4" - check that VideoFile extends MediaFile and playFile accepts the base type').toBe('Playing video: movie.mp4')
2323
})
2424

2525
await testStep('AudioFile and VideoFile should be substitutable for MediaFile', async () => {
@@ -30,8 +30,8 @@ await testStep('AudioFile and VideoFile should be substitutable for MediaFile',
3030
const audioResult = player.playFile(audio)
3131
const videoResult = player.playFile(video)
3232

33-
expect(audioResult).toContain('audio')
34-
expect(videoResult).toContain('video')
35-
expect(audio instanceof MediaFile).toBe(true)
36-
expect(video instanceof MediaFile).toBe(true)
33+
expect(audioResult, '🚨 AudioFile result should contain "audio" - check that AudioFile extends MediaFile and overrides playFile behavior').toContain('audio')
34+
expect(videoResult, '🚨 VideoFile result should contain "video" - check that VideoFile extends MediaFile and overrides playFile behavior').toContain('video')
35+
expect(audio instanceof MediaFile, '🚨 AudioFile should be an instance of MediaFile - check that AudioFile extends MediaFile').toBe(true)
36+
expect(video instanceof MediaFile, '🚨 VideoFile should be an instance of MediaFile - check that VideoFile extends MediaFile').toBe(true)
3737
})

0 commit comments

Comments
 (0)