SlideShare ist ein Scribd-Unternehmen logo
1 von 69
Downloaden Sie, um offline zu lesen
!
The Testing Games: Mocking, yay!
And may the mocks be ever in your favour
I like testing
It allows me to prove my code works
But I’m also quite lazy
And I know I’m not alone
Improving our own tests
Improving our own tests
1. Create a set of guidelines
Improving our own tests
1. Create a set of guidelines
2. Update the testing framework’s DSL (we use Quick)
Improving our own tests
1. Create a set of guidelines
2. Update the testing framework’s DSL (we use Quick)
3. Change the way we think about mocking
class AuthenticationServiceTests: QuickSpec {
override func spec() {
describe("The Service") {
it("Should log in") {
}
}
}
}
class AuthenticationServiceTests: QuickSpec {
override func spec() {
describe("The Service") {
it("Should log in") {
}
}
}
}
This isn’t too bad, right?
class AuthenticationServiceTests: QuickSpec {
override func spec() {
describe("The Service") {
it("Should log in") {
let service = AuthenticationService(api: self)
service.loginUser(withUsername: "Donny", password: "password") { user, error in
expect(user?.name) == "Donny"
expect(user?.id) == 1
}
}
}
}
}
extension AuthenticationServiceTests: AuthenticationAPi {
func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) {
let json = """
{"status": "ok", "user": {"id": 1, "name": “Donny"}}
""".data(using: .utf8)
callback(json, nil)
}
}
This is hard to read!
class AuthenticationServiceTests: QuickSpec {
override func spec() {
describe("The Service") {
it("Should log in") {
let service = AuthenticationService(api: self)
service.loginUser(withUsername: "Donny", password: "password") { user, error in
expect(user?.name) == "Donny"
expect(user?.id) == 1
}
}
}
}
}
extension AuthenticationServiceTests: AuthenticationAPi {
func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) {
let json = """
{"status": "ok", "user": {"id": 1, "name": “Donny"}}
""".data(using: .utf8)
callback(json, nil)
}
}
All values are hardcoded
class AuthenticationServiceTests: QuickSpec {
override func spec() {
describe("The Service") {
it("Should log in") {
let service = AuthenticationService(api: self)
service.loginUser(withUsername: "Donny", password: "password") { user, error in
expect(user?.name) == "Donny"
expect(user?.id) == 1
}
}
}
}
}
extension AuthenticationServiceTests: AuthenticationAPi {
func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) {
let json = """
{"status": "ok", "user": {"id": 1, "name": “Donny"}}
""".data(using: .utf8)
callback(json, nil)
}
}
The json is inline
class AuthenticationServiceTests: QuickSpec {
override func spec() {
describe("The Service") {
it("Should log in") {
let service = AuthenticationService(api: self)
service.loginUser(withUsername: "Donny", password: "password") { user, error in
expect(user?.name) == "Donny"
expect(user?.id) == 1
}
}
}
}
}
extension AuthenticationServiceTests: AuthenticationAPi {
func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) {
let json = """
{"status": "ok", "user": {"id": 1, "name": “Donny"}}
""".data(using: .utf8)
callback(json, nil)
}
}
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let validationError = """

{"status": "error", "code": 1003, "message": "Username or password missing"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let validationError = """

