SlideShare a Scribd company logo
Test or GoFishing

Paul Ardeleanu
@pardel
A guide on how to write better Swift for iOS
TechFest Bucharest, Sept 2018 @pardel
Test Driven Development


TechFest Bucharest, Sept 2018 @pardel
TDD
according to Wikipedia
“Test-driven development (TDD) is a software development process
that relies on the repetition of a very short development cycle:
requirements are turned into very specific test cases, then the
software is improved to pass the new tests, only. This is opposed
to software development that allows software to be added that is not
proven to meet requirements.”
https://en.wikipedia.org/wiki/Test-driven_development
TechFest Bucharest, Sept 2018 @pardel
Why use TDD?

TechFest Bucharest, Sept 2018 @pardel
Behaviour Driven Development


TechFest Bucharest, Sept 2018 @pardel
BDD
according to Wikipedia
"combines the general techniques and principles of TDD with ideas
from domain-driven design and object-oriented analysis and design
to provide software development and management teams with
shared tools and a shared process to collaborate on software
development."
https://en.wikipedia.org/wiki/Behavior-driven_development
TechFest Bucharest, Sept 2018 @pardel
TDD vs BDD

TechFest Bucharest, Sept 2018 @pardel
To Be Determined

TechFest Bucharest, Sept 2018 @pardel
Quick But Gentle
Introduction to TDD


TechFest Bucharest, Sept 2018 @pardel
TDD lifecycle
Feature, Red, Green, Refactor
Pick a feature to implement
making sure it’s a small enough unit.
FEATURE
Change any of the existing code
making sure ALL tests are passing.
REFACTOR
Write a failing test.
Stop as soon as you get a failure.
RED
Write code to pass the test.
Write as little code as possible.
GREENL (
)

TechFest Bucharest, Sept 2018 @pardel
TDD lifecycle
Feature, Red, Green, Refactor
New
feature
create test
enough code to
make it pass
successful
test execution
No
anything to
refactor
Yes
Yes
refactoring
No
Ya Ain’t Gonna Need It

TechFest Bucharest, Sept 2018 @pardel
Testing pyramid
Unit Tests
Integration Tests
UI Tests
TechFest Bucharest, Sept 2018 @pardel
Testing in iOS


TechFest Bucharest, Sept 2018 @pardel
Testing in iOS
XCTest - since Xcode 5

TechFest Bucharest, Sept 2018 @pardel
Testing in iOS
XCTest - since Xcode 5
TechFest Bucharest, Sept 2018 @pardel
Playgrounds

TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
struct Task {
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
_ = Task()
}
}
TaskTests.defaultTestSuite.run()
TechFest Bucharest, Sept 2018 @pardel
struct Task {
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
_ = Task()
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
TechFest Bucharest, Sept 2018 @pardel
struct Task {
var name: String
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
_ = Task()
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
TechFest Bucharest, Sept 2018 @pardel
struct Task {
var name: String
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
_ = Task()
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
init?(name: String) {
if name.lengthOfBytes(using: .utf8) == 0 {
return nil
}
self.name = name
}
TechFest Bucharest, Sept 2018 @pardel
struct Task {
var name: String
init?(name: String) {
if name.lengthOfBytes(using: .utf8) == 0 {
return nil
}
self.name = name
}
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
_ = Task(name: UUID().uuidString)
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
TechFest Bucharest, Sept 2018 @pardel
struct Task {
var name: String
init?(name: String) {
if name.lengthOfBytes(using: .utf8) == 0 {
return nil
}
self.name = name
}
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
_ = Task(name: UUID().uuidString)
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
TechFest Bucharest, Sept 2018 @pardel
struct Task {
var name: String
init?(name: String) {
if name.lengthOfBytes(using: .utf8) == 0 {
return nil
}
self.name = name
}
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
_ = Task(name: UUID().uuidString)
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
let name = UUID().uuidString
let task = Task(name: name)
XCTAssertEqual(task?.name, name)
TechFest Bucharest, Sept 2018 @pardel
struct Task {
var name: String
init?(name: String) {
if name.lengthOfBytes(using: .utf8) == 0 {
return nil
}
self.name = name
}
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
let name = UUID().uuidString
let task = Task(name: name)
XCTAssertEqual(task?.name, name)
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
TechFest Bucharest, Sept 2018 @pardel
struct Task {
var name: String
init?(name: String) {
// if name.lengthOfBytes(using: .utf8) == 0 {
// return nil
// }
// self.name = name
}
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
let name = UUID().uuidString
let task = Task(name: name)
XCTAssertEqual(task?.name, name)
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
TechFest Bucharest, Sept 2018 @pardel
struct Task {
var name: String
init?(name: String) {
if name.lengthOfBytes(using: .utf8) == 0 {
return nil
}
self.name = name
}
}
class TaskTests: XCTestCase {
func testCanCreateATask() {
let name = UUID().uuidString
let task = Task(name: name)
XCTAssertEqual(task?.name, name)
}
func testTaskMustHaveANonBlankName() {
let task = Task(name: "")
XCTAssertNil(task)
}
}
TaskTests.defaultTestSuite.run()
TechFest Bucharest, Sept 2018 @pardel

Moving to an
Xcode project
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel

Object Factories
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
enum TaskFactory {
static var active: Task {
return Task(name: UUID().uuidString)!
}
static var completed: Task {
var task = Task(name: UUID().uuidString)!
task.complete()
return task
}
}
class TaskFactoryTests: XCTestCase {
func testActiveTask() {
let task = TaskFactory.active
XCTAssertTrue(task.isActive)
}
func testCompletedTask() {
let task = TaskFactory.completed
XCTAssertFalse(task.isActive)
}
}
TechFest Bucharest, Sept 2018 @pardel
func testActiveTasksDontIncludeCompletedTask() {
let startTaskCount = taskManager.activeTasks.count
taskManager.add(task: TaskFactory.completed)
XCTAssertEqual(taskManager.activeTasks.count, startTaskCount)
}
func testCompletedTasksDontIncludeActiveTask() {
let startTaskCount = taskManager.completedTasks.count
taskManager.add(task: TaskFactory.active )
XCTAssertEqual(taskManager.completedTasks.count, startTaskCount)
}
TechFest Bucharest, Sept 2018 @pardel
func testActiveTasksDontIncludeCompletedTask() {
let startTaskCount = taskManager.activeTasks.count
taskManager.add(task: TaskFactory.completed)
XCTAssertEqual(taskManager.activeTasks.count, startTaskCount)
}
func testCompletedTasksDontIncludeActiveTask() {
let startTaskCount = taskManager.completedTasks.count
taskManager.add(task: TaskFactory.active)
XCTAssertEqual(taskManager.completedTasks.count, startTaskCount)
}
TechFest Bucharest, Sept 2018 @pardel

3rd Party Libraries
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
class TaskManager {
private var tasks = [Task]()
public var activeTasks: [Task] {
do {
let predicate = NSPredicate(format: "completedAt == nil")
let items = try Realm().objects(Task.self).filter(predicate)
return Array(items)
} catch {
return [Task]()
}
}
public var completedTasks: [Task] {
do {
let predicate = NSPredicate(format: "completedAt != nil")
let items = try Realm().objects(Task.self).filter(predicate)
return Array(items)
} catch {
return [Task]()
}
}
…
}
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
class TaskManagerTests: XCTestCase {
var taskManager: TaskManager!
override func setUp() {
super.setUp()
taskManager = TaskManager()
}
…
}
TechFest Bucharest, Sept 2018 @pardel
class TaskManagerTests: XCTestCase {
var taskManager: TaskManager!
override func setUp() {
super.setUp()
Realm.Configuration.defaultConfiguration =
Realm.Configuration(inMemoryIdentifier: "Toddledoo")
taskManager = TaskManager()
}
…
}
TechFest Bucharest, Sept 2018 @pardel
class TaskManagerTests: XCTestCase {
var taskManager: TaskManager!
override func setUp() {
super.setUp()
Realm.Configuration.defaultConfiguration =
Realm.Configuration(inMemoryIdentifier: UUID().uuidString)
taskManager = TaskManager()
}
…
}
TechFest Bucharest, Sept 2018 @pardel
class TaskManagerTests: XCTestCase {
var taskManager: TaskManager!
override func setUp() {
super.setUp()
Realm.Configuration.defaultConfiguration =
Realm.Configuration(inMemoryIdentifier: UUID().uuidString)
taskManager = TaskManager()
}
…
}
TechFest Bucharest, Sept 2018 @pardel
UI tests

TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
class TasksViewControllerTests: XCTestCase {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc: TasksViewController?
override func setUp() {
super.setUp()
vc = storyboard.instantiateViewController(withIdentifier: "TasksViewController") as? TasksViewController
vc?.loadViewIfNeeded()
}
func testSetup() {
XCTAssertNotNil(vc)
XCTAssertEqual(vc?.isViewLoaded, true)
}
func testIsInitialViewController() {
let nav = storyboard.instantiateInitialViewController() as? UINavigationController
XCTAssertNotNil(nav)
XCTAssertEqual(nav?.viewControllers.count, 1)
XCTAssertNotNil(nav?.viewControllers.first as? TasksViewController)
}
func testHasTitle() {
XCTAssertEqual(vc?.title, "Toddledoo")
}
}
TechFest Bucharest, Sept 2018 @pardel
TechFest Bucharest, Sept 2018 @pardel
class TasksViewControllerTests: XCTestCase {
}
TechFest Bucharest, Sept 2018 @pardel
class TasksViewControllerTests: XCTestCase {
}
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc: TasksViewController?
override func setUp() {
super.setUp()
vc = storyboard.instantiateViewController(withIdentifier: "TasksViewController") as? TasksViewController
vc?.loadViewIfNeeded()
}
func testSetup() {
XCTAssertNotNil(vc)
XCTAssertEqual(vc?.isViewLoaded, true)
}
TechFest Bucharest, Sept 2018 @pardel
class TasksViewControllerTests: XCTestCase {
...
}
func testHasTableView() {
XCTAssertNotNil(vc?.tableView)
XCTAssertNotNil(vc?.tableView.superview)
XCTAssertEqual(vc?.tableView.superview, vc?.view)
}
func testTableViewCoversWholeView() {
XCTAssertNotNil(vc?.view)
XCTAssertEqual(vc?.view.bounds, vc?.tableView.frame)
}
func testTableViewCoversWholeView() {
XCTAssertNotNil(vc?.view)
XCTAssertNotNil(vc?.tableView)
let hasTopConstraint = vc?.view.constraints.contains(where: { constraint -> Bool in
return ((constraint.firstItem as? UITableView) == vc?.tableView) &&
(constraint.firstAnchor == vc?.tableView.topAnchor) &&
((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) &&
(constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.topAnchor) &&
(constraint.relation == .equal) &&
constraint.multiplier == 1.0 &&
constraint.constant == 0 &&
constraint.isActive == true
})
XCTAssert(hasTopConstraint ?? false)
}
TechFest Bucharest, Sept 2018 @pardel
class TasksViewControllerTests: XCTestCase {
}
func testTableViewCoversWholeView() {
XCTAssertNotNil(vc?.view)
XCTAssertNotNil(vc?.tableView)
XCTAssert(vc?.view.constraints.contains(where: { constraint -> Bool in
return ((constraint.firstItem as? UITableView) == vc?.tableView) &&
(constraint.firstAnchor == vc?.tableView.topAnchor) &&
((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) &&
(constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.topAnchor) &&
(constraint.relation == .equal) &&
constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true
}) ?? false)
XCTAssert(vc?.view.constraints.contains(where: { constraint -> Bool in
return ((constraint.firstItem as? UITableView) == vc?.tableView) &&
(constraint.firstAnchor == vc?.tableView.bottomAnchor) &&
((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) &&
(constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.bottomAnchor) &&
(constraint.relation == .equal) &&
constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true
}) ?? false)
XCTAssert(vc?.view.constraints.contains(where: { constraint -> Bool in
return ((constraint.firstItem as? UITableView) == vc?.tableView) &&
(constraint.firstAnchor == vc?.tableView.leadingAnchor) &&
((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) &&
(constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.leadingAnchor) &&
(constraint.relation == .equal) &&
constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true
}) ?? false)
XCTAssert(vc?.view.constraints.contains(where: { constraint -> Bool in
return ((constraint.firstItem as? UITableView) == vc?.tableView) &&
(constraint.firstAnchor == vc?.tableView.trailingAnchor) &&
((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) &&
(constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.trailingAnchor) &&
(constraint.relation == .equal) &&
constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true
}) ?? false)
}
TechFest Bucharest, Sept 2018 @pardel
Continuous Integration


TechFest Bucharest, Sept 2018 @pardel
macOS Server

TechFest Bucharest, Sept 2018 @pardel
macOS Server
TechFest Bucharest, Sept 2018 @pardel
Some tips


TechFest Bucharest, Sept 2018 @pardel
Pick good test names
test_init_takesTitleAndDescription()
test_ValidAccountName_InvalidAccountNumber_ValidSortCode_CurrentAccoun
t_OneOwner_CanNotBeInstantiated()

TechFest Bucharest, Sept 2018 @pardel
Pick good names
SUT
func testLogoutCallsDelegateMethod() {
// Given
guard let sut = sut else {
XCTFail("Could not get system under test")
return
}
// When
delegateStatus = .noneCalled
sut.logout()
// Then
XCTAssertTrue(delegateStatus == .didLogoutCalled, "delegate method was not called")
}

TechFest Bucharest, Sept 2018 @pardel
No magic numbers
UUID().uuidString
// Swift 4.2
Int.random(in: 1..<5)
Float.random(in: 1..<10)
Double.random(in: 1...100)
CGFloat.random(in: 1...1000)
Bool.random()
anArray.shuffle()
anArray.shuffled()
anArray.randomElement()

TechFest Bucharest, Sept 2018 @pardel
No magic numbers
func testActiveTasksDontIncludeCompletedTask() {
let startTaskCount = taskManager.activeTasks.count
XCTAssertTrue(taskManager.add(task: TaskFactory.completed))
XCTAssertEqual(taskManager.activeTasks.count, startTaskCount)
}
func testCompletedTasksDontIncludeActiveTask() {
let startTaskCount = taskManager.completedTasks.count
XCTAssertTrue(taskManager.add(task: TaskFactory.active))
XCTAssertEqual(taskManager.completedTasks.count, startTaskCount)
}

TechFest Bucharest, Sept 2018 @pardel
Input & output in one place
func testCanCreateATask() {
let name = UUID().uuidString
let task = Task(name: name)
XCTAssertNotNil(task)
XCTAssertEqual(task?.name, name)
}

TechFest Bucharest, Sept 2018 @pardel
Third party libraries

TechFest Bucharest, Sept 2018 @pardel
Test EVERYTHING

TechFest Bucharest, Sept 2018 @pardel
Be very specific in your tests
We wanted flying cars

TechFest Bucharest, Sept 2018 @pardel
App Architecture
MVC, MVVM, MVVM-P, Viper, etc.
https://www.objc.io/books/app-architecture/

TechFest Bucharest, Sept 2018 @pardel
BDD
Quick & Nimble
import Quick
import Nimble
class TableOfContentsSpec: QuickSpec {
override func spec() {
describe("the 'Documentation' directory") {
it("has everything you need to get started") {
let sections = Directory("Documentation").sections
expect(sections).to(contain("Organized Tests with Quick Examples and Example Groups"))
expect(sections).to(contain("Installing Quick"))
}
context("if it doesn't have what you're looking for") {
it("needs to be updated") {
let you = You(awesome: true)
expect{you.submittedAnIssue}.toEventually(beTruthy())
}
}
}
}
}

TechFest Bucharest, Sept 2018 @pardel
FitNesse
http://fitnesse.org
package fitnesse.slim.test;
public class ShouldIBuyMilk {
private int dollars;
private int pints;
private boolean creditCard;
public void setCashInWallet(int dollars) {
this.dollars = dollars;
}
public void setPintsOfMilkRemaining(int pints) {
this.pints = pints;
}
public void setCreditCard(String valid) {
creditCard = "yes".equals(valid);
}
public String goToStore() {
return (pints == 0 && (dollars > 2 || creditCard)) ? "yes" : "no";
}
// The following functions are optional. If they aren't declared they'll be ignored.
public void execute() {
}
public void reset() {
}
public void table(List<List<String>> table) {
}
public void beginTable() {
}
public void endTable() {
}
}
TechFest Bucharest, Sept 2018 @pardel
https://m.pardel.net
TechFest Bucharest, Sept 2018 @pardel
h24.io /techfest
TechFest Bucharest, Sept 2018 @pardel
Thank you!
paul@codica.eu
@pardel
codica.eu

More Related Content

What's hot

Software engineering ⊇ Software testing
Software engineering ⊇ Software testingSoftware engineering ⊇ Software testing
Software engineering ⊇ Software testingPavel Tcholakov
 
Durable functions
Durable functionsDurable functions
Durable functions명신 김
 
Azure Durable Functions (2019-03-30)
Azure Durable Functions (2019-03-30) Azure Durable Functions (2019-03-30)
Azure Durable Functions (2019-03-30) Paco de la Cruz
 
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018Chris Gillum
 
Serverless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsServerless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsLoiane Groner
 
Android architecture blueprints overview
Android architecture blueprints overviewAndroid architecture blueprints overview
Android architecture blueprints overviewChih-Chung Lee
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsJeff Durta
 
JavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good PartsJavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good PartsKonrad Malawski
 
FullStack Reativo com Spring WebFlux + Angular
FullStack Reativo com Spring WebFlux + AngularFullStack Reativo com Spring WebFlux + Angular
FullStack Reativo com Spring WebFlux + AngularLoiane Groner
 
Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)Paco de la Cruz
 
Presente e Futuro: Java EE.next()
Presente e Futuro: Java EE.next()Presente e Futuro: Java EE.next()
Presente e Futuro: Java EE.next()Bruno Borges
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaFabio Collini
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andevMike Nakhimovich
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKFabio Collini
 
Angular for Java Enterprise Developers: Oracle Code One 2018
Angular for Java Enterprise Developers: Oracle Code One 2018Angular for Java Enterprise Developers: Oracle Code One 2018
Angular for Java Enterprise Developers: Oracle Code One 2018Loiane Groner
 
GKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroidGKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroidGDG Korea
 
So how do I test my Sling application?
 So how do I test my Sling application? So how do I test my Sling application?
So how do I test my Sling application?Robert Munteanu
 

What's hot (20)

Software engineering ⊇ Software testing
Software engineering ⊇ Software testingSoftware engineering ⊇ Software testing
Software engineering ⊇ Software testing
 
Durable functions
Durable functionsDurable functions
Durable functions
 
Open sourcing the store
Open sourcing the storeOpen sourcing the store
Open sourcing the store
 
Azure Durable Functions (2019-03-30)
Azure Durable Functions (2019-03-30) Azure Durable Functions (2019-03-30)
Azure Durable Functions (2019-03-30)
 
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018
Advanced Durable Functions - Serverless Meetup Tokyo - Feb 2018
 
Serverless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsServerless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applications
 
Android architecture blueprints overview
Android architecture blueprints overviewAndroid architecture blueprints overview
Android architecture blueprints overview
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
JavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good PartsJavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good Parts
 
FullStack Reativo com Spring WebFlux + Angular
FullStack Reativo com Spring WebFlux + AngularFullStack Reativo com Spring WebFlux + Angular
FullStack Reativo com Spring WebFlux + Angular
 
Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)
 
Presente e Futuro: Java EE.next()
Presente e Futuro: Java EE.next()Presente e Futuro: Java EE.next()
Presente e Futuro: Java EE.next()
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andev
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
 
Angular for Java Enterprise Developers: Oracle Code One 2018
Angular for Java Enterprise Developers: Oracle Code One 2018Angular for Java Enterprise Developers: Oracle Code One 2018
Angular for Java Enterprise Developers: Oracle Code One 2018
 
Code Samples
Code SamplesCode Samples
Code Samples
 
GKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroidGKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroid
 
So how do I test my Sling application?
 So how do I test my Sling application? So how do I test my Sling application?
So how do I test my Sling application?
 
Enter the gradle
Enter the gradleEnter the gradle
Enter the gradle
 

Similar to Test or Go Fishing - a guide on how to write better Swift for iOS

Introducing Struts 2
Introducing Struts 2Introducing Struts 2
Introducing Struts 2wiradikusuma
 
Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeMacoscope
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android InfrastructureC.T.Co
 
Why Spring <3 Kotlin
Why Spring <3 KotlinWhy Spring <3 Kotlin
Why Spring <3 KotlinVMware Tanzu
 
Summer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and ScalaSummer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and Scalarostislav
 
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageabilityDaniel Fisher
 
Introduction to Spring MVC
Introduction to Spring MVCIntroduction to Spring MVC
Introduction to Spring MVCRichard Paul
 
Применение паттерна Page Object для автоматизации веб сервисов
Применение паттерна Page Object для автоматизации веб сервисовПрименение паттерна Page Object для автоматизации веб сервисов
Применение паттерна Page Object для автоматизации веб сервисовCOMAQA.BY
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotationjavatwo2011
 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unitliminescence
 
Тарас Олексин - Sculpt! Your! Tests!
Тарас Олексин  - Sculpt! Your! Tests!Тарас Олексин  - Sculpt! Your! Tests!
Тарас Олексин - Sculpt! Your! Tests!DataArt
 
Idiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingIdiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingSchalk Cronjé
 
Federico Feroldi - Scala microservices
Federico Feroldi - Scala microservicesFederico Feroldi - Scala microservices
Federico Feroldi - Scala microservicesScala Italy
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013Laurent_VB
 

Similar to Test or Go Fishing - a guide on how to write better Swift for iOS (20)

Android best practices
Android best practicesAndroid best practices
Android best practices
 
Introducing Struts 2
Introducing Struts 2Introducing Struts 2
Introducing Struts 2
 
Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, Macoscope
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
JUnit 5
JUnit 5JUnit 5
JUnit 5
 
Why Spring <3 Kotlin
Why Spring <3 KotlinWhy Spring <3 Kotlin
Why Spring <3 Kotlin
 
Summer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and ScalaSummer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and Scala
 
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
 
Introduction to Spring MVC
Introduction to Spring MVCIntroduction to Spring MVC
Introduction to Spring MVC
 
Применение паттерна Page Object для автоматизации веб сервисов
Применение паттерна Page Object для автоматизации веб сервисовПрименение паттерна Page Object для автоматизации веб сервисов
Применение паттерна Page Object для автоматизации веб сервисов
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unit
 
Coding Ajax
Coding AjaxCoding Ajax
Coding Ajax
 
Arquitecturas de microservicios - Medianet Software
Arquitecturas de microservicios   -  Medianet SoftwareArquitecturas de microservicios   -  Medianet Software
Arquitecturas de microservicios - Medianet Software
 
Тарас Олексин - Sculpt! Your! Tests!
Тарас Олексин  - Sculpt! Your! Tests!Тарас Олексин  - Sculpt! Your! Tests!
Тарас Олексин - Sculpt! Your! Tests!
 
Coding Ajax
Coding AjaxCoding Ajax
Coding Ajax
 
Javascript Design Patterns
Javascript Design PatternsJavascript Design Patterns
Javascript Design Patterns
 
Idiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin WritingIdiomatic Gradle Plugin Writing
Idiomatic Gradle Plugin Writing
 
Federico Feroldi - Scala microservices
Federico Feroldi - Scala microservicesFederico Feroldi - Scala microservices
Federico Feroldi - Scala microservices
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013
 

More from Paul Ardeleanu

Architecting apps - Can we write better code by planning ahead?
Architecting apps - Can we write better code by planning ahead?Architecting apps - Can we write better code by planning ahead?
Architecting apps - Can we write better code by planning ahead?Paul Ardeleanu
 
iOSNeXT.ro - 10 reasons you'll love Swift - Paul Ardeleanu
iOSNeXT.ro - 10 reasons you'll love Swift - Paul ArdeleanuiOSNeXT.ro - 10 reasons you'll love Swift - Paul Ardeleanu
iOSNeXT.ro - 10 reasons you'll love Swift - Paul ArdeleanuPaul Ardeleanu
 
iOSNeXT.ro - Scout mapping & navigation SDK for iOS developers - Zoltan Korosi
iOSNeXT.ro - Scout mapping & navigation SDK for iOS developers - Zoltan KorosiiOSNeXT.ro - Scout mapping & navigation SDK for iOS developers - Zoltan Korosi
iOSNeXT.ro - Scout mapping & navigation SDK for iOS developers - Zoltan KorosiPaul Ardeleanu
 
iOSNeXT.ro - Catwalk15 - Mark Filipas
iOSNeXT.ro - Catwalk15 - Mark FilipasiOSNeXT.ro - Catwalk15 - Mark Filipas
iOSNeXT.ro - Catwalk15 - Mark FilipasPaul Ardeleanu
 
iOSNeXT.ro - Lessons learnt as Indie Developer in Romania - Alexandru Iliescu
iOSNeXT.ro - Lessons learnt as Indie Developer in Romania - Alexandru IliescuiOSNeXT.ro - Lessons learnt as Indie Developer in Romania - Alexandru Iliescu
iOSNeXT.ro - Lessons learnt as Indie Developer in Romania - Alexandru IliescuPaul Ardeleanu
 
7 things one should learn from iOS
7 things one should learn from iOS7 things one should learn from iOS
7 things one should learn from iOSPaul Ardeleanu
 
To swiftly go where no OS has gone before
To swiftly go where no OS has gone beforeTo swiftly go where no OS has gone before
To swiftly go where no OS has gone beforePaul Ardeleanu
 
iOS Developer Overview - DevWeek 2014
iOS Developer Overview - DevWeek 2014iOS Developer Overview - DevWeek 2014
iOS Developer Overview - DevWeek 2014Paul Ardeleanu
 
Prototyping saves your bacon
Prototyping saves your baconPrototyping saves your bacon
Prototyping saves your baconPaul Ardeleanu
 
My talk @ Timisoara Mobile Development Group February Meetup
My talk @ Timisoara Mobile Development Group February MeetupMy talk @ Timisoara Mobile Development Group February Meetup
My talk @ Timisoara Mobile Development Group February MeetupPaul Ardeleanu
 
How to prototype your mobile apps
How to prototype your mobile appsHow to prototype your mobile apps
How to prototype your mobile appsPaul Ardeleanu
 
Prototyping your iPhone/iPad app
Prototyping your iPhone/iPad appPrototyping your iPhone/iPad app
Prototyping your iPhone/iPad appPaul Ardeleanu
 
There is no spoon - iPhone vs. iPad
There is no spoon - iPhone vs. iPadThere is no spoon - iPhone vs. iPad
There is no spoon - iPhone vs. iPadPaul Ardeleanu
 
The Adventure - From idea to the iPhone
The Adventure - From idea to the iPhoneThe Adventure - From idea to the iPhone
The Adventure - From idea to the iPhonePaul Ardeleanu
 
iPhone and Rails integration
iPhone and Rails integrationiPhone and Rails integration
iPhone and Rails integrationPaul Ardeleanu
 

More from Paul Ardeleanu (19)

Prototype your dream
Prototype your dreamPrototype your dream
Prototype your dream
 
Architecting apps - Can we write better code by planning ahead?
Architecting apps - Can we write better code by planning ahead?Architecting apps - Can we write better code by planning ahead?
Architecting apps - Can we write better code by planning ahead?
 
iOSNeXT.ro - 10 reasons you'll love Swift - Paul Ardeleanu
iOSNeXT.ro - 10 reasons you'll love Swift - Paul ArdeleanuiOSNeXT.ro - 10 reasons you'll love Swift - Paul Ardeleanu
iOSNeXT.ro - 10 reasons you'll love Swift - Paul Ardeleanu
 
iOSNeXT.ro - Scout mapping & navigation SDK for iOS developers - Zoltan Korosi
iOSNeXT.ro - Scout mapping & navigation SDK for iOS developers - Zoltan KorosiiOSNeXT.ro - Scout mapping & navigation SDK for iOS developers - Zoltan Korosi
iOSNeXT.ro - Scout mapping & navigation SDK for iOS developers - Zoltan Korosi
 
iOSNeXT.ro - Catwalk15 - Mark Filipas
iOSNeXT.ro - Catwalk15 - Mark FilipasiOSNeXT.ro - Catwalk15 - Mark Filipas
iOSNeXT.ro - Catwalk15 - Mark Filipas
 
iOSNeXT.ro - Lessons learnt as Indie Developer in Romania - Alexandru Iliescu
iOSNeXT.ro - Lessons learnt as Indie Developer in Romania - Alexandru IliescuiOSNeXT.ro - Lessons learnt as Indie Developer in Romania - Alexandru Iliescu
iOSNeXT.ro - Lessons learnt as Indie Developer in Romania - Alexandru Iliescu
 
7 things one should learn from iOS
7 things one should learn from iOS7 things one should learn from iOS
7 things one should learn from iOS
 
iOScon 2014
iOScon 2014 iOScon 2014
iOScon 2014
 
To swiftly go where no OS has gone before
To swiftly go where no OS has gone beforeTo swiftly go where no OS has gone before
To swiftly go where no OS has gone before
 
iOS Developer Overview - DevWeek 2014
iOS Developer Overview - DevWeek 2014iOS Developer Overview - DevWeek 2014
iOS Developer Overview - DevWeek 2014
 
Prototyping saves your bacon
Prototyping saves your baconPrototyping saves your bacon
Prototyping saves your bacon
 
My talk @ Timisoara Mobile Development Group February Meetup
My talk @ Timisoara Mobile Development Group February MeetupMy talk @ Timisoara Mobile Development Group February Meetup
My talk @ Timisoara Mobile Development Group February Meetup
 
How to prototype your mobile apps
How to prototype your mobile appsHow to prototype your mobile apps
How to prototype your mobile apps
 
Native vs html5
Native vs html5Native vs html5
Native vs html5
 
Prototyping your iPhone/iPad app
Prototyping your iPhone/iPad appPrototyping your iPhone/iPad app
Prototyping your iPhone/iPad app
 
Whats new in iOS5
Whats new in iOS5Whats new in iOS5
Whats new in iOS5
 
There is no spoon - iPhone vs. iPad
There is no spoon - iPhone vs. iPadThere is no spoon - iPhone vs. iPad
There is no spoon - iPhone vs. iPad
 
The Adventure - From idea to the iPhone
The Adventure - From idea to the iPhoneThe Adventure - From idea to the iPhone
The Adventure - From idea to the iPhone
 
iPhone and Rails integration
iPhone and Rails integrationiPhone and Rails integration
iPhone and Rails integration
 

Recently uploaded

Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesBhaskar Mitra
 
UiPath Test Automation using UiPath Test Suite series, part 2
UiPath Test Automation using UiPath Test Suite series, part 2UiPath Test Automation using UiPath Test Suite series, part 2
UiPath Test Automation using UiPath Test Suite series, part 2DianaGray10
 
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka DoktorováCzechDreamin
 
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxUnpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxDavid Michel
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Alison B. Lowndes
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...Product School
 
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀DianaGray10
 
Demystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyDemystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyJohn Staveley
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupCatarinaPereira64715
 
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...CzechDreamin
 
AI revolution and Salesforce, Jiří Karpíšek
AI revolution and Salesforce, Jiří KarpíšekAI revolution and Salesforce, Jiří Karpíšek
AI revolution and Salesforce, Jiří KarpíšekCzechDreamin
 
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Julian Hyde
 
UiPath Test Automation using UiPath Test Suite series, part 1
UiPath Test Automation using UiPath Test Suite series, part 1UiPath Test Automation using UiPath Test Suite series, part 1
UiPath Test Automation using UiPath Test Suite series, part 1DianaGray10
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Jeffrey Haguewood
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonDianaGray10
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...Product School
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxAbida Shariff
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualityInflectra
 
Optimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through ObservabilityOptimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through ObservabilityScyllaDB
 
IESVE for Early Stage Design and Planning
IESVE for Early Stage Design and PlanningIESVE for Early Stage Design and Planning
IESVE for Early Stage Design and PlanningIES VE
 

Recently uploaded (20)

Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
 
UiPath Test Automation using UiPath Test Suite series, part 2
UiPath Test Automation using UiPath Test Suite series, part 2UiPath Test Automation using UiPath Test Suite series, part 2
UiPath Test Automation using UiPath Test Suite series, part 2
 
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová10 Differences between Sales Cloud and CPQ, Blanka Doktorová
10 Differences between Sales Cloud and CPQ, Blanka Doktorová
 
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptxUnpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
Unpacking Value Delivery - Agile Oxford Meetup - May 2024.pptx
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
Exploring UiPath Orchestrator API: updates and limits in 2024 🚀
 
Demystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John StaveleyDemystifying gRPC in .Net by John Staveley
Demystifying gRPC in .Net by John Staveley
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
 
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
 
AI revolution and Salesforce, Jiří Karpíšek
AI revolution and Salesforce, Jiří KarpíšekAI revolution and Salesforce, Jiří Karpíšek
AI revolution and Salesforce, Jiří Karpíšek
 
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
Measures in SQL (a talk at SF Distributed Systems meetup, 2024-05-22)
 
UiPath Test Automation using UiPath Test Suite series, part 1
UiPath Test Automation using UiPath Test Suite series, part 1UiPath Test Automation using UiPath Test Suite series, part 1
UiPath Test Automation using UiPath Test Suite series, part 1
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
Optimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through ObservabilityOptimizing NoSQL Performance Through Observability
Optimizing NoSQL Performance Through Observability
 
IESVE for Early Stage Design and Planning
IESVE for Early Stage Design and PlanningIESVE for Early Stage Design and Planning
IESVE for Early Stage Design and Planning
 

Test or Go Fishing - a guide on how to write better Swift for iOS

  • 1. Test or GoFishing  Paul Ardeleanu @pardel A guide on how to write better Swift for iOS
  • 2. TechFest Bucharest, Sept 2018 @pardel Test Driven Development 
  • 3.  TechFest Bucharest, Sept 2018 @pardel TDD according to Wikipedia “Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved to pass the new tests, only. This is opposed to software development that allows software to be added that is not proven to meet requirements.” https://en.wikipedia.org/wiki/Test-driven_development
  • 4. TechFest Bucharest, Sept 2018 @pardel Why use TDD? 
  • 5. TechFest Bucharest, Sept 2018 @pardel Behaviour Driven Development 
  • 6.  TechFest Bucharest, Sept 2018 @pardel BDD according to Wikipedia "combines the general techniques and principles of TDD with ideas from domain-driven design and object-oriented analysis and design to provide software development and management teams with shared tools and a shared process to collaborate on software development." https://en.wikipedia.org/wiki/Behavior-driven_development
  • 7. TechFest Bucharest, Sept 2018 @pardel TDD vs BDD 
  • 8. TechFest Bucharest, Sept 2018 @pardel To Be Determined 
  • 9. TechFest Bucharest, Sept 2018 @pardel Quick But Gentle Introduction to TDD 
  • 10.  TechFest Bucharest, Sept 2018 @pardel TDD lifecycle Feature, Red, Green, Refactor Pick a feature to implement making sure it’s a small enough unit. FEATURE Change any of the existing code making sure ALL tests are passing. REFACTOR Write a failing test. Stop as soon as you get a failure. RED Write code to pass the test. Write as little code as possible. GREENL ( )
  • 11.  TechFest Bucharest, Sept 2018 @pardel TDD lifecycle Feature, Red, Green, Refactor New feature create test enough code to make it pass successful test execution No anything to refactor Yes Yes refactoring No Ya Ain’t Gonna Need It
  • 12.  TechFest Bucharest, Sept 2018 @pardel Testing pyramid Unit Tests Integration Tests UI Tests
  • 13. TechFest Bucharest, Sept 2018 @pardel Testing in iOS 
  • 14.  TechFest Bucharest, Sept 2018 @pardel Testing in iOS XCTest - since Xcode 5
  • 15.  TechFest Bucharest, Sept 2018 @pardel Testing in iOS XCTest - since Xcode 5
  • 16. TechFest Bucharest, Sept 2018 @pardel Playgrounds 
  • 17. TechFest Bucharest, Sept 2018 @pardel
  • 18. TechFest Bucharest, Sept 2018 @pardel
  • 19. TechFest Bucharest, Sept 2018 @pardel
  • 20. TechFest Bucharest, Sept 2018 @pardel
  • 21. TechFest Bucharest, Sept 2018 @pardel
  • 22. TechFest Bucharest, Sept 2018 @pardel
  • 23. TechFest Bucharest, Sept 2018 @pardel
  • 24. TechFest Bucharest, Sept 2018 @pardel
  • 25. TechFest Bucharest, Sept 2018 @pardel
  • 26. TechFest Bucharest, Sept 2018 @pardel struct Task { } class TaskTests: XCTestCase { func testCanCreateATask() { _ = Task() } } TaskTests.defaultTestSuite.run()
  • 27. TechFest Bucharest, Sept 2018 @pardel struct Task { } class TaskTests: XCTestCase { func testCanCreateATask() { _ = Task() } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run()
  • 28. TechFest Bucharest, Sept 2018 @pardel struct Task { var name: String } class TaskTests: XCTestCase { func testCanCreateATask() { _ = Task() } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run()
  • 29. TechFest Bucharest, Sept 2018 @pardel struct Task { var name: String } class TaskTests: XCTestCase { func testCanCreateATask() { _ = Task() } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run() init?(name: String) { if name.lengthOfBytes(using: .utf8) == 0 { return nil } self.name = name }
  • 30. TechFest Bucharest, Sept 2018 @pardel struct Task { var name: String init?(name: String) { if name.lengthOfBytes(using: .utf8) == 0 { return nil } self.name = name } } class TaskTests: XCTestCase { func testCanCreateATask() { _ = Task(name: UUID().uuidString) } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run()
  • 31. TechFest Bucharest, Sept 2018 @pardel struct Task { var name: String init?(name: String) { if name.lengthOfBytes(using: .utf8) == 0 { return nil } self.name = name } } class TaskTests: XCTestCase { func testCanCreateATask() { _ = Task(name: UUID().uuidString) } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run()
  • 32. TechFest Bucharest, Sept 2018 @pardel struct Task { var name: String init?(name: String) { if name.lengthOfBytes(using: .utf8) == 0 { return nil } self.name = name } } class TaskTests: XCTestCase { func testCanCreateATask() { _ = Task(name: UUID().uuidString) } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run() let name = UUID().uuidString let task = Task(name: name) XCTAssertEqual(task?.name, name)
  • 33. TechFest Bucharest, Sept 2018 @pardel struct Task { var name: String init?(name: String) { if name.lengthOfBytes(using: .utf8) == 0 { return nil } self.name = name } } class TaskTests: XCTestCase { func testCanCreateATask() { let name = UUID().uuidString let task = Task(name: name) XCTAssertEqual(task?.name, name) } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run()
  • 34. TechFest Bucharest, Sept 2018 @pardel struct Task { var name: String init?(name: String) { // if name.lengthOfBytes(using: .utf8) == 0 { // return nil // } // self.name = name } } class TaskTests: XCTestCase { func testCanCreateATask() { let name = UUID().uuidString let task = Task(name: name) XCTAssertEqual(task?.name, name) } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run()
  • 35. TechFest Bucharest, Sept 2018 @pardel struct Task { var name: String init?(name: String) { if name.lengthOfBytes(using: .utf8) == 0 { return nil } self.name = name } } class TaskTests: XCTestCase { func testCanCreateATask() { let name = UUID().uuidString let task = Task(name: name) XCTAssertEqual(task?.name, name) } func testTaskMustHaveANonBlankName() { let task = Task(name: "") XCTAssertNil(task) } } TaskTests.defaultTestSuite.run()
  • 36.
  • 37.
  • 38.
  • 39. TechFest Bucharest, Sept 2018 @pardel  Moving to an Xcode project
  • 40. TechFest Bucharest, Sept 2018 @pardel
  • 41. TechFest Bucharest, Sept 2018 @pardel
  • 42.
  • 43. TechFest Bucharest, Sept 2018 @pardel
  • 44. TechFest Bucharest, Sept 2018 @pardel
  • 45. TechFest Bucharest, Sept 2018 @pardel
  • 46. TechFest Bucharest, Sept 2018 @pardel
  • 47. TechFest Bucharest, Sept 2018 @pardel  Object Factories
  • 48. TechFest Bucharest, Sept 2018 @pardel
  • 49. TechFest Bucharest, Sept 2018 @pardel enum TaskFactory { static var active: Task { return Task(name: UUID().uuidString)! } static var completed: Task { var task = Task(name: UUID().uuidString)! task.complete() return task } } class TaskFactoryTests: XCTestCase { func testActiveTask() { let task = TaskFactory.active XCTAssertTrue(task.isActive) } func testCompletedTask() { let task = TaskFactory.completed XCTAssertFalse(task.isActive) } }
  • 50. TechFest Bucharest, Sept 2018 @pardel func testActiveTasksDontIncludeCompletedTask() { let startTaskCount = taskManager.activeTasks.count taskManager.add(task: TaskFactory.completed) XCTAssertEqual(taskManager.activeTasks.count, startTaskCount) } func testCompletedTasksDontIncludeActiveTask() { let startTaskCount = taskManager.completedTasks.count taskManager.add(task: TaskFactory.active ) XCTAssertEqual(taskManager.completedTasks.count, startTaskCount) }
  • 51. TechFest Bucharest, Sept 2018 @pardel func testActiveTasksDontIncludeCompletedTask() { let startTaskCount = taskManager.activeTasks.count taskManager.add(task: TaskFactory.completed) XCTAssertEqual(taskManager.activeTasks.count, startTaskCount) } func testCompletedTasksDontIncludeActiveTask() { let startTaskCount = taskManager.completedTasks.count taskManager.add(task: TaskFactory.active) XCTAssertEqual(taskManager.completedTasks.count, startTaskCount) }
  • 52. TechFest Bucharest, Sept 2018 @pardel  3rd Party Libraries
  • 53. TechFest Bucharest, Sept 2018 @pardel
  • 54. TechFest Bucharest, Sept 2018 @pardel
  • 55. TechFest Bucharest, Sept 2018 @pardel class TaskManager { private var tasks = [Task]() public var activeTasks: [Task] { do { let predicate = NSPredicate(format: "completedAt == nil") let items = try Realm().objects(Task.self).filter(predicate) return Array(items) } catch { return [Task]() } } public var completedTasks: [Task] { do { let predicate = NSPredicate(format: "completedAt != nil") let items = try Realm().objects(Task.self).filter(predicate) return Array(items) } catch { return [Task]() } } … }
  • 56. TechFest Bucharest, Sept 2018 @pardel
  • 57. TechFest Bucharest, Sept 2018 @pardel class TaskManagerTests: XCTestCase { var taskManager: TaskManager! override func setUp() { super.setUp() taskManager = TaskManager() } … }
  • 58. TechFest Bucharest, Sept 2018 @pardel class TaskManagerTests: XCTestCase { var taskManager: TaskManager! override func setUp() { super.setUp() Realm.Configuration.defaultConfiguration = Realm.Configuration(inMemoryIdentifier: "Toddledoo") taskManager = TaskManager() } … }
  • 59. TechFest Bucharest, Sept 2018 @pardel class TaskManagerTests: XCTestCase { var taskManager: TaskManager! override func setUp() { super.setUp() Realm.Configuration.defaultConfiguration = Realm.Configuration(inMemoryIdentifier: UUID().uuidString) taskManager = TaskManager() } … }
  • 60. TechFest Bucharest, Sept 2018 @pardel class TaskManagerTests: XCTestCase { var taskManager: TaskManager! override func setUp() { super.setUp() Realm.Configuration.defaultConfiguration = Realm.Configuration(inMemoryIdentifier: UUID().uuidString) taskManager = TaskManager() } … }
  • 61. TechFest Bucharest, Sept 2018 @pardel UI tests 
  • 62. TechFest Bucharest, Sept 2018 @pardel
  • 63. TechFest Bucharest, Sept 2018 @pardel
  • 64. TechFest Bucharest, Sept 2018 @pardel
  • 65. TechFest Bucharest, Sept 2018 @pardel
  • 66. TechFest Bucharest, Sept 2018 @pardel
  • 67. TechFest Bucharest, Sept 2018 @pardel
  • 68. TechFest Bucharest, Sept 2018 @pardel class TasksViewControllerTests: XCTestCase { let storyboard = UIStoryboard(name: "Main", bundle: nil) var vc: TasksViewController? override func setUp() { super.setUp() vc = storyboard.instantiateViewController(withIdentifier: "TasksViewController") as? TasksViewController vc?.loadViewIfNeeded() } func testSetup() { XCTAssertNotNil(vc) XCTAssertEqual(vc?.isViewLoaded, true) } func testIsInitialViewController() { let nav = storyboard.instantiateInitialViewController() as? UINavigationController XCTAssertNotNil(nav) XCTAssertEqual(nav?.viewControllers.count, 1) XCTAssertNotNil(nav?.viewControllers.first as? TasksViewController) } func testHasTitle() { XCTAssertEqual(vc?.title, "Toddledoo") } }
  • 69. TechFest Bucharest, Sept 2018 @pardel
  • 70. TechFest Bucharest, Sept 2018 @pardel class TasksViewControllerTests: XCTestCase { }
  • 71. TechFest Bucharest, Sept 2018 @pardel class TasksViewControllerTests: XCTestCase { } let storyboard = UIStoryboard(name: "Main", bundle: nil) var vc: TasksViewController? override func setUp() { super.setUp() vc = storyboard.instantiateViewController(withIdentifier: "TasksViewController") as? TasksViewController vc?.loadViewIfNeeded() } func testSetup() { XCTAssertNotNil(vc) XCTAssertEqual(vc?.isViewLoaded, true) }
  • 72. TechFest Bucharest, Sept 2018 @pardel class TasksViewControllerTests: XCTestCase { ... } func testHasTableView() { XCTAssertNotNil(vc?.tableView) XCTAssertNotNil(vc?.tableView.superview) XCTAssertEqual(vc?.tableView.superview, vc?.view) } func testTableViewCoversWholeView() { XCTAssertNotNil(vc?.view) XCTAssertEqual(vc?.view.bounds, vc?.tableView.frame) } func testTableViewCoversWholeView() { XCTAssertNotNil(vc?.view) XCTAssertNotNil(vc?.tableView) let hasTopConstraint = vc?.view.constraints.contains(where: { constraint -> Bool in return ((constraint.firstItem as? UITableView) == vc?.tableView) && (constraint.firstAnchor == vc?.tableView.topAnchor) && ((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) && (constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.topAnchor) && (constraint.relation == .equal) && constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true }) XCTAssert(hasTopConstraint ?? false) }
  • 73. TechFest Bucharest, Sept 2018 @pardel class TasksViewControllerTests: XCTestCase { } func testTableViewCoversWholeView() { XCTAssertNotNil(vc?.view) XCTAssertNotNil(vc?.tableView) XCTAssert(vc?.view.constraints.contains(where: { constraint -> Bool in return ((constraint.firstItem as? UITableView) == vc?.tableView) && (constraint.firstAnchor == vc?.tableView.topAnchor) && ((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) && (constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.topAnchor) && (constraint.relation == .equal) && constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true }) ?? false) XCTAssert(vc?.view.constraints.contains(where: { constraint -> Bool in return ((constraint.firstItem as? UITableView) == vc?.tableView) && (constraint.firstAnchor == vc?.tableView.bottomAnchor) && ((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) && (constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.bottomAnchor) && (constraint.relation == .equal) && constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true }) ?? false) XCTAssert(vc?.view.constraints.contains(where: { constraint -> Bool in return ((constraint.firstItem as? UITableView) == vc?.tableView) && (constraint.firstAnchor == vc?.tableView.leadingAnchor) && ((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) && (constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.leadingAnchor) && (constraint.relation == .equal) && constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true }) ?? false) XCTAssert(vc?.view.constraints.contains(where: { constraint -> Bool in return ((constraint.firstItem as? UITableView) == vc?.tableView) && (constraint.firstAnchor == vc?.tableView.trailingAnchor) && ((constraint.secondItem as? UILayoutGuide) == vc?.view.safeAreaLayoutGuide) && (constraint.secondAnchor == vc?.view.safeAreaLayoutGuide.trailingAnchor) && (constraint.relation == .equal) && constraint.multiplier == 1.0 && constraint.constant == 0 && constraint.isActive == true }) ?? false) }
  • 74. TechFest Bucharest, Sept 2018 @pardel Continuous Integration 
  • 75.  TechFest Bucharest, Sept 2018 @pardel macOS Server
  • 76.  TechFest Bucharest, Sept 2018 @pardel macOS Server
  • 77. TechFest Bucharest, Sept 2018 @pardel Some tips 
  • 78.  TechFest Bucharest, Sept 2018 @pardel Pick good test names test_init_takesTitleAndDescription() test_ValidAccountName_InvalidAccountNumber_ValidSortCode_CurrentAccoun t_OneOwner_CanNotBeInstantiated()
  • 79.  TechFest Bucharest, Sept 2018 @pardel Pick good names SUT func testLogoutCallsDelegateMethod() { // Given guard let sut = sut else { XCTFail("Could not get system under test") return } // When delegateStatus = .noneCalled sut.logout() // Then XCTAssertTrue(delegateStatus == .didLogoutCalled, "delegate method was not called") }
  • 80.  TechFest Bucharest, Sept 2018 @pardel No magic numbers UUID().uuidString // Swift 4.2 Int.random(in: 1..<5) Float.random(in: 1..<10) Double.random(in: 1...100) CGFloat.random(in: 1...1000) Bool.random() anArray.shuffle() anArray.shuffled() anArray.randomElement()
  • 81.  TechFest Bucharest, Sept 2018 @pardel No magic numbers func testActiveTasksDontIncludeCompletedTask() { let startTaskCount = taskManager.activeTasks.count XCTAssertTrue(taskManager.add(task: TaskFactory.completed)) XCTAssertEqual(taskManager.activeTasks.count, startTaskCount) } func testCompletedTasksDontIncludeActiveTask() { let startTaskCount = taskManager.completedTasks.count XCTAssertTrue(taskManager.add(task: TaskFactory.active)) XCTAssertEqual(taskManager.completedTasks.count, startTaskCount) }
  • 82.  TechFest Bucharest, Sept 2018 @pardel Input & output in one place func testCanCreateATask() { let name = UUID().uuidString let task = Task(name: name) XCTAssertNotNil(task) XCTAssertEqual(task?.name, name) }
  • 83.  TechFest Bucharest, Sept 2018 @pardel Third party libraries
  • 84.  TechFest Bucharest, Sept 2018 @pardel Test EVERYTHING
  • 85.  TechFest Bucharest, Sept 2018 @pardel Be very specific in your tests We wanted flying cars
  • 86.  TechFest Bucharest, Sept 2018 @pardel App Architecture MVC, MVVM, MVVM-P, Viper, etc. https://www.objc.io/books/app-architecture/
  • 87.  TechFest Bucharest, Sept 2018 @pardel BDD Quick & Nimble import Quick import Nimble class TableOfContentsSpec: QuickSpec { override func spec() { describe("the 'Documentation' directory") { it("has everything you need to get started") { let sections = Directory("Documentation").sections expect(sections).to(contain("Organized Tests with Quick Examples and Example Groups")) expect(sections).to(contain("Installing Quick")) } context("if it doesn't have what you're looking for") { it("needs to be updated") { let you = You(awesome: true) expect{you.submittedAnIssue}.toEventually(beTruthy()) } } } } }
  • 88.  TechFest Bucharest, Sept 2018 @pardel FitNesse http://fitnesse.org package fitnesse.slim.test; public class ShouldIBuyMilk { private int dollars; private int pints; private boolean creditCard; public void setCashInWallet(int dollars) { this.dollars = dollars; } public void setPintsOfMilkRemaining(int pints) { this.pints = pints; } public void setCreditCard(String valid) { creditCard = "yes".equals(valid); } public String goToStore() { return (pints == 0 && (dollars > 2 || creditCard)) ? "yes" : "no"; } // The following functions are optional. If they aren't declared they'll be ignored. public void execute() { } public void reset() { } public void table(List<List<String>> table) { } public void beginTable() { } public void endTable() { } }
  • 89. TechFest Bucharest, Sept 2018 @pardel https://m.pardel.net
  • 90. TechFest Bucharest, Sept 2018 @pardel h24.io /techfest
  • 91. TechFest Bucharest, Sept 2018 @pardel Thank you! paul@codica.eu @pardel codica.eu