SlideShare una empresa de Scribd logo
1 de 91
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

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
 

La actualidad más candente (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 a Quick Intro to Test Driven Development (TDD) in Swift

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 a Quick Intro to Test Driven Development (TDD) in Swift (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
 

Más de 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
 

Más de 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
 

Último

Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
The Evolution of Money: Digital Transformation and CBDCs in Central Banking
The Evolution of Money: Digital Transformation and CBDCs in Central BankingThe Evolution of Money: Digital Transformation and CBDCs in Central Banking
The Evolution of Money: Digital Transformation and CBDCs in Central BankingSelcen Ozturkcan
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 

Último (20)

Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
The Evolution of Money: Digital Transformation and CBDCs in Central Banking
The Evolution of Money: Digital Transformation and CBDCs in Central BankingThe Evolution of Money: Digital Transformation and CBDCs in Central Banking
The Evolution of Money: Digital Transformation and CBDCs in Central Banking
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 

Quick Intro to Test Driven Development (TDD) in Swift

  • 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