{"status": "error", "code": 1003, "message": "Username or password missing"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let validationError = """

{"status": "error", "code": 1003, "message": "Username or password missing"}

"""
let serverError = """

{"status": "error", "code": 1004, "message": "Something went wrong on the server"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let validationError = """

{"status": "error", "code": 1003, "message": "Username or password missing"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let validationError = """

{"status": "error", "code": 1003, "message": "Username or password missing"}

"""
let passwordError = """

{"status": "error", "code": 1001, "message": "Wrong password"}

"""
let usernameError = """

{"status": "error", "code": 1002, "message": "User not found"}

"""
let validationError = """

{"status": "error", "code": 1003, "message": "Username or password missing"}

"""
let serverError = """

{"status": "error", "code": 1004, "message": "Something went wrong on the server"}

"""
let json = """
{
"_id": "5b05709156da3c5e1a933dce",
"index": 0,
"guid": "ca6507f6-f462-478e-ad4d-f9e46f9ee88a",
"isActive": true,
"balance": "$3,125.88",
"picture": "http://placehold.it/32x32",
"age": 38,
"eyeColor": "green",
"name": {
"first": "Dunlap",
"last": "Lott"
},
"company": "FUELTON",
"email": "dunlap.lott@fuelton.biz",
"phone": "+1 (888) 407-2414",
"address": "883 Nixon Court, Wolcott, Marshall Islands, 2469",
"about": "Dolor tempor ullamco deserunt tempor sunt esse irure ut fugiat. Irure occaecat fugiat deserunt
ad. Ipsum laborum excepteur excepteur dolore qui aliqua. Ad tempor culpa commodo laborum laboris ut
amet elit est cupidatat consectetur nostrud consequat.",
"registered": "Saturday, August 5, 2017 12:57 PM",
let json = """
{
"_id": "5b05709156da3c5e1a933dce",
"index": 0,
"guid": "ca6507f6-f462-478e-ad4d-f9e46f9ee88a",
"isActive": true,
"balance": "$3,125.88",
"picture": "http://placehold.it/32x32",
"age": 38,
"eyeColor": "green",
"name": {
"first": "Dunlap",
"last": "Lott"
},
"company": "FUELTON",
"email": "dunlap.lott@fuelton.biz",
"phone": "+1 (888) 407-2414",
"address": "883 Nixon Court, Wolcott, Marshall Islands, 2469",
"about": "Dolor tempor ullamco deserunt tempor sunt esse irure ut fugiat. Irure occaecat fugiat deserunt
ad. Ipsum laborum excepteur excepteur dolore qui aliqua. Ad tempor culpa commodo laborum laboris ut
amet elit est cupidatat consectetur nostrud consequat.",
"registered": "Saturday, August 5, 2017 12:57 PM",
😒
Duh! You should use fixtures
let passwordError = ErrorFixture(type: .password)
let usernameError = ErrorFixture(type: .username)
let validationError = ErrorFixture(type: .validation)
let serverError = ErrorFixture(type: .server)
extension AuthenticationServiceTests: AuthenticationAPI {
func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) {
let json = """
{"status": "ok", "user": {"id": 1, "name": "Donny"}
""".data(using: .utf8)
callback(json, nil)
}
}
extension AuthenticationServiceTests: AuthenticationAPI {
func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) {
let fixture = AuthenticationFixture(type: .success)
callback(fixture.data, nil)
}
}
class AuthenticationServiceTests: QuickSpec {
override func spec() {
describe("The Service") {
it("Should log in") {
let service = AuthenticationService(api: self)
service.loginUser(withUsername: "Donny", password: "password") { user, error in
expect(user?.name) == "Donny"
expect(user?.id) == 1
}
}
}
}
}
We just made the hardcoded values worse…
Swift 4.0, to the rescue
Swift 4.0, to the rescue
{JSON} Decodable User
Swift 4.0, to the rescue
{JSON} Decodable User
Why not do this?
Swift 4.0, to the rescue
{JSON} Decodable User
Why not do this?
{JSON} Encodable User
describe("The Authentication Service") {
it("Should log in a user") {
let mockUser = User(name: "Donny", id: 1)
let userData = try! JSONEncoder().encode(mockUser)
let authenticationApi = MockAuthenticationApi(responseData: userData, error: nil)
let service = AuthenticationService(api: authenticationApi)
service.loginUser(withUsername: "Donny", password: "password") { user, error in
expect(user?.name) == mockUser.name
expect(user?.id) == mockUser.id
}
}
}
Now we’re getting somewhere!
struct Prize: Codable {
enum PrizeType: Int, Codable {
case regular = 0, main = 1
}
let id: Int
let type: PrizeType
let label: String
}
struct Play: Codable {
let id: Int
let acquiredPrizes: [Prize]
}
struct Game: Codable {
let id: Int
let availablePrizes: [Prize]
let name: String
let closeDate: Date
let plays: [Play]
let maxPlayCount: Int
}
struct PlayResponse {
let game: Game
let currentPlay: Play
}
But we’re not quite there
The final steps
Mockable
protocol Mockable: Codable {
associatedtype BaseType: Decodable
}
extension Mockable {
var data: Data? {
return try? JSONEncoder().encode(self)
}
var baseType: BaseType? {
guard let data = self.data
else { return nil }
return try? JSONDecoder().decode(BaseType.self, from: data)
}
}
Mockable
protocol Mockable: Codable {
associatedtype BaseType: Decodable
}
extension Mockable {
var data: Data? {
return try? JSONEncoder().encode(self)
}
var baseType: BaseType? {
guard let data = self.data
else { return nil }
return try? JSONDecoder().decode(BaseType.self, from: data)
}
}
Mockable
protocol Mockable: Codable {
associatedtype BaseType: Decodable
}
extension Mockable {
var data: Data? {
return try? JSONEncoder().encode(self)
}
var baseType: BaseType? {
guard let data = self.data
else { return nil }
return try? JSONDecoder().decode(BaseType.self, from: data)
}
}
Mock all the things!
class MockPrize: Mockable {
typealias BaseType = Prize
enum PrizeType: Int, Codable {
case regular = 0, main = 1
}
var id = 0
var type = PrizeType.regular
var label = "Default Label"
}
class MockPlay: Mockable {
typealias BaseType = Play
var id = 0
var acquiredPrizes = [MockPrize]()
}
class MockGame: Mockable {
typealias BaseType = Game
var id = 0
var availablePrizes = [MockPrize]()
var name = "Default game name"
var closeDate = Date()
var plays = [MockPlay]()
var maxPlayCount = 1
}
class MockPlayResponse: Mockable {
typealias BaseType = PlayResponse
var game = MockGame()
var currentPlay = MockPlay()
}
Mock all the things!
I know… it would be great if you could skip this!
class MockPrize: Mockable {
typealias BaseType = Prize
enum PrizeType: Int, Codable {
case regular = 0, main = 1
}
var id = 0
var type = PrizeType.regular
var label = "Default Label"
}
class MockPlay: Mockable {
typealias BaseType = Play
var id = 0
var acquiredPrizes = [MockPrize]()
}
class MockGame: Mockable {
typealias BaseType = Game
var id = 0
var availablePrizes = [MockPrize]()
var name = "Default game name"
var closeDate = Date()
var plays = [MockPlay]()
var maxPlayCount = 1
}
class MockPlayResponse: Mockable {
typealias BaseType = PlayResponse
var game = MockGame()
var currentPlay = MockPlay()
}
What does this give us?
What does this give us?
• Mutable “clones” of the models
What does this give us?
• Mutable “clones” of the models
• (Sensible) Default values
What does this give us?
• Mutable “clones” of the models
• (Sensible) Default values
• Objects that are ready to be encoded to JSON
What does this give us?
• Mutable “clones” of the models
• (Sensible) Default values
• Objects that are ready to be encoded to JSON
• Some spare time because we don’t need to collect mocks for each
configuration
What does this give us?
• Mutable “clones” of the models
• (Sensible) Default values
• Objects that are ready to be encoded to JSON
• Some spare time because we don’t need to collect mocks for each
configuration
• Typo-free comparisons
Now this is MotherMocking nice!
describe("When playing a game and the server returns no prizes") {
let gameData = MockGame().data
let gameApi = MockGameApi(preparedData: gameData, error: nil)
let gameService = GameService(api: gameApi)
it("The parsed model should not contain prizes") {
gameService.play { game, error in
expect(game?.plays.first?.acquiredPrizes.count) == 0
}
}
}
Now this is MotherMocking nice!
describe("When playing a game and the server returns a main prize") {
let game = MockGame()
let play = MockPlay()
let prize = MockPrize()
prize.type = .main
play.acquiredPrizes = [prize]
game.plays = [play]
let gameApi = MockGameApi(preparedData: game.data, error: nil)
let gameService = GameService(api: gameApi)
it("The parsed model should contain a single main prize") {
gameService.play { game, error in
expect(game?.plays.first?.acquiredPrizes.count) == 1
expect(game?.plays.first?.acquiredPrizes.first?.type) == .main
}
}
}
Easier configuration of mocks
encourages testing more cases
What is not easier with this approach?
What is not easier with this approach?
• It’s hard to test what your code does if you get malformed json
What is not easier with this approach?
• It’s hard to test what your code does if you get malformed json
• You can’t purposefully omit certain properties (unless you make a special
mock class for that case)
What is not easier with this approach?
• It’s hard to test what your code does if you get malformed json
• You can’t purposefully omit certain properties (unless you make a special
mock class for that case)
• You still need a single source of truth for what data goes into a model. You
could coordinate some sort of setup with the back-end team so you can
fetch a blueprint for each model
Some final notes
Some final notes
• It’s easier to mock responses if your api services return Data instead of
models
Some final notes
• It’s easier to mock responses if your api services return Data instead of
models
• Make smaller services so you can easily create focussed mocks for them
Some final notes
• It’s easier to mock responses if your api services return Data instead of
models
• Make smaller services so you can easily create focussed mocks for them
• Your tests shouldn’t rely on a network connection
Some final notes
• It’s easier to mock responses if your api services return Data instead of
models
• Make smaller services so you can easily create focussed mocks for them
• Your tests shouldn’t rely on a network connection
• This (obviously) is not a silver bullet
Some final notes
• It’s easier to mock responses if your api services return Data instead of
models
• Make smaller services so you can easily create focussed mocks for them
• Your tests shouldn’t rely on a network connection
• This (obviously) is not a silver bullet
• Large or complex configurations can still be fed to Mockable objects since
they conform to Codable
#

Weitere ähnliche Inhalte

Was ist angesagt?

Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Remy Sharp
 
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...Dev2Dev
 
The (unknown) collections module
The (unknown) collections moduleThe (unknown) collections module
The (unknown) collections modulePablo Enfedaque
 
MongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDB
MongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDBMongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDB
MongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDBMongoDB
 
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDBMongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDBMongoDB
 
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebBDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebChristian Baranowski
 
Getting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETGetting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETTomas Jansson
 
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web TestingBDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web TestingJohn Ferguson Smart Limited
 
MongoDB World 2016: Deciphering .explain() Output
MongoDB World 2016: Deciphering .explain() OutputMongoDB World 2016: Deciphering .explain() Output
MongoDB World 2016: Deciphering .explain() OutputMongoDB
 
Event handling using jQuery
Event handling using jQueryEvent handling using jQuery
Event handling using jQueryIban Martinez
 
To infinity and beyond
To infinity and beyondTo infinity and beyond
To infinity and beyondclintongormley
 
Mongo db mug_2012-02-07
Mongo db mug_2012-02-07Mongo db mug_2012-02-07
Mongo db mug_2012-02-07Will Button
 
MongoDB Performance Tuning
MongoDB Performance TuningMongoDB Performance Tuning
MongoDB Performance TuningPuneet Behl
 
Round pegs and square holes
Round pegs and square holesRound pegs and square holes
Round pegs and square holesDaniel Greenfeld
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperGiordano Scalzo
 
Conquering JSONB in PostgreSQL
Conquering JSONB in PostgreSQLConquering JSONB in PostgreSQL
Conquering JSONB in PostgreSQLInes Panker
 

Was ist angesagt? (18)

Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)Is HTML5 Ready? (workshop)
Is HTML5 Ready? (workshop)
 
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
 
