SlideShare une entreprise Scribd logo
Refactoring for
testability in C++
Hard-to-test patterns in C++ and
how to refactor them
About me Dimitrios Platis
● Grew up in Rodos, Greece
● Software Engineer @ Edument,
Gothenburg
● Course responsible @ Gothenburg
University
● Interests:
○ Embedded systems
○ Software Architecture
○ API Design
○ Open source software & hardware
○ Robots, Portable gadgets, IoT
○ 3D printing
○ Autonomous Driving
● Website: https://platis.solutions
DIT112-V19
DIT112-V20
Agenda
1. GoogleTest & GoogleMock
2. Ηard-to-test code
3. Easy-to-test code
4. Patterns to refactor
GoogleTest &
GoogleMock
The de facto (?) unit test framework for C++
● xUnit test framework
● Runs on multiple platforms
● Open source
○ https://github.com/google/googletest
● Rich documentation & community
● Easy to integrate with CMake
● No fancy functionality
✔ Mocks, assertions, parameterized tests
✖ DI container, "spy"
Hard-to-test code
When is code hard to test?
● High complexity
● Tight coupling with other components
● Dependence on global/static states
● Sequential coupling
● ...more!
When
it's not fun
● Create fakes containing a lot of logic
● Change visibility of member functions and attributes
● Test things that have been tested before
● Sporadic fails
● Execution takes ages
Easy-to-test code
When is code easy to test?
● Low complexity
● SOLID
● Prefers composition over inheritance
● Abstracted dependencies
● Functional code without side-effects
● ...more!
Patterns to refactor
Grain of salt
● Treat these examples as generic guidelines
○ Won't necessarily apply to your project
● Maybe worse performance
○ 3 rules of optimization
● Possibly incompatible with your domain constraints
○ ISO 26262, AUTOSAR, MISRA
● Perhaps easier/cheaper with more advanced test frameworks
○ Fruit
Core philosophy
Getting own
dependencies
Managing own
lifecycle
Getting own
configuration
Ignorantly controlling self
Dependencies
injected
Lifecycle
managed from
outside
Configuration
injected
Inversion of Control
Adopted from picocontainer.com article
Files
Why is it difficult?
bool write(const std::string& filePath,
const std::string& content)
{
std::ofstream outfile(filePath.c_str(),
std::ios::trunc);
if (outfile.good())
{
outfile << content << std::endl;
}
return outfile.good();
}
std::optional<std::string>
read(const std::string& filePath)
{
std::ifstream fileToRead(filePath);
std::stringstream buffer;
buffer << fileToRead.rdbuf();
if (buffer.good())
{
return std::make_optional(buffer.str());
}
return std::nullopt;
}
How would you test this?
bool FileEncoder::encode(const std::string& filePath) const
{
const auto validFileContents = read(filePath);
if (!validFileContents)
{
return false;
}
auto encodedFileContents = validFileContents.value();
// Do something with file contents
const auto wroteFileSuccessfully
= write(filePath + ".encoded", encodedFileContents);
return wroteFileSuccessfully;
}
Refactor
Abstract
struct FileReader {
virtual ~FileReader() = default;
virtual std::optional<std::string>
read(const std::string& filePath) const = 0;
};
struct FileWriter {
virtual ~FileWriter() = default;
virtual bool
write(const std::string& filePath,
const std::string& content) const = 0;
};
Inject
struct FileEncoder {
FileEncoder(FileReader& fileReader,
FileWriter& fileWriter);
bool encode(const std::string& filePath) const;
private:
FileReader& mFileReader;
FileWriter& mFileWriter;
};
Hard-coded
dependencies
Why is it difficult?
struct DirectionlessOdometer
{
DirectionlessOdometer(int pulsePin,
int pulsesPerMeter);
double getDistance() const;
protected:
const int mPulsesPerMeter;
int mPulses{0};
MyInterruptServiceManager mInterruptManager;
};
struct DirectionalOdometer
: public DirectionlessOdometer
{
DirectionalOdometer(int directionPin,
int pulsePin,
int pulsesPerMeter);
private:
MyPinReader mPinReader;
const int mDirectionPin;
};
How would you test this?
DirectionlessOdometer::DirectionlessOdometer(
int pulsePin, int pulsesPerMeter)
: mPulsesPerMeter{pulsesPerMeter}
{
mInterruptManager.triggerOnNewPulse(
pulsePin, [this]() { mPulses++; });
}
double DirectionlessOdometer::getDistance() const
{
return mPulses == 0 ?
0.0 :
static_cast<double>(mPulsesPerMeter) / mPulses;
}
DirectionalOdometer::DirectionalOdometer(
int directionPin,
int pulsePin,
int pulsesPerMeter)
: DirectionlessOdometer(pulsePin, pulsesPerMeter)
, mDirectionPin{directionPin}
{
mInterruptManager.triggerOnNewPulse(
pulsePin,
[this]() {
mPinReader.read(mDirectionPin) ?
mPulses++ :
mPulses--;
});
}
Refactor
Abstract common functionality & inject
struct Encoder
{
virtual ~Encoder() = default;
virtual void incrementPulses() = 0;
virtual void decrementPulses() = 0;
virtual double getDistance() const = 0;
};
struct DirectionlessOdometer
{
DirectionlessOdometer(Encoder& encoder,
InterruptServiceManager& ism,
int pulsePin);
double getDistance() const;
private:
Encoder& mEncoder;
};
Abstract dependencies & inject
struct DirectionalOdometer
{
DirectionalOdometer(Encoder& encoder,
InterruptServiceManager& ism,
PinReader& pinReader,
int directionPin,
int pulsePin);
double getDistance() const;
private:
Encoder& mEncoder;
PinReader& mPinReader;
};
Time
Why is it difficult?
set(gtest_run_flags --gtest_repeat=1000)
How would you test this?
struct PowerController
{
PowerController(PinManager& pinManager,
InterruptManager& interruptManager);
bool turnOn();
private:
PinManager& mPinManager;
std::condition_variable mConditionVariable;
std::atomic<bool> mPulseReceived{false};
std::mutex mRunnerMutex;
};
bool PowerController::turnOn()
{
mPinManager.setPin(kPin);
std::this_thread::sleep_for(1s);
mPinManager.clearPin(kPin);
std::unique_lock<std::mutex> lk(mRunnerMutex);
mConditionVariable.wait_for(
lk,
10s,
[this]() { return mPulseReceived.load(); });
return mPulseReceived.load();
}
Refactor
struct TimeKeeper
{
virtual ~TimeKeeper() = default;
virtual void
sleepFor(std::chrono::milliseconds ms) const = 0;
};
struct AsynchronousTimer
{
virtual ~AsynchronousTimer() = default;
virtual void
schedule(std::function<void()> task,
std::chrono::seconds delay) = 0;
};
bool PowerController::turnOn() {
mPinManager.setPin(kPin);
mTimeKeeper.sleepFor(1s);
mPinManager.clearPin(kPin);
mAsynchronousTimer.schedule(
[this]() {
mPulseTimedOut = true;
mConditionVariable.notify_one(); }, 10s);
std::unique_lock<std::mutex> lk(mRunnerMutex);
mConditionVariable.wait(lk, [this]() {
return mPulseReceived.load() ||
mPulseTimedOut.load(); });
mPulseTimedOut = false;
return mPulseReceived.load();
}
Domain logic dependent
on application logic
Why is it difficult?
● "The dependency rule"
○ Clean architecture
● Circular dependencies
● Domain layer eventually becomes
unsustainable to develop or test
● (C++ specific) Macro madness
Variant A Variant B Variant C
Platform
How would you test this?
struct CommunicationManager
{
CommunicationManager(SerialPortClient& serial);
void sendViaSerial(std::string message);
private:
SerialPortClient& mSerialPortClient;
int mSequenceNumber{0};
};
void
CommunicationManager::sendViaSerial(std::string message)
{
#if defined(FOO_PRODUCT)
mSerialPortClient.send(
std::to_string(mSequenceNumber++) + ":" + message);
#elif defined(BAR_PRODUCT)
mSerialPortClient.send("M:" + message + ",");
#else
#error Did you forget to define a product?
#endif
}
Refactor
struct SerialFormatter {
virtual ~SerialFormatter() = default;
virtual std::string
format(std::string in) = 0;
};
std::string
BarSerialFormatter::format(std::string in) {
return "M:" + in + ",";
}
std::string
FooSerialFormatter::format(std::string in) {
return std::to_string(mSequenceNumber++) +
":" + input;
}
struct CommunicationManager {
CommunicationManager(
SerialPortClient& serialPortClient,
SerialFormatter& serialFormatter);
void sendViaSerial(std::string message);
private:
SerialPortClient& mSerialPortClient;
SerialFormatter& mSerialFormatter;
};
void
CommunicationManager::sendViaSerial(std::string message) {
mSerialPortClient.send(mSerialFormatter.format(message));
}
Refactoring workshop
● 19th of May
● Deeper look
● More patterns
○ Including the dreaded singleton
● Unit tests
● More Q&A
● Hands-on exercises
● 2 hours
● 399 SEK
Takeaways
More patterns & working code examples:
platisd/refactoring-for-testability-cpp
Contact me: dimitris@platis.solutions
● Unit tests should be atomic and run fast
● Verify your code (only)
● It should be fun
○ Not much up-front effort
● Abstract & inject
○ Care about what and not how
● Write SOLID code
● Follow OOP best practices
● Follow CPP core guidelines

