SlideShare a Scribd company logo
1 of 30
Download to read offline
< Does testability imply good design? >
Andrzej Jóźwiak 2022
“It depends”
“Depends on what?”
“It depends on the context!”
“What’s the context?”
“It depends”
class NewFeatureController(private val context: AppContext) {
fun showReminderIfNeed() {
updateCounter()
if (counterIsBelowMaxAttempts()
&& userFinishedWelcomeFlow()
&& !userDoesNotWantToSeeItAgain()
) {
startFeatureReminderDialog()
}
}
private fun updateCounter() { // … }
private fun counterIsBlowMaxAttempts(): Boolean { // … }
private fun userFinishedWelcomeFlow(): Boolean { // … }
private fun userDoesNotWantToSeeItAgain(): Boolean { // … }
private fun startFeatureReminderDialog() { // … }
}
Slido: #DD22A
@RunWith(MockitoJUnitRunner.class)
class NewFeatureControllerTest {
@Mock
lateinit var context: AppContext
@Mock
lateinit var system: System
@Mock
lateinit var system: Settings
@Before
fun setUp() {
whenever(context.getSystem()).thenReturn(system)
whenever(system.getSettings()).thenReturn(settings)
// …
}
}
@RunWith(MockitoJUnitRunner.class)
class NewFeatureControllerTest {
// …
@Test
fun `after welcome controller should show the dialog`() {
whenever(settings.getInt(eq(“NEW_FEATURE_COUNTER”),
any())).thenReturn(0)
whenever(settings.getBoolean(eq(“WELCOME_FLOW_COMPLETED”),
any())).thenReturn(false)
whenever(settings.getBoolean(eq(“USER_DISMISSED_DIALOG”),
any())).thenReturn(false)
// …
val tested = NewFeatureController(context)
tested.showReminderIfNeed()
verify(context, times(1)).getSystem()
verify(system, times(1)).getSettings()
verify(settings).getInt(any(), any())
// …
verify(system, times(1)).startScreen(any())
}
}
Design issues or test issues?
We managed to test the code in the end haven’t we?
Slido: #DD22A
Or at least when
writing tests?
“If you have mocks returning mocks
returning mocks, then your test is
completely coupled with the
implementation not the interface.”
Kent Beck
Slido: #DD22A
Difficulty in mocking properly
Need to know what methods are called?
Need to know what arguments are passed?
Train wreck design?
Final methods or classes?
Static functions?
Insufficient access?
Slido: #DD22A
// a train wreck design?
appContext.getSystem().getSettings().getInt(...
// the longer the train, the longer the mocking
whenever(context.getSystem()).thenReturn(system)
whenever(system.getSettings()).thenReturn(settings)
// a possible fix?
class NewFeatureController(
private val system: System,
private val settings: Settings,
) {
// can we do better?
Slido: #DD22A
// an even better fix?
class NewFeatureController(
private val startScreen: (Screen) -> Unit,
private val getIntSetting: (key: String, default: Int) -> Int,
private val putIntSetting: (key: String, value: Int) -> Unit,
private val getBooleanSetting: (key: String, value: Boolean) -> Boolean,
// ...
) {
// we don’t need this anymore
whenever(context.getSystem()).thenReturn(system)
whenever(system.getSettings()).thenReturn(settings)
// but what if need more functions?
Slido: #DD22A
Hard to instantiate the tested code
Too many constructor arguments?
Invoking the constructor is not the only step?
Need to pass specific framework dependencies?
Need to receive specific framework callbacks/events?
class NewFeatureController(
private val startScreen: (Screen) -> Unit,
private val getIntSetting: (key: String, default: Int) -> Int,
private val putIntSetting: (key: String, value: Int) -> Unit,
private val getBooleanSetting: (key: String, value: Boolean) -> Boolean,
// ...
@Test
fun `after welcome controller should show the dialog`() {
val getIntSetting = { key, default -> 0 }
val getBooleanSetting = { key, default -> if (key == “
WELCOME_FLOW_COMPLETED”) … }
val putIntSetting = { key, value -> ... }
val startScreen = { screen -> ... }
// ...
val tested = NewFeatureController(
startScreen, getIntSetting,
putIntSetting, getBooleanSetting
)
Slido: #DD22A
@Test
fun `some test`() {
val tested = Foo()
// calling constructor is not enough
tested.initialize()
// calling functions indicating some stage of the environment lifecycle
tested.onStart()
tested.onResume()
// ...
// we can finally call the tested method
tested.someTestedMethod()
// ...
}
Slido: #DD22A
Leaking state between tests
Tested code is using singletons?
Tested code is using global variables?
Incomplete setup?
Incomplete cleanup?
Slido: #DD22A
Let’s rework the example!
Hopefully it will be better this time
Slido: #DD22A
class NewFeatureController(private val context: AppContext) {
fun showReminderIfNeed() {
updateCounter()
if (counterIsBelowMaxAttempts()
&& userFinishedWelcomeFlow()
&& !userDoesNotWantToSeeItAgain()
) {
startFeatureReminderDialog()
}
}
Is the class needed?
Can we skip passing AppContext?
What does it mean “If Needed”?
Do we need this method?
Can this be baked into the check?
Are these the only conditions?
Maybe starting a screen and checking conditions can
be generalized?
Slido: #DD22A
What do we need?
Don’t use AppContext as an argument, be more
explicit!
Something that will model a condition!
A function that starts a screen after checking
conditions
Slido: #DD22A
typealias Condition = () -> Boolean
fun <A: Screen> System.startScreen(
screen: Class<A>
condition: Condition,
init: Intent.() ->Unit
): Boolean{
if (condition()) {
val intent = Intent(screen.simpleName)
intent.init()
startScreen(intent)
return true
}
return false
}
Slido: #DD22A
typealias Condition = () -> Boolean
infix fun Condition.and(other: Condition): Condition ={
return this() && other()
}
infix fun Condition.or(other: Condition): Condition ={
return this() || other()
}
fun not(conditon: Condition): Condition ={
return !condition()
}
// …
Slido: #DD22A
val userCompletedWelcomeFlow: Condition = { // … }
val userHasNotDismissedDialogBefore: Condition = { // … }
val dialogShownLessThenThreeTimes: Condition = { // … }
// …
val startSuccessful =
System.startScreen(
screen = NewFeatureDialog.class,
condition = userCompletedWelcomeFlow and
userHasNotDismissedDialogBefore and
dialogShownLessThenThreeTimes,
) {
putExtra(“MESSAGE”, context.getResources(R.string.message))
}
Slido: #DD22A
But is it really better?
Is it better cognitively?
Previously everything was in one place!
One needs a mental model of the startScreen and
Condition!
We have more tests not less!
val userCompletedWelcomeFlow: Condition = { // … }
val userHasNotDismissedDialogBefore: Condition = { // … }
val dialogShownLessThenThreeTimes: Condition = { // … }
// …
val startSuccessful =
System.startScreen(
screen = NewFeatureDialog.class,
condition = userCompletedWelcomeFlow and
userHasNotDismissedDialogBefore and
dialogShownLessThenThreeTimes,
) {
putExtra(“MESSAGE”, context.getResources(R.string.message))
}
Slido: #DD22A
Test induced damage?
Making it test better made it read worse?
Making it test better made it harder to understand?
data class Person(
val birthday: LocalDate,
val name: String,
val surname: String
) {
// …
fun getAge(): Long =
YEARS.between(birthday, LocalDate.now())
// …
}
Reads wonderfully like a sentence!
Age is the number of YEARS between persons birthday and the current date
data class Person(
val birthday: LocalDate,
val name: String,
val surname: String
) {
// …
fun getAge(now: () -> LocalDate = { LocalDate.now() }): Long =
YEARS.between(birthday, now())
// …
}
Reads less wonderfully!
Age is the number of YEARS between persons birthday and the specified date that could be
current date if not specified
fun sumAge(
people: List<Person>,
now: () -> LocalDate = { LocalDate.now() }
) {
return people.foldLeft(0) { sum, person ->
sum + person.getAge(now)
}
}
It’s leaking up the call chain!
Thank you!

More Related Content

Similar to Does testability imply good design

Kotlin : Advanced Tricks - Ubiratan Soares
Kotlin : Advanced Tricks - Ubiratan SoaresKotlin : Advanced Tricks - Ubiratan Soares
Kotlin : Advanced Tricks - Ubiratan SoaresiMasters
 
Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose worldFabio Collini
 
Chainer-Compiler 動かしてみた
Chainer-Compiler 動かしてみたChainer-Compiler 動かしてみた
Chainer-Compiler 動かしてみたAkira Maruoka
 
Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019UA Mobile
 
Net conf BG xamarin lecture
Net conf BG xamarin lectureNet conf BG xamarin lecture
Net conf BG xamarin lectureTsvyatko Konov
 
Version1.0 StartHTML000000232 EndHTML000065057 StartFragment0000.docx
Version1.0 StartHTML000000232 EndHTML000065057 StartFragment0000.docxVersion1.0 StartHTML000000232 EndHTML000065057 StartFragment0000.docx
Version1.0 StartHTML000000232 EndHTML000065057 StartFragment0000.docxtienboileau
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM patternNAVER Engineering
 
Dicoding Developer Coaching #30: Android | Mengenal Macam-Macam Software Desi...
Dicoding Developer Coaching #30: Android | Mengenal Macam-Macam Software Desi...Dicoding Developer Coaching #30: Android | Mengenal Macam-Macam Software Desi...
Dicoding Developer Coaching #30: Android | Mengenal Macam-Macam Software Desi...DicodingEvent
 
Java Virtual Keyboard Using Robot, Toolkit and JToggleButton Classes
Java Virtual Keyboard Using Robot, Toolkit and JToggleButton ClassesJava Virtual Keyboard Using Robot, Toolkit and JToggleButton Classes
Java Virtual Keyboard Using Robot, Toolkit and JToggleButton ClassesOXUS 20
 
2011 py con
2011 py con2011 py con
2011 py conEing Ong
 
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdfJAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdfcalderoncasto9163
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingSamuel ROZE
 
What the FUF?
What the FUF?What the FUF?
What the FUF?An Doan
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to SwiftElmar Kretzer
 
Building a js widget
Building a js widgetBuilding a js widget
Building a js widgetTudor Barbu
 

Similar to Does testability imply good design (20)

Kotlin : Advanced Tricks - Ubiratan Soares
Kotlin : Advanced Tricks - Ubiratan SoaresKotlin : Advanced Tricks - Ubiratan Soares
Kotlin : Advanced Tricks - Ubiratan Soares
 
Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Chainer-Compiler 動かしてみた
Chainer-Compiler 動かしてみたChainer-Compiler 動かしてみた
Chainer-Compiler 動かしてみた
 
Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019
 
Net conf BG xamarin lecture
Net conf BG xamarin lectureNet conf BG xamarin lecture
Net conf BG xamarin lecture
 
Version1.0 StartHTML000000232 EndHTML000065057 StartFragment0000.docx
Version1.0 StartHTML000000232 EndHTML000065057 StartFragment0000.docxVersion1.0 StartHTML000000232 EndHTML000065057 StartFragment0000.docx
Version1.0 StartHTML000000232 EndHTML000065057 StartFragment0000.docx
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
Dicoding Developer Coaching #30: Android | Mengenal Macam-Macam Software Desi...
Dicoding Developer Coaching #30: Android | Mengenal Macam-Macam Software Desi...Dicoding Developer Coaching #30: Android | Mengenal Macam-Macam Software Desi...
Dicoding Developer Coaching #30: Android | Mengenal Macam-Macam Software Desi...
 
Java Virtual Keyboard Using Robot, Toolkit and JToggleButton Classes
Java Virtual Keyboard Using Robot, Toolkit and JToggleButton ClassesJava Virtual Keyboard Using Robot, Toolkit and JToggleButton Classes
Java Virtual Keyboard Using Robot, Toolkit and JToggleButton Classes
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
2011 py con
2011 py con2011 py con
2011 py con
 
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdfJAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
JAVA...With N.E.T_B.E.A.N.S___________________________________.pdf
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
Flutter
FlutterFlutter
Flutter
 
What the FUF?
What the FUF?What the FUF?
What the FUF?
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Migrating Objective-C to Swift
Migrating Objective-C to SwiftMigrating Objective-C to Swift
Migrating Objective-C to Swift
 
Kotlin Generation
Kotlin GenerationKotlin Generation
Kotlin Generation
 
Building a js widget
Building a js widgetBuilding a js widget
Building a js widget
 

More from Andrzej Jóźwiak

Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...
Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...
Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...Andrzej Jóźwiak
 
Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...
Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...
Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...Andrzej Jóźwiak
 
Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webina...
Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webina...Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webina...
Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webina...Andrzej Jóźwiak
 
Capability Driven Design - Andrzej Jóźwiak - TomTom Dev Day 2021
Capability Driven Design - Andrzej Jóźwiak  - TomTom Dev Day 2021Capability Driven Design - Andrzej Jóźwiak  - TomTom Dev Day 2021
Capability Driven Design - Andrzej Jóźwiak - TomTom Dev Day 2021Andrzej Jóźwiak
 
Types of Randomness in Game Design - Rapid Talks - December 2020
Types of Randomness in Game Design - Rapid Talks - December 2020Types of Randomness in Game Design - Rapid Talks - December 2020
Types of Randomness in Game Design - Rapid Talks - December 2020Andrzej Jóźwiak
 
Capability Driven Design - Rapid Talks - November 2020
Capability Driven Design - Rapid Talks - November 2020Capability Driven Design - Rapid Talks - November 2020
Capability Driven Design - Rapid Talks - November 2020Andrzej Jóźwiak
 
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020Andrzej Jóźwiak
 
JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...
JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...
JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...Andrzej Jóźwiak
 

More from Andrzej Jóźwiak (8)

Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...
Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...
Encapsulation – the pitfalls of Object-Oriented Programming - Andrzej Jóźwiak...
 
Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...
Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...
Introduction to the Kotlin programming language - Andrzej Jóźwiak - JUG Łódź ...
 
Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webina...
Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webina...Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webina...
Property based tests and where to find them - Andrzej Jóźwiak - TomTom Webina...
 
Capability Driven Design - Andrzej Jóźwiak - TomTom Dev Day 2021
Capability Driven Design - Andrzej Jóźwiak  - TomTom Dev Day 2021Capability Driven Design - Andrzej Jóźwiak  - TomTom Dev Day 2021
Capability Driven Design - Andrzej Jóźwiak - TomTom Dev Day 2021
 
Types of Randomness in Game Design - Rapid Talks - December 2020
Types of Randomness in Game Design - Rapid Talks - December 2020Types of Randomness in Game Design - Rapid Talks - December 2020
Types of Randomness in Game Design - Rapid Talks - December 2020
 
Capability Driven Design - Rapid Talks - November 2020
Capability Driven Design - Rapid Talks - November 2020Capability Driven Design - Rapid Talks - November 2020
Capability Driven Design - Rapid Talks - November 2020
 
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
Do I need tests when I have the compiler - Andrzej Jóźwiak - TomTom Dev Day 2020
 
JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...
JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...
JUnit 4 Can it still teach us something? - Andrzej Jóźwiak - Kariera IT Łodź ...
 

Recently uploaded

Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur EscortsCall Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur High Profile
 
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptxthe ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptxhumanexperienceaaa
 
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Christo Ananth
 
Microscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxMicroscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxpurnimasatapathy1234
 
Analog to Digital and Digital to Analog Converter
Analog to Digital and Digital to Analog ConverterAnalog to Digital and Digital to Analog Converter
Analog to Digital and Digital to Analog ConverterAbhinavSharma374939
 
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escortsranjana rawat
 
main PPT.pptx of girls hostel security using rfid
main PPT.pptx of girls hostel security using rfidmain PPT.pptx of girls hostel security using rfid
main PPT.pptx of girls hostel security using rfidNikhilNagaraju
 
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)Suman Mia
 
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Serviceranjana rawat
 
Coefficient of Thermal Expansion and their Importance.pptx
Coefficient of Thermal Expansion and their Importance.pptxCoefficient of Thermal Expansion and their Importance.pptx
Coefficient of Thermal Expansion and their Importance.pptxAsutosh Ranjan
 
Introduction and different types of Ethernet.pptx
Introduction and different types of Ethernet.pptxIntroduction and different types of Ethernet.pptx
Introduction and different types of Ethernet.pptxupamatechverse
 
Processing & Properties of Floor and Wall Tiles.pptx
Processing & Properties of Floor and Wall Tiles.pptxProcessing & Properties of Floor and Wall Tiles.pptx
Processing & Properties of Floor and Wall Tiles.pptxpranjaldaimarysona
 
Architect Hassan Khalil Portfolio for 2024
Architect Hassan Khalil Portfolio for 2024Architect Hassan Khalil Portfolio for 2024
Architect Hassan Khalil Portfolio for 2024hassan khalil
 
SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )Tsuyoshi Horigome
 
VIP Call Girls Service Kondapur Hyderabad Call +91-8250192130
VIP Call Girls Service Kondapur Hyderabad Call +91-8250192130VIP Call Girls Service Kondapur Hyderabad Call +91-8250192130
VIP Call Girls Service Kondapur Hyderabad Call +91-8250192130Suhani Kapoor
 
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...Soham Mondal
 
Extrusion Processes and Their Limitations
Extrusion Processes and Their LimitationsExtrusion Processes and Their Limitations
Extrusion Processes and Their Limitations120cr0395
 
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...ranjana rawat
 

Recently uploaded (20)

Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur EscortsCall Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
Call Girls Service Nagpur Tanvi Call 7001035870 Meet With Nagpur Escorts
 
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptxthe ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
the ladakh protest in leh ladakh 2024 sonam wangchuk.pptx
 
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
 
Microscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxMicroscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptx
 
Analog to Digital and Digital to Analog Converter
Analog to Digital and Digital to Analog ConverterAnalog to Digital and Digital to Analog Converter
Analog to Digital and Digital to Analog Converter
 
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur EscortsHigh Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
High Profile Call Girls Nagpur Isha Call 7001035870 Meet With Nagpur Escorts
 
main PPT.pptx of girls hostel security using rfid
main PPT.pptx of girls hostel security using rfidmain PPT.pptx of girls hostel security using rfid
main PPT.pptx of girls hostel security using rfid
 
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
 
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
 
Coefficient of Thermal Expansion and their Importance.pptx
Coefficient of Thermal Expansion and their Importance.pptxCoefficient of Thermal Expansion and their Importance.pptx
Coefficient of Thermal Expansion and their Importance.pptx
 
Introduction and different types of Ethernet.pptx
Introduction and different types of Ethernet.pptxIntroduction and different types of Ethernet.pptx
Introduction and different types of Ethernet.pptx
 
Processing & Properties of Floor and Wall Tiles.pptx
Processing & Properties of Floor and Wall Tiles.pptxProcessing & Properties of Floor and Wall Tiles.pptx
Processing & Properties of Floor and Wall Tiles.pptx
 
Exploring_Network_Security_with_JA3_by_Rakesh Seal.pptx
Exploring_Network_Security_with_JA3_by_Rakesh Seal.pptxExploring_Network_Security_with_JA3_by_Rakesh Seal.pptx
Exploring_Network_Security_with_JA3_by_Rakesh Seal.pptx
 
Architect Hassan Khalil Portfolio for 2024
Architect Hassan Khalil Portfolio for 2024Architect Hassan Khalil Portfolio for 2024
Architect Hassan Khalil Portfolio for 2024
 
DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINEDJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
DJARUM4D - SLOT GACOR ONLINE | SLOT DEMO ONLINE
 
SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )
 
VIP Call Girls Service Kondapur Hyderabad Call +91-8250192130
VIP Call Girls Service Kondapur Hyderabad Call +91-8250192130VIP Call Girls Service Kondapur Hyderabad Call +91-8250192130
VIP Call Girls Service Kondapur Hyderabad Call +91-8250192130
 
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
OSVC_Meta-Data based Simulation Automation to overcome Verification Challenge...
 
Extrusion Processes and Their Limitations
Extrusion Processes and Their LimitationsExtrusion Processes and Their Limitations
Extrusion Processes and Their Limitations
 
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
(SHREYA) Chakan Call Girls Just Call 7001035870 [ Cash on Delivery ] Pune Esc...
 

Does testability imply good design

  • 1. < Does testability imply good design? > Andrzej Jóźwiak 2022
  • 2.
  • 3. “It depends” “Depends on what?” “It depends on the context!” “What’s the context?” “It depends”
  • 4. class NewFeatureController(private val context: AppContext) { fun showReminderIfNeed() { updateCounter() if (counterIsBelowMaxAttempts() && userFinishedWelcomeFlow() && !userDoesNotWantToSeeItAgain() ) { startFeatureReminderDialog() } } private fun updateCounter() { // … } private fun counterIsBlowMaxAttempts(): Boolean { // … } private fun userFinishedWelcomeFlow(): Boolean { // … } private fun userDoesNotWantToSeeItAgain(): Boolean { // … } private fun startFeatureReminderDialog() { // … } } Slido: #DD22A
  • 5. @RunWith(MockitoJUnitRunner.class) class NewFeatureControllerTest { @Mock lateinit var context: AppContext @Mock lateinit var system: System @Mock lateinit var system: Settings @Before fun setUp() { whenever(context.getSystem()).thenReturn(system) whenever(system.getSettings()).thenReturn(settings) // … } }
  • 6. @RunWith(MockitoJUnitRunner.class) class NewFeatureControllerTest { // … @Test fun `after welcome controller should show the dialog`() { whenever(settings.getInt(eq(“NEW_FEATURE_COUNTER”), any())).thenReturn(0) whenever(settings.getBoolean(eq(“WELCOME_FLOW_COMPLETED”), any())).thenReturn(false) whenever(settings.getBoolean(eq(“USER_DISMISSED_DIALOG”), any())).thenReturn(false) // … val tested = NewFeatureController(context) tested.showReminderIfNeed() verify(context, times(1)).getSystem() verify(system, times(1)).getSettings() verify(settings).getInt(any(), any()) // … verify(system, times(1)).startScreen(any()) } }
  • 7. Design issues or test issues? We managed to test the code in the end haven’t we? Slido: #DD22A
  • 8. Or at least when writing tests?
  • 9. “If you have mocks returning mocks returning mocks, then your test is completely coupled with the implementation not the interface.” Kent Beck Slido: #DD22A
  • 10. Difficulty in mocking properly Need to know what methods are called? Need to know what arguments are passed? Train wreck design? Final methods or classes? Static functions? Insufficient access? Slido: #DD22A
  • 11. // a train wreck design? appContext.getSystem().getSettings().getInt(... // the longer the train, the longer the mocking whenever(context.getSystem()).thenReturn(system) whenever(system.getSettings()).thenReturn(settings) // a possible fix? class NewFeatureController( private val system: System, private val settings: Settings, ) { // can we do better? Slido: #DD22A
  • 12. // an even better fix? class NewFeatureController( private val startScreen: (Screen) -> Unit, private val getIntSetting: (key: String, default: Int) -> Int, private val putIntSetting: (key: String, value: Int) -> Unit, private val getBooleanSetting: (key: String, value: Boolean) -> Boolean, // ... ) { // we don’t need this anymore whenever(context.getSystem()).thenReturn(system) whenever(system.getSettings()).thenReturn(settings) // but what if need more functions? Slido: #DD22A
  • 13. Hard to instantiate the tested code Too many constructor arguments? Invoking the constructor is not the only step? Need to pass specific framework dependencies? Need to receive specific framework callbacks/events?
  • 14. class NewFeatureController( private val startScreen: (Screen) -> Unit, private val getIntSetting: (key: String, default: Int) -> Int, private val putIntSetting: (key: String, value: Int) -> Unit, private val getBooleanSetting: (key: String, value: Boolean) -> Boolean, // ... @Test fun `after welcome controller should show the dialog`() { val getIntSetting = { key, default -> 0 } val getBooleanSetting = { key, default -> if (key == “ WELCOME_FLOW_COMPLETED”) … } val putIntSetting = { key, value -> ... } val startScreen = { screen -> ... } // ... val tested = NewFeatureController( startScreen, getIntSetting, putIntSetting, getBooleanSetting ) Slido: #DD22A
  • 15. @Test fun `some test`() { val tested = Foo() // calling constructor is not enough tested.initialize() // calling functions indicating some stage of the environment lifecycle tested.onStart() tested.onResume() // ... // we can finally call the tested method tested.someTestedMethod() // ... } Slido: #DD22A
  • 16. Leaking state between tests Tested code is using singletons? Tested code is using global variables? Incomplete setup? Incomplete cleanup? Slido: #DD22A
  • 17. Let’s rework the example! Hopefully it will be better this time Slido: #DD22A
  • 18. class NewFeatureController(private val context: AppContext) { fun showReminderIfNeed() { updateCounter() if (counterIsBelowMaxAttempts() && userFinishedWelcomeFlow() && !userDoesNotWantToSeeItAgain() ) { startFeatureReminderDialog() } } Is the class needed? Can we skip passing AppContext? What does it mean “If Needed”? Do we need this method? Can this be baked into the check? Are these the only conditions? Maybe starting a screen and checking conditions can be generalized? Slido: #DD22A
  • 19. What do we need? Don’t use AppContext as an argument, be more explicit! Something that will model a condition! A function that starts a screen after checking conditions Slido: #DD22A
  • 20. typealias Condition = () -> Boolean fun <A: Screen> System.startScreen( screen: Class<A> condition: Condition, init: Intent.() ->Unit ): Boolean{ if (condition()) { val intent = Intent(screen.simpleName) intent.init() startScreen(intent) return true } return false } Slido: #DD22A
  • 21. typealias Condition = () -> Boolean infix fun Condition.and(other: Condition): Condition ={ return this() && other() } infix fun Condition.or(other: Condition): Condition ={ return this() || other() } fun not(conditon: Condition): Condition ={ return !condition() } // … Slido: #DD22A
  • 22. val userCompletedWelcomeFlow: Condition = { // … } val userHasNotDismissedDialogBefore: Condition = { // … } val dialogShownLessThenThreeTimes: Condition = { // … } // … val startSuccessful = System.startScreen( screen = NewFeatureDialog.class, condition = userCompletedWelcomeFlow and userHasNotDismissedDialogBefore and dialogShownLessThenThreeTimes, ) { putExtra(“MESSAGE”, context.getResources(R.string.message)) } Slido: #DD22A
  • 23. But is it really better? Is it better cognitively? Previously everything was in one place! One needs a mental model of the startScreen and Condition! We have more tests not less!
  • 24. val userCompletedWelcomeFlow: Condition = { // … } val userHasNotDismissedDialogBefore: Condition = { // … } val dialogShownLessThenThreeTimes: Condition = { // … } // … val startSuccessful = System.startScreen( screen = NewFeatureDialog.class, condition = userCompletedWelcomeFlow and userHasNotDismissedDialogBefore and dialogShownLessThenThreeTimes, ) { putExtra(“MESSAGE”, context.getResources(R.string.message)) } Slido: #DD22A
  • 25. Test induced damage? Making it test better made it read worse? Making it test better made it harder to understand?
  • 26. data class Person( val birthday: LocalDate, val name: String, val surname: String ) { // … fun getAge(): Long = YEARS.between(birthday, LocalDate.now()) // … } Reads wonderfully like a sentence! Age is the number of YEARS between persons birthday and the current date
  • 27. data class Person( val birthday: LocalDate, val name: String, val surname: String ) { // … fun getAge(now: () -> LocalDate = { LocalDate.now() }): Long = YEARS.between(birthday, now()) // … } Reads less wonderfully! Age is the number of YEARS between persons birthday and the specified date that could be current date if not specified
  • 28. fun sumAge( people: List<Person>, now: () -> LocalDate = { LocalDate.now() } ) { return people.foldLeft(0) { sum, person -> sum + person.getAge(now) } } It’s leaking up the call chain!
  • 29.