The (unknown) collections module
The (unknown) collections moduleThe (unknown) collections module
The (unknown) collections module
 
MongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDB
MongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDBMongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDB
MongoDB .local Paris 2020: La puissance du Pipeline d'Agrégation de MongoDB
 
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDBMongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
MongoDB .local Munich 2019: Tips and Tricks++ for Querying and Indexing MongoDB
 
Perl object ?
Perl object ?Perl object ?
Perl object ?
 
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebBDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
 
Getting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETGetting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NET
 
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web TestingBDD, ATDD, Page Objects: The Road to Sustainable Web Testing
BDD, ATDD, Page Objects: The Road to Sustainable Web Testing
 
Url programming
Url programmingUrl programming
Url programming
 
MongoDB World 2016: Deciphering .explain() Output
MongoDB World 2016: Deciphering .explain() OutputMongoDB World 2016: Deciphering .explain() Output
MongoDB World 2016: Deciphering .explain() Output
 
Event handling using jQuery
Event handling using jQueryEvent handling using jQuery
Event handling using jQuery
 
To infinity and beyond
To infinity and beyondTo infinity and beyond
To infinity and beyond
 
Mongo db mug_2012-02-07
Mongo db mug_2012-02-07Mongo db mug_2012-02-07
Mongo db mug_2012-02-07
 