Contenu connexe

Tendances

Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereSergey Platonov
 
Jenkins 2を使った究極のpipeline ~ 明日もう一度来てください、本物のpipelineをお見せしますよ ~
Jenkins 2を使った究極のpipeline ~ 明日もう一度来てください、本物のpipelineをお見せしますよ ~Jenkins 2を使った究極のpipeline ~ 明日もう一度来てください、本物のpipelineをお見せしますよ ~
Jenkins 2を使った究極のpipeline ~ 明日もう一度来てください、本物のpipelineをお見せしますよ ~ikikko
 
The Simple Scheduler in Embedded System @ OSDC.TW 2014
The Simple Scheduler in Embedded System @ OSDC.TW 2014The Simple Scheduler in Embedded System @ OSDC.TW 2014
The Simple Scheduler in Embedded System @ OSDC.TW 2014Jian-Hong Pan
 
Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)Alexey Fyodorov
 
Node.js System: The Landing
Node.js System: The LandingNode.js System: The Landing
Node.js System: The LandingHaci Murat Yaman
 
Работа с реляционными базами данных в C++
Работа с реляционными базами данных в C++Работа с реляционными базами данных в C++
Работа с реляционными базами данных в C++corehard_by
 
Intro2 Cuda Moayad
Intro2 Cuda MoayadIntro2 Cuda Moayad
Intro2 Cuda MoayadMoayadhn
 
Антон Наумович, Система автоматической крэш-аналитики своими средствами
Антон Наумович, Система автоматической крэш-аналитики своими средствамиАнтон Наумович, Система автоматической крэш-аналитики своими средствами
Антон Наумович, Система автоматической крэш-аналитики своими средствамиSergey Platonov
 
Multithreading done right
Multithreading done rightMultithreading done right
Multithreading done rightPlatonov Sergey
 
Do we need Unsafe in Java?
Do we need Unsafe in Java?Do we need Unsafe in Java?
Do we need Unsafe in Java?Andrei Pangin
 
Java9を迎えた今こそ!Java本格(再)入門
Java9を迎えた今こそ!Java本格(再)入門Java9を迎えた今こそ!Java本格(再)入門
Java9を迎えた今こそ!Java本格(再)入門Takuya Okada
 
Down to Stack Traces, up from Heap Dumps
Down to Stack Traces, up from Heap DumpsDown to Stack Traces, up from Heap Dumps
Down to Stack Traces, up from Heap DumpsAndrei Pangin
 
Csw2016 gong pwn_a_nexus_device_with_a_single_vulnerability
Csw2016 gong pwn_a_nexus_device_with_a_single_vulnerabilityCsw2016 gong pwn_a_nexus_device_with_a_single_vulnerability
Csw2016 gong pwn_a_nexus_device_with_a_single_vulnerabilityCanSecWest
 
Functional Reactive Programming with RxJS
Functional Reactive Programming with RxJSFunctional Reactive Programming with RxJS
Functional Reactive Programming with RxJSstefanmayer13
 
Jdk 7 4-forkjoin
Jdk 7 4-forkjoinJdk 7 4-forkjoin
Jdk 7 4-forkjoinknight1128
 
Node.js flow control
Node.js flow controlNode.js flow control
Node.js flow controlSimon Su
 

Tendances (20)

Алексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhereАлексей Кутумов, Coroutines everywhere
Алексей Кутумов, Coroutines everywhere
 
Jenkins 2を使った究極のpipeline ~ 明日もう一度来てください、本物のpipelineをお見せしますよ ~
Jenkins 2を使った究極のpipeline ~ 明日もう一度来てください、本物のpipelineをお見せしますよ ~Jenkins 2を使った究極のpipeline ~ 明日もう一度来てください、本物のpipelineをお見せしますよ ~
Jenkins 2を使った究極のpipeline ~ 明日もう一度来てください、本物のpipelineをお見せしますよ ~
 