MongoDB Performance Tuning
MongoDB Performance TuningMongoDB Performance Tuning
MongoDB Performance Tuning
 
Round pegs and square holes
Round pegs and square holesRound pegs and square holes
Round pegs and square holes
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapper
 
Conquering JSONB in PostgreSQL
Conquering JSONB in PostgreSQLConquering JSONB in PostgreSQL
Conquering JSONB in PostgreSQL
 

Ähnlich wie The Testing Games: Mocking, yay!

How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014Guillaume POTIER
 
Opa presentation at GamesJs
Opa presentation at GamesJsOpa presentation at GamesJs
Opa presentation at GamesJsHenri Binsztok
 
10 Rules for Safer Code
10 Rules for Safer Code10 Rules for Safer Code
10 Rules for Safer CodeQuang Ngoc
 
10 Rules for Safer Code [Odoo Experience 2016]
10 Rules for Safer Code [Odoo Experience 2016]10 Rules for Safer Code [Odoo Experience 2016]
10 Rules for Safer Code [Odoo Experience 2016]Olivier Dony
 
Amazon Web Services Security
Amazon Web Services SecurityAmazon Web Services Security
Amazon Web Services SecurityJason Chan
 
Nodejs functional programming and schema validation lightning talk
Nodejs   functional programming and schema validation lightning talkNodejs   functional programming and schema validation lightning talk
Nodejs functional programming and schema validation lightning talkDeepank Gupta
 
Security Slicing for Auditing XML, XPath, and SQL Injection Vulnerabilities
Security Slicing for Auditing XML, XPath, and SQL Injection VulnerabilitiesSecurity Slicing for Auditing XML, XPath, and SQL Injection Vulnerabilities
Security Slicing for Auditing XML, XPath, and SQL Injection VulnerabilitiesLionel Briand
 
Take Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorksTake Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorksNodejsFoundation
 
JSON Schema: Your API's Secret Weapon
JSON Schema: Your API's Secret WeaponJSON Schema: Your API's Secret Weapon
JSON Schema: Your API's Secret WeaponPete Gamache
 
Take Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorksTake Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorksNodejsFoundation
 
MongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-esMongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-esMongoDB
 
To Err Is Human
To Err Is HumanTo Err Is Human
To Err Is HumanAlex Liu
 
Third Party Auth in WebObjects
Third Party Auth in WebObjectsThird Party Auth in WebObjects
Third Party Auth in WebObjectsWO Community
 
Open Source Search: An Analysis
Open Source Search: An AnalysisOpen Source Search: An Analysis
Open Source Search: An AnalysisJustin Finkelstein
 
JWT - To authentication and beyond!
JWT - To authentication and beyond!JWT - To authentication and beyond!
JWT - To authentication and beyond!Luís Cobucci
 
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)Joel Lord
 
Html basics 11 form validation
Html basics 11 form validationHtml basics 11 form validation
Html basics 11 form validationH K
 
GraphQL & Relay - 串起前後端世界的橋樑
GraphQL & Relay - 串起前後端世界的橋樑GraphQL & Relay - 串起前後端世界的橋樑
GraphQL & Relay - 串起前後端世界的橋樑Pokai Chang
 
Web Security - Hands-on
Web Security - Hands-onWeb Security - Hands-on
Web Security - Hands-onAndrea Valenza
 

Ähnlich wie The Testing Games: Mocking, yay! (20)

How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014
 
Opa presentation at GamesJs
Opa presentation at GamesJsOpa presentation at GamesJs
Opa presentation at GamesJs
 
10 Rules for Safer Code
10 Rules for Safer Code10 Rules for Safer Code
10 Rules for Safer Code
 
10 Rules for Safer Code [Odoo Experience 2016]
10 Rules for Safer Code [Odoo Experience 2016]10 Rules for Safer Code [Odoo Experience 2016]
10 Rules for Safer Code [Odoo Experience 2016]
 
Amazon Web Services Security
Amazon Web Services SecurityAmazon Web Services Security
Amazon Web Services Security
 