The Simple Scheduler in Embedded System @ OSDC.TW 2014
The Simple Scheduler in Embedded System @ OSDC.TW 2014The Simple Scheduler in Embedded System @ OSDC.TW 2014
The Simple Scheduler in Embedded System @ OSDC.TW 2014
 
Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)Counter Wars (JEEConf 2016)
Counter Wars (JEEConf 2016)
 
Node.js System: The Landing
Node.js System: The LandingNode.js System: The Landing
Node.js System: The Landing
 
Работа с реляционными базами данных в C++
Работа с реляционными базами данных в C++Работа с реляционными базами данных в C++
Работа с реляционными базами данных в C++
 
Intro2 Cuda Moayad
Intro2 Cuda MoayadIntro2 Cuda Moayad
Intro2 Cuda Moayad
 
Антон Наумович, Система автоматической крэш-аналитики своими средствами
Антон Наумович, Система автоматической крэш-аналитики своими средствамиАнтон Наумович, Система автоматической крэш-аналитики своими средствами
Антон Наумович, Система автоматической крэш-аналитики своими средствами
 
Multithreading done right
Multithreading done rightMultithreading done right
Multithreading done right
 
Do we need Unsafe in Java?
Do we need Unsafe in Java?Do we need Unsafe in Java?
Do we need Unsafe in Java?
 
Java9を迎えた今こそ!Java本格(再)入門
Java9を迎えた今こそ!Java本格(再)入門Java9を迎えた今こそ!Java本格(再)入門
Java9を迎えた今こそ!Java本格(再)入門
 
Joel Falcou, Boost.SIMD
Joel Falcou, Boost.SIMDJoel Falcou, Boost.SIMD
Joel Falcou, Boost.SIMD
 
Down to Stack Traces, up from Heap Dumps
Down to Stack Traces, up from Heap DumpsDown to Stack Traces, up from Heap Dumps
Down to Stack Traces, up from Heap Dumps
 
Csw2016 gong pwn_a_nexus_device_with_a_single_vulnerability
Csw2016 gong pwn_a_nexus_device_with_a_single_vulnerabilityCsw2016 gong pwn_a_nexus_device_with_a_single_vulnerability
Csw2016 gong pwn_a_nexus_device_with_a_single_vulnerability
 
Functional Reactive Programming with RxJS
Functional Reactive Programming with RxJSFunctional Reactive Programming with RxJS
Functional Reactive Programming with RxJS
 
Testing with Node.js
Testing with Node.jsTesting with Node.js
Testing with Node.js
 
Jdk 7 4-forkjoin
Jdk 7 4-forkjoinJdk 7 4-forkjoin
Jdk 7 4-forkjoin
 
Qt Rest Server
Qt Rest ServerQt Rest Server
Qt Rest Server
 
Node.js flow control
Node.js flow controlNode.js flow control
Node.js flow control
 
C++17 now
C++17 nowC++17 now
C++17 now
 

Similaire à Refactoring for testability c++

用 Go 語言打造多台機器 Scale 架構
用 Go 語言打造多台機器 Scale 架構用 Go 語言打造多台機器 Scale 架構
用 Go 語言打造多台機器 Scale 架構Bo-Yi Wu
 
JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?Doug Hawkins
 
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...corehard_by
 
Tdd with python unittest for embedded c
Tdd with python unittest for embedded cTdd with python unittest for embedded c
Tdd with python unittest for embedded cBenux Wei
 
Cross Platform App Development with C++
Cross Platform App Development with C++Cross Platform App Development with C++
Cross Platform App Development with C++Joan Puig Sanz
 
TWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonTWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonCodemotion
 
Linux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudLinux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudAndrea Righi
 
Robust C++ Task Systems Through Compile-time Checks
Robust C++ Task Systems Through Compile-time ChecksRobust C++ Task Systems Through Compile-time Checks
Robust C++ Task Systems Through Compile-time ChecksStoyan Nikolov
 
Static code analysis: what? how? why?
Static code analysis: what? how? why?Static code analysis: what? how? why?
Static code analysis: what? how? why?Andrey Karpov
 