Nodejs functional programming and schema validation lightning talk
Nodejs   functional programming and schema validation lightning talkNodejs   functional programming and schema validation lightning talk
Nodejs functional programming and schema validation lightning talk
 
Security Slicing for Auditing XML, XPath, and SQL Injection Vulnerabilities
Security Slicing for Auditing XML, XPath, and SQL Injection VulnerabilitiesSecurity Slicing for Auditing XML, XPath, and SQL Injection Vulnerabilities
Security Slicing for Auditing XML, XPath, and SQL Injection Vulnerabilities
 
Take Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorksTake Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorks
 
JSON Schema: Your API's Secret Weapon
JSON Schema: Your API's Secret WeaponJSON Schema: Your API's Secret Weapon
JSON Schema: Your API's Secret Weapon
 
Take Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorksTake Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorks
 
MongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-esMongoDB World 2019: Life In Stitch-es
MongoDB World 2019: Life In Stitch-es
 
To Err Is Human
To Err Is HumanTo Err Is Human
To Err Is Human
 
Third Party Auth in WebObjects
Third Party Auth in WebObjectsThird Party Auth in WebObjects
Third Party Auth in WebObjects
 
Open Source Search: An Analysis
Open Source Search: An AnalysisOpen Source Search: An Analysis
Open Source Search: An Analysis
 
Dependency injection in Scala
Dependency injection in ScalaDependency injection in Scala
Dependency injection in Scala
 
JWT - To authentication and beyond!
JWT - To authentication and beyond!JWT - To authentication and beyond!
JWT - To authentication and beyond!
 
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
 
Html basics 11 form validation
Html basics 11 form validationHtml basics 11 form validation
Html basics 11 form validation
 
GraphQL & Relay - 串起前後端世界的橋樑
GraphQL & Relay - 串起前後端世界的橋樑GraphQL & Relay - 串起前後端世界的橋樑
GraphQL & Relay - 串起前後端世界的橋樑
 
Web Security - Hands-on
Web Security - Hands-onWeb Security - Hands-on
Web Security - Hands-on
 

Mehr von Donny Wals

Your 🧠 on Swift Concurrency
Your 🧠 on Swift ConcurrencyYour 🧠 on Swift Concurrency
Your 🧠 on Swift ConcurrencyDonny Wals
 
Using Combine, SwiftUI and callAsFunction to build an experimental localizati...
Using Combine, SwiftUI and callAsFunction to build an experimental localizati...Using Combine, SwiftUI and callAsFunction to build an experimental localizati...
Using Combine, SwiftUI and callAsFunction to build an experimental localizati...Donny Wals
 
The combine triad
The combine triadThe combine triad
The combine triadDonny Wals
 
Building reusable components with generics and protocols
Building reusable components with generics and protocolsBuilding reusable components with generics and protocols
Building reusable components with generics and protocolsDonny Wals
 
Adopting tdd in the workplace
Adopting tdd in the workplaceAdopting tdd in the workplace
Adopting tdd in the workplaceDonny Wals
 
Me and my importers
Me and my importersMe and my importers
Me and my importersDonny Wals
 
Adopting tdd in the workplace
Adopting tdd in the workplaceAdopting tdd in the workplace
Adopting tdd in the workplaceDonny Wals
 
In Defense Of Core Data
In Defense Of Core DataIn Defense Of Core Data
In Defense Of Core DataDonny Wals
 
Effectively Producing And Shipping Frameworks For Multiple Platforms
Effectively Producing And Shipping Frameworks For Multiple PlatformsEffectively Producing And Shipping Frameworks For Multiple Platforms
Effectively Producing And Shipping Frameworks For Multiple PlatformsDonny Wals
 
Improving apps with iOS 10 notifications (do iOS 2016)
Improving apps with iOS 10 notifications (do iOS 2016)Improving apps with iOS 10 notifications (do iOS 2016)
Improving apps with iOS 10 notifications (do iOS 2016)Donny Wals
 
Talk - git task managers and ci
Talk - git task managers and ciTalk - git task managers and ci
Talk - git task managers and ciDonny Wals
 
Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...
Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...
Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...Donny Wals
 
Marketing strategie Arto
Marketing strategie ArtoMarketing strategie Arto
Marketing strategie ArtoDonny Wals
 
Hoorcollege Flash 9-2-2012
Hoorcollege Flash 9-2-2012Hoorcollege Flash 9-2-2012
Hoorcollege Flash 9-2-2012Donny Wals
 

Mehr von Donny Wals (14)

Your 🧠 on Swift Concurrency
Your 🧠 on Swift ConcurrencyYour 🧠 on Swift Concurrency
Your 🧠 on Swift Concurrency
 
Using Combine, SwiftUI and callAsFunction to build an experimental localizati...
Using Combine, SwiftUI and callAsFunction to build an experimental localizati...Using Combine, SwiftUI and callAsFunction to build an experimental localizati...
Using Combine, SwiftUI and callAsFunction to build an experimental localizati...
 
The combine triad
The combine triadThe combine triad
The combine triad
 
Building reusable components with generics and protocols
Building reusable components with generics and protocolsBuilding reusable components with generics and protocols
Building reusable components with generics and protocols
 