(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++Amazon Web Services
 
Testing Django APIs
Testing Django APIsTesting Django APIs
Testing Django APIstyomo4ka
 
Jvm profiling under the hood
Jvm profiling under the hoodJvm profiling under the hood
Jvm profiling under the hoodRichardWarburton
 
Hack an ASP .NET website? Hard, but possible!
Hack an ASP .NET website? Hard, but possible! Hack an ASP .NET website? Hard, but possible!
Hack an ASP .NET website? Hard, but possible! Vladimir Kochetkov
 
Cluj.py Meetup: Extending Python in C
Cluj.py Meetup: Extending Python in CCluj.py Meetup: Extending Python in C
Cluj.py Meetup: Extending Python in CSteffen Wenz
 
Gae icc fall2011
Gae icc fall2011Gae icc fall2011
Gae icc fall2011Juan Gomez
 
PythonBrasil[8] - CPython for dummies
PythonBrasil[8] - CPython for dummiesPythonBrasil[8] - CPython for dummies
PythonBrasil[8] - CPython for dummiesTatiana Al-Chueyr
 
4Developers 2018: Evolution of C++ Class Design (Mariusz Łapiński)
4Developers 2018: Evolution of C++ Class Design (Mariusz Łapiński)4Developers 2018: Evolution of C++ Class Design (Mariusz Łapiński)
4Developers 2018: Evolution of C++ Class Design (Mariusz Łapiński)PROIDEA
 

Similaire à Refactoring for testability c++ (20)

用 Go 語言打造多台機器 Scale 架構
用 Go 語言打造多台機器 Scale 架構用 Go 語言打造多台機器 Scale 架構
用 Go 語言打造多台機器 Scale 架構
 
Modern c++
Modern c++Modern c++
Modern c++
 
JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?JVM Mechanics: When Does the JVM JIT & Deoptimize?
JVM Mechanics: When Does the JVM JIT & Deoptimize?
 
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
C++ CoreHard Autumn 2018. Concurrency and Parallelism in C++17 and C++20/23 -...
 
Tdd with python unittest for embedded c
Tdd with python unittest for embedded cTdd with python unittest for embedded c
Tdd with python unittest for embedded c
 
Cross Platform App Development with C++
Cross Platform App Development with C++Cross Platform App Development with C++
Cross Platform App Development with C++
 
TWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonTWINS: OOP and FP - Warburton
TWINS: OOP and FP - Warburton
 
Linux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudLinux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloud
 
Twins: OOP and FP
Twins: OOP and FPTwins: OOP and FP
Twins: OOP and FP
 
Robust C++ Task Systems Through Compile-time Checks
Robust C++ Task Systems Through Compile-time ChecksRobust C++ Task Systems Through Compile-time Checks
Robust C++ Task Systems Through Compile-time Checks
 
Static code analysis: what? how? why?
Static code analysis: what? how? why?Static code analysis: what? how? why?
Static code analysis: what? how? why?
 
(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++(DEV204) Building High-Performance Native Cloud Apps In C++
(DEV204) Building High-Performance Native Cloud Apps In C++
 
Testing Django APIs
Testing Django APIsTesting Django APIs
Testing Django APIs
 
Jvm profiling under the hood
Jvm profiling under the hoodJvm profiling under the hood
Jvm profiling under the hood
 
Hack an ASP .NET website? Hard, but possible!
Hack an ASP .NET website? Hard, but possible! Hack an ASP .NET website? Hard, but possible!
Hack an ASP .NET website? Hard, but possible!
 
Cluj.py Meetup: Extending Python in C
Cluj.py Meetup: Extending Python in CCluj.py Meetup: Extending Python in C
Cluj.py Meetup: Extending Python in C
 
Gae icc fall2011
Gae icc fall2011Gae icc fall2011
Gae icc fall2011
 
Java Concurrency
Java ConcurrencyJava Concurrency
Java Concurrency
 
PythonBrasil[8] - CPython for dummies
PythonBrasil[8] - CPython for dummiesPythonBrasil[8] - CPython for dummies
PythonBrasil[8] - CPython for dummies
 
4Developers 2018: Evolution of C++ Class Design (Mariusz Łapiński)
4Developers 2018: Evolution of C++ Class Design (Mariusz Łapiński)4Developers 2018: Evolution of C++ Class Design (Mariusz Łapiński)
4Developers 2018: Evolution of C++ Class Design (Mariusz Łapiński)
 

Plus de Dimitrios Platis

[GRCPP] Introduction to concepts (C++20)
[GRCPP] Introduction to concepts (C++20)[GRCPP] Introduction to concepts (C++20)
[GRCPP] Introduction to concepts (C++20)Dimitrios Platis
 
Builder pattern in C++.pdf
Builder pattern in C++.pdfBuilder pattern in C++.pdf
Builder pattern in C++.pdfDimitrios Platis
 
Interprocess communication with C++.pdf
Interprocess communication with C++.pdfInterprocess communication with C++.pdf
Interprocess communication with C++.pdfDimitrios Platis
 
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]Dimitrios Platis
 
Pointer to implementation idiom
Pointer to implementation idiomPointer to implementation idiom
Pointer to implementation idiomDimitrios Platis
 
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)Dimitrios Platis
 
How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)Dimitrios Platis
 

Plus de Dimitrios Platis (10)

[GRCPP] Introduction to concepts (C++20)
[GRCPP] Introduction to concepts (C++20)[GRCPP] Introduction to concepts (C++20)
[GRCPP] Introduction to concepts (C++20)
 
OpenAI API crash course
OpenAI API crash courseOpenAI API crash course
OpenAI API crash course
 
Builder pattern in C++.pdf
Builder pattern in C++.pdfBuilder pattern in C++.pdf
Builder pattern in C++.pdf
 
Interprocess communication with C++.pdf
Interprocess communication with C++.pdfInterprocess communication with C++.pdf
Interprocess communication with C++.pdf
 
Lambda expressions in C++
Lambda expressions in C++Lambda expressions in C++
Lambda expressions in C++
 
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
 
Introduction to CMake
Introduction to CMakeIntroduction to CMake
Introduction to CMake
 
Pointer to implementation idiom
Pointer to implementation idiomPointer to implementation idiom
Pointer to implementation idiom
 
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
Afry software safety ISO26262 (Embedded @ Gothenburg Meetup)
 
How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)How to create your own Linux distribution (embedded-gothenburg)
How to create your own Linux distribution (embedded-gothenburg)
 

Dernier

De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEJelle | Nordend
 
A Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationA Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationHelp Desk Migration
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabbereGrabber
 
OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024Shane Coughlan
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdfkalichargn70th171
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?XfilesPro
 
GraphAware - Transforming policing with graph-based intelligence analysis
GraphAware - Transforming policing with graph-based intelligence analysisGraphAware - Transforming policing with graph-based intelligence analysis
GraphAware - Transforming policing with graph-based intelligence analysisNeo4j
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfkalichargn70th171
 
AI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAlluxio, Inc.
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfmbmh111980
 
Studiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting softwareStudiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting softwareinfo611746
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Soroosh Khodami
 
A Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationA Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationHelp Desk Migration
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfDeskTrack
 
INGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignINGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignNeo4j
 
Breaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdfBreaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdfMeon Technology
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with StrimziStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzisteffenkarlsson2
 
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
KLARNA -  Language Models and Knowledge Graphs: A Systems ApproachKLARNA -  Language Models and Knowledge Graphs: A Systems Approach
KLARNA - Language Models and Knowledge Graphs: A Systems ApproachNeo4j
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)Max Lee
 

Dernier (20)

De mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FMEDe mooiste recreatieve routes ontdekken met RouteYou en FME
De mooiste recreatieve routes ontdekken met RouteYou en FME
 
A Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationA Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data Migration
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024
 
SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf
 
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
How Does XfilesPro Ensure Security While Sharing Documents in Salesforce?
 
GraphAware - Transforming policing with graph-based intelligence analysis
GraphAware - Transforming policing with graph-based intelligence analysisGraphAware - Transforming policing with graph-based intelligence analysis
GraphAware - Transforming policing with graph-based intelligence analysis
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
AI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in Michelangelo
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
Studiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting softwareStudiovity film pre-production and screenwriting software
Studiovity film pre-production and screenwriting software
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
A Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data MigrationA Guideline to Gorgias to to Re:amaze Data Migration
A Guideline to Gorgias to to Re:amaze Data Migration
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdf
 
INGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignINGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by Design
 
Breaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdfBreaking the Code : A Guide to WhatsApp Business API.pdf
Breaking the Code : A Guide to WhatsApp Business API.pdf
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with StrimziStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi
 
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
KLARNA -  Language Models and Knowledge Graphs: A Systems ApproachKLARNA -  Language Models and Knowledge Graphs: A Systems Approach
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 