Adopting tdd in the workplace
Adopting tdd in the workplaceAdopting tdd in the workplace
Adopting tdd in the workplace
 
Me and my importers
Me and my importersMe and my importers
Me and my importers
 
Adopting tdd in the workplace
Adopting tdd in the workplaceAdopting tdd in the workplace
Adopting tdd in the workplace
 
In Defense Of Core Data
In Defense Of Core DataIn Defense Of Core Data
In Defense Of Core Data
 
Effectively Producing And Shipping Frameworks For Multiple Platforms
Effectively Producing And Shipping Frameworks For Multiple PlatformsEffectively Producing And Shipping Frameworks For Multiple Platforms
Effectively Producing And Shipping Frameworks For Multiple Platforms
 
Improving apps with iOS 10 notifications (do iOS 2016)
Improving apps with iOS 10 notifications (do iOS 2016)Improving apps with iOS 10 notifications (do iOS 2016)
Improving apps with iOS 10 notifications (do iOS 2016)
 
Talk - git task managers and ci
Talk - git task managers and ciTalk - git task managers and ci
Talk - git task managers and ci
 
Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...
Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...
Developing in the Fastlane -> How LookLive uses Fastlane to automate and spee...
 
Marketing strategie Arto
Marketing strategie ArtoMarketing strategie Arto
Marketing strategie Arto
 
Hoorcollege Flash 9-2-2012
Hoorcollege Flash 9-2-2012Hoorcollege Flash 9-2-2012
Hoorcollege Flash 9-2-2012
 

Kürzlich hochgeladen

The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfCionsystems
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 

Kürzlich hochgeladen (20)

The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
Active Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdfActive Directory Penetration Testing, cionsystems.com.pdf
Active Directory Penetration Testing, cionsystems.com.pdf
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 