Refactoring for testability c++

  • 1. Refactoring for testability in C++ Hard-to-test patterns in C++ and how to refactor them
  • 2. About me Dimitrios Platis ● Grew up in Rodos, Greece ● Software Engineer @ Edument, Gothenburg ● Course responsible @ Gothenburg University ● Interests: ○ Embedded systems ○ Software Architecture ○ API Design ○ Open source software & hardware ○ Robots, Portable gadgets, IoT ○ 3D printing ○ Autonomous Driving ● Website: https://platis.solutions
  • 3.
  • 4.
  • 6. Agenda 1. GoogleTest & GoogleMock 2. Ηard-to-test code 3. Easy-to-test code 4. Patterns to refactor
  • 8. The de facto (?) unit test framework for C++ ● xUnit test framework ● Runs on multiple platforms ● Open source ○ https://github.com/google/googletest ● Rich documentation & community ● Easy to integrate with CMake ● No fancy functionality ✔ Mocks, assertions, parameterized tests ✖ DI container, "spy"
  • 10. When is code hard to test? ● High complexity ● Tight coupling with other components ● Dependence on global/static states ● Sequential coupling ● ...more!
  • 11. When it's not fun ● Create fakes containing a lot of logic ● Change visibility of member functions and attributes ● Test things that have been tested before ● Sporadic fails ● Execution takes ages
  • 13. When is code easy to test? ● Low complexity ● SOLID ● Prefers composition over inheritance ● Abstracted dependencies ● Functional code without side-effects ● ...more!
  • 15. Grain of salt ● Treat these examples as generic guidelines ○ Won't necessarily apply to your project ● Maybe worse performance ○ 3 rules of optimization ● Possibly incompatible with your domain constraints ○ ISO 26262, AUTOSAR, MISRA ● Perhaps easier/cheaper with more advanced test frameworks ○ Fruit
  • 16. Core philosophy Getting own dependencies Managing own lifecycle Getting own configuration Ignorantly controlling self Dependencies injected Lifecycle managed from outside Configuration injected Inversion of Control Adopted from picocontainer.com article
  • 17. Files
  • 18. Why is it difficult? bool write(const std::string& filePath, const std::string& content) { std::ofstream outfile(filePath.c_str(), std::ios::trunc); if (outfile.good()) { outfile << content << std::endl; } return outfile.good(); } std::optional<std::string> read(const std::string& filePath) { std::ifstream fileToRead(filePath); std::stringstream buffer; buffer << fileToRead.rdbuf(); if (buffer.good()) { return std::make_optional(buffer.str()); } return std::nullopt; }
  • 19. How would you test this? bool FileEncoder::encode(const std::string& filePath) const { const auto validFileContents = read(filePath); if (!validFileContents) { return false; } auto encodedFileContents = validFileContents.value(); // Do something with file contents const auto wroteFileSuccessfully = write(filePath + ".encoded", encodedFileContents); return wroteFileSuccessfully; }
  • 20. Refactor Abstract struct FileReader { virtual ~FileReader() = default; virtual std::optional<std::string> read(const std::string& filePath) const = 0; }; struct FileWriter { virtual ~FileWriter() = default; virtual bool write(const std::string& filePath, const std::string& content) const = 0; }; Inject struct FileEncoder { FileEncoder(FileReader& fileReader, FileWriter& fileWriter); bool encode(const std::string& filePath) const; private: FileReader& mFileReader; FileWriter& mFileWriter; };
  • 22. Why is it difficult? struct DirectionlessOdometer { DirectionlessOdometer(int pulsePin, int pulsesPerMeter); double getDistance() const; protected: const int mPulsesPerMeter; int mPulses{0}; MyInterruptServiceManager mInterruptManager; }; struct DirectionalOdometer : public DirectionlessOdometer { DirectionalOdometer(int directionPin, int pulsePin, int pulsesPerMeter); private: MyPinReader mPinReader; const int mDirectionPin; };
  • 23. How would you test this? DirectionlessOdometer::DirectionlessOdometer( int pulsePin, int pulsesPerMeter) : mPulsesPerMeter{pulsesPerMeter} { mInterruptManager.triggerOnNewPulse( pulsePin, [this]() { mPulses++; }); } double DirectionlessOdometer::getDistance() const { return mPulses == 0 ? 0.0 : static_cast<double>(mPulsesPerMeter) / mPulses; } DirectionalOdometer::DirectionalOdometer( int directionPin, int pulsePin, int pulsesPerMeter) : DirectionlessOdometer(pulsePin, pulsesPerMeter) , mDirectionPin{directionPin} { mInterruptManager.triggerOnNewPulse( pulsePin, [this]() { mPinReader.read(mDirectionPin) ? mPulses++ : mPulses--; }); }
  • 24. Refactor Abstract common functionality & inject struct Encoder { virtual ~Encoder() = default; virtual void incrementPulses() = 0; virtual void decrementPulses() = 0; virtual double getDistance() const = 0; }; struct DirectionlessOdometer { DirectionlessOdometer(Encoder& encoder, InterruptServiceManager& ism, int pulsePin); double getDistance() const; private: Encoder& mEncoder; }; Abstract dependencies & inject struct DirectionalOdometer { DirectionalOdometer(Encoder& encoder, InterruptServiceManager& ism, PinReader& pinReader, int directionPin, int pulsePin); double getDistance() const; private: Encoder& mEncoder; PinReader& mPinReader; };
  • 25. Time
  • 26. Why is it difficult? set(gtest_run_flags --gtest_repeat=1000)
  • 27. How would you test this? struct PowerController { PowerController(PinManager& pinManager, InterruptManager& interruptManager); bool turnOn(); private: PinManager& mPinManager; std::condition_variable mConditionVariable; std::atomic<bool> mPulseReceived{false}; std::mutex mRunnerMutex; }; bool PowerController::turnOn() { mPinManager.setPin(kPin); std::this_thread::sleep_for(1s); mPinManager.clearPin(kPin); std::unique_lock<std::mutex> lk(mRunnerMutex); mConditionVariable.wait_for( lk, 10s, [this]() { return mPulseReceived.load(); }); return mPulseReceived.load(); }
  • 28. Refactor struct TimeKeeper { virtual ~TimeKeeper() = default; virtual void sleepFor(std::chrono::milliseconds ms) const = 0; }; struct AsynchronousTimer { virtual ~AsynchronousTimer() = default; virtual void schedule(std::function<void()> task, std::chrono::seconds delay) = 0; }; bool PowerController::turnOn() { mPinManager.setPin(kPin); mTimeKeeper.sleepFor(1s); mPinManager.clearPin(kPin); mAsynchronousTimer.schedule( [this]() { mPulseTimedOut = true; mConditionVariable.notify_one(); }, 10s); std::unique_lock<std::mutex> lk(mRunnerMutex); mConditionVariable.wait(lk, [this]() { return mPulseReceived.load() || mPulseTimedOut.load(); }); mPulseTimedOut = false; return mPulseReceived.load(); }
  • 29. Domain logic dependent on application logic
  • 30. Why is it difficult? ● "The dependency rule" ○ Clean architecture ● Circular dependencies ● Domain layer eventually becomes unsustainable to develop or test ● (C++ specific) Macro madness Variant A Variant B Variant C Platform
  • 31. How would you test this? struct CommunicationManager { CommunicationManager(SerialPortClient& serial); void sendViaSerial(std::string message); private: SerialPortClient& mSerialPortClient; int mSequenceNumber{0}; }; void CommunicationManager::sendViaSerial(std::string message) { #if defined(FOO_PRODUCT) mSerialPortClient.send( std::to_string(mSequenceNumber++) + ":" + message); #elif defined(BAR_PRODUCT) mSerialPortClient.send("M:" + message + ","); #else #error Did you forget to define a product? #endif }
  • 32. Refactor struct SerialFormatter { virtual ~SerialFormatter() = default; virtual std::string format(std::string in) = 0; }; std::string BarSerialFormatter::format(std::string in) { return "M:" + in + ","; } std::string FooSerialFormatter::format(std::string in) { return std::to_string(mSequenceNumber++) + ":" + input; } struct CommunicationManager { CommunicationManager( SerialPortClient& serialPortClient, SerialFormatter& serialFormatter); void sendViaSerial(std::string message); private: SerialPortClient& mSerialPortClient; SerialFormatter& mSerialFormatter; }; void CommunicationManager::sendViaSerial(std::string message) { mSerialPortClient.send(mSerialFormatter.format(message)); }
  • 33. Refactoring workshop ● 19th of May ● Deeper look ● More patterns ○ Including the dreaded singleton ● Unit tests ● More Q&A ● Hands-on exercises ● 2 hours ● 399 SEK
  • 34. Takeaways More patterns & working code examples: platisd/refactoring-for-testability-cpp Contact me: dimitris@platis.solutions ● Unit tests should be atomic and run fast ● Verify your code (only) ● It should be fun ○ Not much up-front effort ● Abstract & inject ○ Care about what and not how ● Write SOLID code ● Follow OOP best practices ● Follow CPP core guidelines