The Testing Games: Mocking, yay!

  • 1. !
  • 2. The Testing Games: Mocking, yay! And may the mocks be ever in your favour
  • 3. I like testing It allows me to prove my code works
  • 4. But I’m also quite lazy And I know I’m not alone
  • 6. Improving our own tests 1. Create a set of guidelines
  • 7. Improving our own tests 1. Create a set of guidelines 2. Update the testing framework’s DSL (we use Quick)
  • 8. Improving our own tests 1. Create a set of guidelines 2. Update the testing framework’s DSL (we use Quick) 3. Change the way we think about mocking
  • 9. class AuthenticationServiceTests: QuickSpec { override func spec() { describe("The Service") { it("Should log in") { } } } }
  • 10. class AuthenticationServiceTests: QuickSpec { override func spec() { describe("The Service") { it("Should log in") { } } } } This isn’t too bad, right?
  • 11. class AuthenticationServiceTests: QuickSpec { override func spec() { describe("The Service") { it("Should log in") { let service = AuthenticationService(api: self) service.loginUser(withUsername: "Donny", password: "password") { user, error in expect(user?.name) == "Donny" expect(user?.id) == 1 } } } } } extension AuthenticationServiceTests: AuthenticationAPi { func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) { let json = """ {"status": "ok", "user": {"id": 1, "name": “Donny"}} """.data(using: .utf8) callback(json, nil) } }
  • 12. This is hard to read! class AuthenticationServiceTests: QuickSpec { override func spec() { describe("The Service") { it("Should log in") { let service = AuthenticationService(api: self) service.loginUser(withUsername: "Donny", password: "password") { user, error in expect(user?.name) == "Donny" expect(user?.id) == 1 } } } } } extension AuthenticationServiceTests: AuthenticationAPi { func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) { let json = """ {"status": "ok", "user": {"id": 1, "name": “Donny"}} """.data(using: .utf8) callback(json, nil) } }
  • 13. All values are hardcoded class AuthenticationServiceTests: QuickSpec { override func spec() { describe("The Service") { it("Should log in") { let service = AuthenticationService(api: self) service.loginUser(withUsername: "Donny", password: "password") { user, error in expect(user?.name) == "Donny" expect(user?.id) == 1 } } } } } extension AuthenticationServiceTests: AuthenticationAPi { func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) { let json = """ {"status": "ok", "user": {"id": 1, "name": “Donny"}} """.data(using: .utf8) callback(json, nil) } }
  • 14. The json is inline class AuthenticationServiceTests: QuickSpec { override func spec() { describe("The Service") { it("Should log in") { let service = AuthenticationService(api: self) service.loginUser(withUsername: "Donny", password: "password") { user, error in expect(user?.name) == "Donny" expect(user?.id) == 1 } } } } } extension AuthenticationServiceTests: AuthenticationAPi { func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) { let json = """ {"status": "ok", "user": {"id": 1, "name": “Donny"}} """.data(using: .utf8) callback(json, nil) } }
  • 15.
  • 16. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """
  • 17. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """
  • 18. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """
  • 19. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """
  • 20. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """ let validationError = """
 {"status": "error", "code": 1003, "message": "Username or password missing"}
 """
  • 21. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """ let validationError = """
 {"status": "error", "code": 1003, "message": "Username or password missing"}
 """
  • 22. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """ let validationError = """
 {"status": "error", "code": 1003, "message": "Username or password missing"}
 """ let serverError = """
 {"status": "error", "code": 1004, "message": "Something went wrong on the server"}
 """
  • 23.
  • 24. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """
  • 25. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """
  • 26. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """
  • 27. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """
  • 28. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """ let validationError = """
 {"status": "error", "code": 1003, "message": "Username or password missing"}
 """
  • 29. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """ let validationError = """
 {"status": "error", "code": 1003, "message": "Username or password missing"}
 """
  • 30. let passwordError = """
 {"status": "error", "code": 1001, "message": "Wrong password"}
 """ let usernameError = """
 {"status": "error", "code": 1002, "message": "User not found"}
 """ let validationError = """
 {"status": "error", "code": 1003, "message": "Username or password missing"}
 """ let serverError = """
 {"status": "error", "code": 1004, "message": "Something went wrong on the server"}
 """
  • 31. let json = """ { "_id": "5b05709156da3c5e1a933dce", "index": 0, "guid": "ca6507f6-f462-478e-ad4d-f9e46f9ee88a", "isActive": true, "balance": "$3,125.88", "picture": "http://placehold.it/32x32", "age": 38, "eyeColor": "green", "name": { "first": "Dunlap", "last": "Lott" }, "company": "FUELTON", "email": "dunlap.lott@fuelton.biz", "phone": "+1 (888) 407-2414", "address": "883 Nixon Court, Wolcott, Marshall Islands, 2469", "about": "Dolor tempor ullamco deserunt tempor sunt esse irure ut fugiat. Irure occaecat fugiat deserunt ad. Ipsum laborum excepteur excepteur dolore qui aliqua. Ad tempor culpa commodo laborum laboris ut amet elit est cupidatat consectetur nostrud consequat.", "registered": "Saturday, August 5, 2017 12:57 PM",
  • 32. let json = """ { "_id": "5b05709156da3c5e1a933dce", "index": 0, "guid": "ca6507f6-f462-478e-ad4d-f9e46f9ee88a", "isActive": true, "balance": "$3,125.88", "picture": "http://placehold.it/32x32", "age": 38, "eyeColor": "green", "name": { "first": "Dunlap", "last": "Lott" }, "company": "FUELTON", "email": "dunlap.lott@fuelton.biz", "phone": "+1 (888) 407-2414", "address": "883 Nixon Court, Wolcott, Marshall Islands, 2469", "about": "Dolor tempor ullamco deserunt tempor sunt esse irure ut fugiat. Irure occaecat fugiat deserunt ad. Ipsum laborum excepteur excepteur dolore qui aliqua. Ad tempor culpa commodo laborum laboris ut amet elit est cupidatat consectetur nostrud consequat.", "registered": "Saturday, August 5, 2017 12:57 PM", 😒
  • 33. Duh! You should use fixtures
  • 34. let passwordError = ErrorFixture(type: .password) let usernameError = ErrorFixture(type: .username) let validationError = ErrorFixture(type: .validation) let serverError = ErrorFixture(type: .server)
  • 35. extension AuthenticationServiceTests: AuthenticationAPI { func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) { let json = """ {"status": "ok", "user": {"id": 1, "name": "Donny"} """.data(using: .utf8) callback(json, nil) } }
  • 36. extension AuthenticationServiceTests: AuthenticationAPI { func loginUser(withUsername username: String, password: String, callback: (Data?, Error?) -> Void) { let fixture = AuthenticationFixture(type: .success) callback(fixture.data, nil) } }
  • 37. class AuthenticationServiceTests: QuickSpec { override func spec() { describe("The Service") { it("Should log in") { let service = AuthenticationService(api: self) service.loginUser(withUsername: "Donny", password: "password") { user, error in expect(user?.name) == "Donny" expect(user?.id) == 1 } } } } } We just made the hardcoded values worse…
  • 38. Swift 4.0, to the rescue
  • 39. Swift 4.0, to the rescue {JSON} Decodable User
  • 40. Swift 4.0, to the rescue {JSON} Decodable User Why not do this?
  • 41. Swift 4.0, to the rescue {JSON} Decodable User Why not do this? {JSON} Encodable User
  • 42. describe("The Authentication Service") { it("Should log in a user") { let mockUser = User(name: "Donny", id: 1) let userData = try! JSONEncoder().encode(mockUser) let authenticationApi = MockAuthenticationApi(responseData: userData, error: nil) let service = AuthenticationService(api: authenticationApi) service.loginUser(withUsername: "Donny", password: "password") { user, error in expect(user?.name) == mockUser.name expect(user?.id) == mockUser.id } } } Now we’re getting somewhere!
  • 43. struct Prize: Codable { enum PrizeType: Int, Codable { case regular = 0, main = 1 } let id: Int let type: PrizeType let label: String } struct Play: Codable { let id: Int let acquiredPrizes: [Prize] } struct Game: Codable { let id: Int let availablePrizes: [Prize] let name: String let closeDate: Date let plays: [Play] let maxPlayCount: Int } struct PlayResponse { let game: Game let currentPlay: Play } But we’re not quite there
  • 45. Mockable protocol Mockable: Codable { associatedtype BaseType: Decodable } extension Mockable { var data: Data? { return try? JSONEncoder().encode(self) } var baseType: BaseType? { guard let data = self.data else { return nil } return try? JSONDecoder().decode(BaseType.self, from: data) } }
  • 46. Mockable protocol Mockable: Codable { associatedtype BaseType: Decodable } extension Mockable { var data: Data? { return try? JSONEncoder().encode(self) } var baseType: BaseType? { guard let data = self.data else { return nil } return try? JSONDecoder().decode(BaseType.self, from: data) } }
  • 47. Mockable protocol Mockable: Codable { associatedtype BaseType: Decodable } extension Mockable { var data: Data? { return try? JSONEncoder().encode(self) } var baseType: BaseType? { guard let data = self.data else { return nil } return try? JSONDecoder().decode(BaseType.self, from: data) } }
  • 48. Mock all the things! class MockPrize: Mockable { typealias BaseType = Prize enum PrizeType: Int, Codable { case regular = 0, main = 1 } var id = 0 var type = PrizeType.regular var label = "Default Label" } class MockPlay: Mockable { typealias BaseType = Play var id = 0 var acquiredPrizes = [MockPrize]() } class MockGame: Mockable { typealias BaseType = Game var id = 0 var availablePrizes = [MockPrize]() var name = "Default game name" var closeDate = Date() var plays = [MockPlay]() var maxPlayCount = 1 } class MockPlayResponse: Mockable { typealias BaseType = PlayResponse var game = MockGame() var currentPlay = MockPlay() }
  • 49. Mock all the things! I know… it would be great if you could skip this! class MockPrize: Mockable { typealias BaseType = Prize enum PrizeType: Int, Codable { case regular = 0, main = 1 } var id = 0 var type = PrizeType.regular var label = "Default Label" } class MockPlay: Mockable { typealias BaseType = Play var id = 0 var acquiredPrizes = [MockPrize]() } class MockGame: Mockable { typealias BaseType = Game var id = 0 var availablePrizes = [MockPrize]() var name = "Default game name" var closeDate = Date() var plays = [MockPlay]() var maxPlayCount = 1 } class MockPlayResponse: Mockable { typealias BaseType = PlayResponse var game = MockGame() var currentPlay = MockPlay() }
  • 50. What does this give us?
  • 51. What does this give us? • Mutable “clones” of the models
  • 52. What does this give us? • Mutable “clones” of the models • (Sensible) Default values
  • 53. What does this give us? • Mutable “clones” of the models • (Sensible) Default values • Objects that are ready to be encoded to JSON
  • 54. What does this give us? • Mutable “clones” of the models • (Sensible) Default values • Objects that are ready to be encoded to JSON • Some spare time because we don’t need to collect mocks for each configuration
  • 55. What does this give us? • Mutable “clones” of the models • (Sensible) Default values • Objects that are ready to be encoded to JSON • Some spare time because we don’t need to collect mocks for each configuration • Typo-free comparisons
  • 56. Now this is MotherMocking nice! describe("When playing a game and the server returns no prizes") { let gameData = MockGame().data let gameApi = MockGameApi(preparedData: gameData, error: nil) let gameService = GameService(api: gameApi) it("The parsed model should not contain prizes") { gameService.play { game, error in expect(game?.plays.first?.acquiredPrizes.count) == 0 } } }
  • 57. Now this is MotherMocking nice! describe("When playing a game and the server returns a main prize") { let game = MockGame() let play = MockPlay() let prize = MockPrize() prize.type = .main play.acquiredPrizes = [prize] game.plays = [play] let gameApi = MockGameApi(preparedData: game.data, error: nil) let gameService = GameService(api: gameApi) it("The parsed model should contain a single main prize") { gameService.play { game, error in expect(game?.plays.first?.acquiredPrizes.count) == 1 expect(game?.plays.first?.acquiredPrizes.first?.type) == .main } } }
  • 58. Easier configuration of mocks encourages testing more cases
  • 59. What is not easier with this approach?
  • 60. What is not easier with this approach? • It’s hard to test what your code does if you get malformed json
  • 61. What is not easier with this approach? • It’s hard to test what your code does if you get malformed json • You can’t purposefully omit certain properties (unless you make a special mock class for that case)
  • 62. What is not easier with this approach? • It’s hard to test what your code does if you get malformed json • You can’t purposefully omit certain properties (unless you make a special mock class for that case) • You still need a single source of truth for what data goes into a model. You could coordinate some sort of setup with the back-end team so you can fetch a blueprint for each model
  • 64. Some final notes • It’s easier to mock responses if your api services return Data instead of models
  • 65. Some final notes • It’s easier to mock responses if your api services return Data instead of models • Make smaller services so you can easily create focussed mocks for them
  • 66. Some final notes • It’s easier to mock responses if your api services return Data instead of models • Make smaller services so you can easily create focussed mocks for them • Your tests shouldn’t rely on a network connection
  • 67. Some final notes • It’s easier to mock responses if your api services return Data instead of models • Make smaller services so you can easily create focussed mocks for them • Your tests shouldn’t rely on a network connection • This (obviously) is not a silver bullet
  • 68. Some final notes • It’s easier to mock responses if your api services return Data instead of models • Make smaller services so you can easily create focussed mocks for them • Your tests shouldn’t rely on a network connection • This (obviously) is not a silver bullet • Large or complex configurations can still be fed to Mockable objects since they conform to Codable
  • 69. #