SlideShare a Scribd company logo
Speaker: Alexey Golub @Tyrrrz
Fallacies of Unit Testing
Road to reality-driven testing
WARNING
Controversial topic
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Clarification
Unit testing is not inherently bad, but the practices & beliefs surrounding it
often lead to cargo cult mentality
Some subjects in the talk may be opinionated and come from my own
experience, which might not represent yours
I will rely on “classic” definitions, where…
• Unit tests target smallest separable parts of code
• End-to-end tests target the application boundaries
• Integration tests target everything in-between
/whois ${speaker}
Speaker: Alexey Golub @Tyrrrz
• Open-source developer ✨
• Conference speaker & blogger 🌐️
• C#, F#, JavaScript 💻
• Cloud & web ☁️
• Automation & DevOps ⚙️
Speaker: Alexey Golub @Tyrrrz
Unit tests…
Unit
Small isolated part
No formal definition
Method of a class
Function of a module
Speaker: Alexey Golub @Tyrrrz
Unit
Unit
Unit
Unit
Unit tests must be isolated
Dependency inversion
Speaker: Alexey Golub @Tyrrrz
Unit
Unit
Unit
Unit
Abstraction
Unit tests must be isolated
Speaker: Alexey Golub @Tyrrrz
Unit tests must be pure
Unit
DB
File
system
• Unit tests achieve isolation by stubbing dependencies
• Unit tests don’t cover integrations between units
• Unit tests mostly avoid exercising effectful code
• Unit tests are primarily useful to verify business logic
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
public class LocationProvider : IDisposable
{
private readonly HttpClient _httpClient = new HttpClient();
// Gets location by query
public async Task<Location> GetLocationAsync(string locationQuery) { /* ... */ }
// Gets current location by IP
public async Task<Location> GetLocationAsync() { /* ... */ }
public void Dispose() => _httpClient.Dispose();
}
Design is fine, but
not unit-testable
public class SolarCalculator : IDiposable
{
private readonly LocationProvider _locationProvider = new LocationProvider();
// Gets solar times for current location and specified date
public async Task<SolarTimes> GetSolarTimesAsync(DateTimeOffset date) { /* //. // }
public void Dispose() /> _locationProvider.Dispose();
}
Speaker: Alexey Golub @Tyrrrz
public interface ILocationProvider
{
Task<Location> GetLocationAsync(string locationQuery);
Task<Location> GetLocationAsync();
}
public class LocationProvider : ILocationProvider
{
private readonly HttpClient _httpClient;
public LocationProvider(HttpClient httpClient) => _httpClient = httpClient;
public async Task<Location> GetLocationAsync(string locationQuery) { /* ... */ }
public async Task<Location> GetLocationAsync() { /* ... */ }
}
public interface ISolarCalculator
{
Task<SolarTimes> GetSolarTimesAsync(DateTimeOffset date);
}
public class SolarCalculator : ISolarCalculator
{
private readonly ILocationProvider _locationProvider;
public SolarCalculator(ILocationProvider locationProvider) => _locationProvider = locationProvider;
public async Task<SolarTimes> GetSolarTimesAsync(DateTimeOffset date) { /* ... */ }
}
Isolation through
dependency
inversion
Design influence
• Isolation comes at the expense of fragmentation
• Objects no longer encapsulate their dependencies
• Abstractions for the sake of testing, not polymorphism
• Design driven by testability, rather than domain
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
public class SolarCalculatorTests
{
[Fact]
public async Task GetSolarTimesAsync_ForKyiv_ReturnsCorrectSolarTimes()
{
// Arrange
var location = new Location(50.45, 30.52);
var date = new DateTimeOffset(2019, 11, 04, 00, 00, 00, TimeSpan.FromHours(+2));
var expectedSolarTimes = new SolarTimes(
new TimeSpan(06, 55, 00),
new TimeSpan(16, 29, 00)
);
var locationProvider = Mock.Of<ILocationProvider>(lp =>
lp.GetLocationAsync() == Task.FromResult(location)
);
var solarCalculator = new SolarCalculator(locationProvider);
// Act
var solarTimes = await solarCalculator.GetSolarTimesAsync(date);
// Assert
solarTimes.Should().BeEquivalentTo(expectedSolarTimes);
}
}
Implementation-
aware testing
Tight coupling to a
specific class/method
Consider the drawbacks
• Unit tests rely on implementation details
Speaker: Alexey Golub @Tyrrrz
Unit
Dep A
Dep B
Dep C
Which method does it use?
How is it used?
When does it call it?
How many times does it call it?
Consider the drawbacks
• Unit tests often lead to more complicated design
Speaker: Alexey Golub @Tyrrrz
Unit Unit
Unit
Unit Unit
Unit
Consider the drawbacks
• Unit tests can get quite expensive
Speaker: Alexey Golub @Tyrrrz
UnitDep
Mock
A
Mock
B
Mock
C
Consider the drawbacks
• Unit tests are limited in purpose
Speaker: Alexey Golub @Tyrrrz
Payroll
System
CRUD
application
Business logic (%)
Consider the drawbacks
• Unit tests don’t exercise user behavior
Speaker: Alexey Golub @Tyrrrz
User
API
UI
CLI
Unit
Unit
Unit
Unit
Speaker: Alexey Golub @Tyrrrz
Pyramid-driven testing
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Development tests need to be
• Fast
• Cheap
• Predictable
• Stable
Speaker: Alexey Golub @Tyrrrz
High-level tests are
• Not Fast
• Not Cheap
• Not Predictable
• Not Stable
(allegedly)
Handled by QAs
Often
avoided
entirely
Development
testing
Speaker: Alexey Golub @Tyrrrz
Someone
else’s
problem
Integration tests are not slow
• Just because they can be, doesn’t mean they should be
• Technologies make things easier
• Architecture dictates testability
Speaker: Alexey Golub @Tyrrrz
Return on investment is not linear
• Isolation isn’t free
• Higher-level tests are often cheaper
• Cheap & fast tests aren’t automatically valuable
Speaker: Alexey Golub @Tyrrrz
Business logic is not a constant
• Value of unit testing is contingent on the amount of business logic
• Modern software contains a lot of “glue” code
• Depending on the project, the pyramid may be misleading
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Development
testing
Unit
testing≠
Reality-driven testing
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Component
System
Confidence
(value)
User
Systemboundary
Unit level End-to-end levelIntegration scope
Speaker: Alexey Golub @Tyrrrz
User needs to be able get solar times for their location
User needs to be able get solar times during periods of midnight sun
User needs to be able get solar times even if their location cannot be determined
Functional requirements
Speaker: Alexey Golub @Tyrrrz
public class SolarTimesSpecs
{
[Fact]
public async Task User_can_get_solar_times_automatically_for_their_location() { /* //. // }
[Fact]
public async Task User_can_get_solar_times_during_periods_of_midnight_sun() { /* //. // }
[Fact]
public async Task User_can_get_solar_times_if_their_location_cannot_be_determined() { /* //. // }
}
Functional tests
Speaker: Alexey Golub @Tyrrrz
Functional tests Powered by xUnit’s
test display options
✓ Live documentation
✓ Human-readable test
names
✓ No dependency on code
structure
Functional requirements can be…
• Formal
Software specification documents, user stories, etc.
• Informal
JIRA tickets, readme, API documentation, usage instructions, assumed, etc.
Every software has them, in one form or the other (yes, even yours)
Speaker: Alexey Golub @Tyrrrz
Speaker: Alexey Golub @Tyrrrz
Guidelines
• Write tests that are as highly integrated as possible ⚙️
• Keep their speed, complexity, and stability acceptable 🚀
• Partition tests based on threads of behavior 🖱️
• Test suite should not mirror code structure 🔗
Example
Functional test suite for an ASP.NET Core application
Speaker: Alexey Golub @Tyrrrz
public class LocationProvider
{
private readonly HttpClient _httpClient;
public LocationProvider(HttpClient httpClient) />
_httpClient = httpClient;
public async Task<Location> GetLocationAsync(IPAddress ip)
{
// If IP is local, just don't pass anything (useful when running on localhost)
var ipFormatted = !ip.IsLocal() ? ip.MapToIPv4().ToString() : "";
var json = await _httpClient.GetJsonAsync($"http://ip-api.com/json/{ipFormatted}");
var latitude = json.GetProperty("lat").GetDouble();
var longitude = json.GetProperty("lon").GetDouble();
return new Location
{
Latitude = latitude,
Longitude = longitude
};
}
}
Speaker: Alexey Golub @Tyrrrz
public class SolarCalculator
{
private readonly LocationProvider _locationProvider;
public SolarCalculator(LocationProvider locationProvider) />
_locationProvider = locationProvider;
private static TimeSpan CalculateSolarTimeOffset(Location location, DateTimeOffset instant,
double zenith, bool isSunrise)
{
/* //. //
}
public async Task<SolarTimes> GetSolarTimesAsync(IPAddress ip, DateTimeOffset date)
{
var location = await _locationProvider.GetLocationAsync(ip);
var sunriseOffset = CalculateSolarTimeOffset(location, date, 90.83, true);
var sunsetOffset = CalculateSolarTimeOffset(location, date, 90.83, false);
var sunrise = date.ResetTimeOfDay().Add(sunriseOffset);
var sunset = date.ResetTimeOfDay().Add(sunsetOffset);
return new SolarTimes
{
Sunrise = sunrise,
Sunset = sunset
};
}
}
Speaker: Alexey Golub @Tyrrrz
[ApiController, Route("solartimes")]
public class SolarTimeController : ControllerBase
{
private readonly SolarCalculator _solarCalculator;
private readonly CachingLayer _cachingLayer;
public SolarTimeController(SolarCalculator solarCalculator, CachingLayer cachingLayer)
{
_solarCalculator = solarCalculator;
_cachingLayer = cachingLayer;
}
[HttpGet("by_ip")]
public async Task<IActionResult> GetByIp(DateTimeOffset? date)
{
var ip = HttpContext.Connection.RemoteIpAddress;
var cacheKey = $"{ip},{date}";
var cachedSolarTimes = await _cachingLayer.TryGetAsync<SolarTimes>(cacheKey);
if (cachedSolarTimes != null)
return Ok(cachedSolarTimes);
var solarTimes = await _solarCalculator.GetSolarTimesAsync(ip, date ?? DateTimeOffset.Now);
await _cachingLayer.SetAsync(cacheKey, solarTimes);
return Ok(solarTimes);
}
}
Speaker: Alexey Golub @Tyrrrz
public class CachingLayer
{
private readonly IConnectionMultiplexer _redis;
public CachingLayer(IConnectionMultiplexer connectionMultiplexer) =>
_redis = connectionMultiplexer;
public async Task<T> TryGetAsync<T>(string key) where T : class
{
var result = await _redis.GetDatabase().StringGetAsync(key);
if (result.HasValue)
return JsonSerializer.Deserialize<T>(result.ToString());
return null;
}
public async Task SetAsync<T>(string key, T obj) where T : class =>
await _redis.GetDatabase().StringSetAsync(key, JsonSerializer.Serialize(obj));
}
Speaker: Alexey Golub @Tyrrrz
public class Startup
{
// //.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o /> o.EnableEndpointRouting = false);
services.AddSingleton<IConnectionMultiplexer>(
ConnectionMultiplexer.Connect(GetRedisConnectionString()));
services.AddSingleton<CachingLayer>();
services.AddHttpClient<LocationProvider>();
services.AddTransient<SolarCalculator>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseMvcWithDefaultRoute();
}
}
Speaker: Alexey Golub @Tyrrrz
public class FakeApp : IDisposable
{
private readonly WebApplicationFactory<Startup> _appFactory;
public HttpClient Client { get; }
public FakeApp()
{
_appFactory = new WebApplicationFactory<Startup>();
Client = _appFactory.CreateClient();
}
public void Dispose()
{
Client.Dispose();
_appFactory.Dispose();
}
}
Speaker: Alexey Golub @Tyrrrz
Runs the entire HTTP
pipeline in-memory
Get a client to talk to
our app
public class RedisFixture : IAsyncLifetime
{
private string _containerId;
public async Task InitializeAsync()
{
var result = await Cli.Wrap("docker")
.WithArguments("run -d -p 6379:6379 redis")
.ExecuteBufferedAsync();
_containerId = result.StandardOutput.Trim();
}
public async Task ResetAsync() />
await Cli.Wrap("docker")
.WithArguments($"exec {_containerId} redis-cli FLUSHALL")
.ExecuteAsync();
public async Task DisposeAsync() />
await Cli.Wrap("docker")
.WithArguments($"container kill {_containerId}")
.ExecuteAsync();
}
Speaker: Alexey Golub @Tyrrrz
Ideally should bind
to a random port
Can also use:
• Docker.NET
• .NET TestContainers
public class SolarTimeSpecs : IClassFixture<RedisFixture>, IAsyncLifetime
{
private readonly RedisFixture _redisFixture;
public SolarTimeSpecs(RedisFixture redisFixture) =>
_redisFixture = redisFixture;
public async Task InitializeAsync() => await _redisFixture.ResetAsync();
[Fact]
public async Task User_can_get_solar_times_for_their_location_by_ip()
{
// Arrange
using var app = new FakeApp();
// Act
var response = await app.Client.GetStringAsync("/solartimes/by_ip");
var solarTimes = JsonSerializer.Deserialize<SolarTimes>(response);
// Assert
solarTimes.Sunset.Should().BeWithin(TimeSpan.FromDays(1)).After(solarTimes.Sunrise);
solarTimes.Sunrise.Should().BeCloseTo(DateTimeOffset.Now, TimeSpan.FromDays(1));
solarTimes.Sunset.Should().BeCloseTo(DateTimeOffset.Now, TimeSpan.FromDays(1));
}
}
Speaker: Alexey Golub @Tyrrrz
Redis is reset
between tests
Requirement-driven test
Property-based assertions
public class FakeIpStartupFilter : IStartupFilter
{
public IPAddress Ip { get; set; } = IPAddress.Parse("::1");
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> nextFilter)
{
return app =>
{
app.Use(async (ctx, next) =>
{
ctx.Connection.RemoteIpAddress = Ip;
await next();
});
nextFilter(app);
};
}
}
Speaker: Alexey Golub @Tyrrrz
public class FakeApp : IDisposable
{
private readonly WebApplicationFactory<Startup> _appFactory;
private readonly FakeIpStartupFilter _fakeIpStartupFilter = new FakeIpStartupFilter();
public HttpClient Client { get; }
public IPAddress ClientIp
{
get => _fakeIpStartupFilter.Ip;
set => _fakeIpStartupFilter.Ip = value;
}
public FakeApp()
{
_appFactory = new WebApplicationFactory<Startup>().WithWebHostBuilder(o =>
{
o.ConfigureServices(s =>
{
s.AddSingleton<IStartupFilter>(_fakeIpStartupFilter);
});
});
Client = _appFactory.CreateClient();
}
// ...
}
Speaker: Alexey Golub @Tyrrrz
Inject fake IP provider
into pipeline
[Fact]
public async Task User_can_get_solar_times_for_their_location_by_ip()
{
// Arrange
using var app = new FakeApp
{
ClientIp = IPAddress.Parse("20.112.101.1")
};
var date = new DateTimeOffset(2020, 07, 03, 0, 0, 0, TimeSpan.FromHours(-5));
var expectedSunrise = new DateTimeOffset(2020, 07, 03, 05, 20, 37, TimeSpan.FromHours(-5));
var expectedSunset = new DateTimeOffset(2020, 07, 03, 20, 28, 54, TimeSpan.FromHours(-5));
// Act
var query = new QueryBuilder
{
{"date", date.ToString("O", CultureInfo.InvariantCulture)}
};
var response = await app.Client.GetStringAsync($"/solartimes/by_ip{query}");
var solarTimes = JsonSerializer.Deserialize<SolarTimes>(response);
// Assert
solarTimes.Sunrise.Should().BeCloseTo(expectedSunrise, TimeSpan.FromSeconds(1));
solarTimes.Sunset.Should().BeCloseTo(expectedSunset, TimeSpan.FromSeconds(1));
}
Speaker: Alexey Golub @Tyrrrz
Strict assertions
Speaker: Alexey Golub @Tyrrrz
✓ No mocks
✓ Real infrastructural dependencies
✓ Full HTTP pipeline
✓ High confidence
✓ Reasonable cost & speed
Speaker: Alexey Golub @Tyrrrz
The goal is not to cover everything end-to-end,
but to gain the highest return on investment
If you can test at the highest level, do it.
If not, reduce scope until the results are acceptable.
Confidence
Usability
Summary
1. Think critically and challenge best practices
2. Don’t rely on the test pyramid too much
3. Separate tests by functionality, rather than by classes or scope
4. Aim for highest level of integration with reasonable cost, speed, reliability
5. Avoid sacrificing software design for testability
6. Consider mocking only as a last resort
Speaker: Alexey Golub @Tyrrrz
Resources
• Unit Testing is Overrated (Alexey Golub)
https://tyrrrz.me/blog/unit-testing-is-overrated
• Write tests. Not too many. Mostly integration (Kent C. Dodds)
https://kentcdodds.com/blog/write-tests
• Mocking is a Code Smell (Eric Elliott)
https://medium.com/javascript-scene/mocking-is-a-code-smell-944a70c90a6a
• Test-induced design damage (David Heinemeier Hansson)
https://dhh.dk/2014/test-induced-design-damage.html
Speaker: Alexey Golub @Tyrrrz
Thank you!
Speaker: Alexey Golub @Tyrrrz

More Related Content

What's hot

Improving the Quality of Existing Software
Improving the Quality of Existing SoftwareImproving the Quality of Existing Software
Improving the Quality of Existing Software
Steven Smith
 
Improving the Quality of Existing Software
Improving the Quality of Existing SoftwareImproving the Quality of Existing Software
Improving the Quality of Existing Software
Steven Smith
 
Improving the Quality of Existing Software - DevIntersection April 2016
Improving the Quality of Existing Software - DevIntersection April 2016Improving the Quality of Existing Software - DevIntersection April 2016
Improving the Quality of Existing Software - DevIntersection April 2016
Steven Smith
 
Clean Code III - Software Craftsmanship at SoCal Code Camp San Diego (07/27/2...
Clean Code III - Software Craftsmanship at SoCal Code Camp San Diego (07/27/2...Clean Code III - Software Craftsmanship at SoCal Code Camp San Diego (07/27/2...
Clean Code III - Software Craftsmanship at SoCal Code Camp San Diego (07/27/2...
Theo Jungeblut
 
The Cowardly Test-o-Phobe's Guide To Testing
The Cowardly Test-o-Phobe's Guide To TestingThe Cowardly Test-o-Phobe's Guide To Testing
The Cowardly Test-o-Phobe's Guide To Testing
Tim Duckett
 
Clean code presentation
Clean code presentationClean code presentation
Clean code presentation
Bhavin Gandhi
 
Learn How to Unit Test Your Android Application (with Robolectric)
Learn How to Unit Test Your Android Application (with Robolectric)Learn How to Unit Test Your Android Application (with Robolectric)
Learn How to Unit Test Your Android Application (with Robolectric)
Marakana Inc.
 
How To Find Information On Your Own
How To Find Information On Your OwnHow To Find Information On Your Own
How To Find Information On Your Own
Dave Haeffner
 
QA / Testing Tools, Automation Testing, Online & Classroom Training
QA / Testing Tools, Automation Testing, Online & Classroom Training QA / Testing Tools, Automation Testing, Online & Classroom Training
QA / Testing Tools, Automation Testing, Online & Classroom Training
AnanthReddy38
 
Projects Valhalla, Loom and GraalVM at JCon 2020
Projects Valhalla, Loom and GraalVM at JCon 2020Projects Valhalla, Loom and GraalVM at JCon 2020
Projects Valhalla, Loom and GraalVM at JCon 2020
Vadym Kazulkin
 
TDD for APIs @ Europython 2015, Bilbao by Michael Kuehne
TDD for APIs @ Europython 2015, Bilbao by Michael KuehneTDD for APIs @ Europython 2015, Bilbao by Michael Kuehne
TDD for APIs @ Europython 2015, Bilbao by Michael Kuehne
Michael Kuehne-Schlinkert
 
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Alan Richardson
 
Generalization in Auto-Testing. How we put what we had into new Technological...
Generalization in Auto-Testing. How we put what we had into new Technological...Generalization in Auto-Testing. How we put what we had into new Technological...
Generalization in Auto-Testing. How we put what we had into new Technological...
SQALab
 
Improving The Quality of Existing Software
Improving The Quality of Existing SoftwareImproving The Quality of Existing Software
Improving The Quality of Existing Software
Steven Smith
 
Selenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver TutorialSelenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver Tutorial
Alan Richardson
 
Introducing domain driven design - dogfood con 2018
Introducing domain driven design - dogfood con 2018Introducing domain driven design - dogfood con 2018
Introducing domain driven design - dogfood con 2018
Steven Smith
 
Improving the Design of Existing Software
Improving the Design of Existing SoftwareImproving the Design of Existing Software
Improving the Design of Existing Software
Steven Smith
 
Clean Code I - Design Patterns and Best Practices at SoCal Code Camp San Dieg...
Clean Code I - Design Patterns and Best Practices at SoCal Code Camp San Dieg...Clean Code I - Design Patterns and Best Practices at SoCal Code Camp San Dieg...
Clean Code I - Design Patterns and Best Practices at SoCal Code Camp San Dieg...
Theo Jungeblut
 
Arquillian: Helping web developers and QA get along
Arquillian: Helping web developers and QA get alongArquillian: Helping web developers and QA get along
Arquillian: Helping web developers and QA get along
Lukáš Fryč
 
Automation is Easy! (python version)
Automation is Easy! (python version)Automation is Easy! (python version)
Automation is Easy! (python version)
Iakiv Kramarenko
 

What's hot (20)

Improving the Quality of Existing Software
Improving the Quality of Existing SoftwareImproving the Quality of Existing Software
Improving the Quality of Existing Software
 
Improving the Quality of Existing Software
Improving the Quality of Existing SoftwareImproving the Quality of Existing Software
Improving the Quality of Existing Software
 
Improving the Quality of Existing Software - DevIntersection April 2016
Improving the Quality of Existing Software - DevIntersection April 2016Improving the Quality of Existing Software - DevIntersection April 2016
Improving the Quality of Existing Software - DevIntersection April 2016
 
Clean Code III - Software Craftsmanship at SoCal Code Camp San Diego (07/27/2...
Clean Code III - Software Craftsmanship at SoCal Code Camp San Diego (07/27/2...Clean Code III - Software Craftsmanship at SoCal Code Camp San Diego (07/27/2...
Clean Code III - Software Craftsmanship at SoCal Code Camp San Diego (07/27/2...
 
The Cowardly Test-o-Phobe's Guide To Testing
The Cowardly Test-o-Phobe's Guide To TestingThe Cowardly Test-o-Phobe's Guide To Testing
The Cowardly Test-o-Phobe's Guide To Testing
 
Clean code presentation
Clean code presentationClean code presentation
Clean code presentation
 
Learn How to Unit Test Your Android Application (with Robolectric)
Learn How to Unit Test Your Android Application (with Robolectric)Learn How to Unit Test Your Android Application (with Robolectric)
Learn How to Unit Test Your Android Application (with Robolectric)
 
How To Find Information On Your Own
How To Find Information On Your OwnHow To Find Information On Your Own
How To Find Information On Your Own
 
QA / Testing Tools, Automation Testing, Online & Classroom Training
QA / Testing Tools, Automation Testing, Online & Classroom Training QA / Testing Tools, Automation Testing, Online & Classroom Training
QA / Testing Tools, Automation Testing, Online & Classroom Training
 
Projects Valhalla, Loom and GraalVM at JCon 2020
Projects Valhalla, Loom and GraalVM at JCon 2020Projects Valhalla, Loom and GraalVM at JCon 2020
Projects Valhalla, Loom and GraalVM at JCon 2020
 
TDD for APIs @ Europython 2015, Bilbao by Michael Kuehne
TDD for APIs @ Europython 2015, Bilbao by Michael KuehneTDD for APIs @ Europython 2015, Bilbao by Michael Kuehne
TDD for APIs @ Europython 2015, Bilbao by Michael Kuehne
 
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
 
Generalization in Auto-Testing. How we put what we had into new Technological...
Generalization in Auto-Testing. How we put what we had into new Technological...Generalization in Auto-Testing. How we put what we had into new Technological...
Generalization in Auto-Testing. How we put what we had into new Technological...
 
Improving The Quality of Existing Software
Improving The Quality of Existing SoftwareImproving The Quality of Existing Software
Improving The Quality of Existing Software
 
Selenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver TutorialSelenium Clinic Eurostar 2012 WebDriver Tutorial
Selenium Clinic Eurostar 2012 WebDriver Tutorial
 
Introducing domain driven design - dogfood con 2018
Introducing domain driven design - dogfood con 2018Introducing domain driven design - dogfood con 2018
Introducing domain driven design - dogfood con 2018
 
Improving the Design of Existing Software
Improving the Design of Existing SoftwareImproving the Design of Existing Software
Improving the Design of Existing Software
 
Clean Code I - Design Patterns and Best Practices at SoCal Code Camp San Dieg...
Clean Code I - Design Patterns and Best Practices at SoCal Code Camp San Dieg...Clean Code I - Design Patterns and Best Practices at SoCal Code Camp San Dieg...
Clean Code I - Design Patterns and Best Practices at SoCal Code Camp San Dieg...
 
Arquillian: Helping web developers and QA get along
Arquillian: Helping web developers and QA get alongArquillian: Helping web developers and QA get along
Arquillian: Helping web developers and QA get along
 
Automation is Easy! (python version)
Automation is Easy! (python version)Automation is Easy! (python version)
Automation is Easy! (python version)
 

Similar to Fallacies of unit testing

Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Oleksii Holub
 
Testcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentationTestcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentation
Richard North
 
Chegg - iOS @ Scale
Chegg - iOS @ ScaleChegg - iOS @ Scale
Chegg - iOS @ Scale
Aviel Lazar
 
Gojko Adzic Cucumber
Gojko Adzic CucumberGojko Adzic Cucumber
Gojko Adzic Cucumber
Skills Matter
 
O365Con18 - Automate your Tasks through Azure Functions - Elio Struyf
O365Con18 - Automate your Tasks through Azure Functions - Elio StruyfO365Con18 - Automate your Tasks through Azure Functions - Elio Struyf
O365Con18 - Automate your Tasks through Azure Functions - Elio Struyf
NCCOMMS
 
Learning iOS and hunting NSZombies in 3 weeks
Learning iOS and hunting NSZombies in 3 weeksLearning iOS and hunting NSZombies in 3 weeks
Learning iOS and hunting NSZombies in 3 weeks
Calvin Cheng
 
jDays Sweden 2016
jDays Sweden 2016jDays Sweden 2016
jDays Sweden 2016
Alex Theedom
 
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit Testing
Steven Smith
 
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
FalafelSoftware
 
Level Up Your Integration Testing With Testcontainers
Level Up Your Integration Testing With TestcontainersLevel Up Your Integration Testing With Testcontainers
Level Up Your Integration Testing With Testcontainers
VMware Tanzu
 
Clean Code - Design Patterns and Best Practices at Silicon Valley Code Camp
Clean Code - Design Patterns and Best Practices at Silicon Valley Code CampClean Code - Design Patterns and Best Practices at Silicon Valley Code Camp
Clean Code - Design Patterns and Best Practices at Silicon Valley Code Camp
Theo Jungeblut
 
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
seleniumconf
 
Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality
 Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality
Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality
Daniel Gallego Vico
 
Protractor survival guide
Protractor survival guideProtractor survival guide
Protractor survival guide
László Andrási
 
Into The Box 2018 | Assert control over your legacy applications
Into The Box 2018 | Assert control over your legacy applicationsInto The Box 2018 | Assert control over your legacy applications
Into The Box 2018 | Assert control over your legacy applications
Ortus Solutions, Corp
 
How to Build Your Own Test Automation Framework?
How to Build Your Own Test Automation Framework?How to Build Your Own Test Automation Framework?
How to Build Your Own Test Automation Framework?
Dmitry Buzdin
 
Devoxx UK 2015: How Java EE has changed pattern implementation
Devoxx UK 2015: How Java EE has changed pattern implementationDevoxx UK 2015: How Java EE has changed pattern implementation
Devoxx UK 2015: How Java EE has changed pattern implementation
Alex Theedom
 
DevOps Fest 2020. Alexey Golub. GitHub Actions in action
DevOps Fest 2020. Alexey Golub. GitHub Actions in actionDevOps Fest 2020. Alexey Golub. GitHub Actions in action
DevOps Fest 2020. Alexey Golub. GitHub Actions in action
DevOps_Fest
 
Scala, ECS, Docker: Delayed Execution @Coursera
Scala, ECS, Docker: Delayed Execution @CourseraScala, ECS, Docker: Delayed Execution @Coursera
Scala, ECS, Docker: Delayed Execution @Coursera
C4Media
 
OpenDaylight Developer Experience 2.0
 OpenDaylight Developer Experience 2.0 OpenDaylight Developer Experience 2.0
OpenDaylight Developer Experience 2.0
Michael Vorburger
 

Similar to Fallacies of unit testing (20)

Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
Alexey Golub - Dependency absolution (application as a pipeline) | Svitla Sma...
 
Testcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentationTestcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentation
 
Chegg - iOS @ Scale
Chegg - iOS @ ScaleChegg - iOS @ Scale
Chegg - iOS @ Scale
 
Gojko Adzic Cucumber
Gojko Adzic CucumberGojko Adzic Cucumber
Gojko Adzic Cucumber
 
O365Con18 - Automate your Tasks through Azure Functions - Elio Struyf
O365Con18 - Automate your Tasks through Azure Functions - Elio StruyfO365Con18 - Automate your Tasks through Azure Functions - Elio Struyf
O365Con18 - Automate your Tasks through Azure Functions - Elio Struyf
 
Learning iOS and hunting NSZombies in 3 weeks
Learning iOS and hunting NSZombies in 3 weeksLearning iOS and hunting NSZombies in 3 weeks
Learning iOS and hunting NSZombies in 3 weeks
 
jDays Sweden 2016
jDays Sweden 2016jDays Sweden 2016
jDays Sweden 2016
 
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit TestingBreaking Dependencies to Allow Unit Testing
Breaking Dependencies to Allow Unit Testing
 
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
 
Level Up Your Integration Testing With Testcontainers
Level Up Your Integration Testing With TestcontainersLevel Up Your Integration Testing With Testcontainers
Level Up Your Integration Testing With Testcontainers
 
Clean Code - Design Patterns and Best Practices at Silicon Valley Code Camp
Clean Code - Design Patterns and Best Practices at Silicon Valley Code CampClean Code - Design Patterns and Best Practices at Silicon Valley Code Camp
Clean Code - Design Patterns and Best Practices at Silicon Valley Code Camp
 
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
 
Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality
 Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality
Droidcon Spain 2016 - The Pragmatic Android Programmer: from hype to reality
 
Protractor survival guide
Protractor survival guideProtractor survival guide
Protractor survival guide
 
Into The Box 2018 | Assert control over your legacy applications
Into The Box 2018 | Assert control over your legacy applicationsInto The Box 2018 | Assert control over your legacy applications
Into The Box 2018 | Assert control over your legacy applications
 
How to Build Your Own Test Automation Framework?
How to Build Your Own Test Automation Framework?How to Build Your Own Test Automation Framework?
How to Build Your Own Test Automation Framework?
 
Devoxx UK 2015: How Java EE has changed pattern implementation
Devoxx UK 2015: How Java EE has changed pattern implementationDevoxx UK 2015: How Java EE has changed pattern implementation
Devoxx UK 2015: How Java EE has changed pattern implementation
 
DevOps Fest 2020. Alexey Golub. GitHub Actions in action
DevOps Fest 2020. Alexey Golub. GitHub Actions in actionDevOps Fest 2020. Alexey Golub. GitHub Actions in action
DevOps Fest 2020. Alexey Golub. GitHub Actions in action
 
Scala, ECS, Docker: Delayed Execution @Coursera
Scala, ECS, Docker: Delayed Execution @CourseraScala, ECS, Docker: Delayed Execution @Coursera
Scala, ECS, Docker: Delayed Execution @Coursera
 
OpenDaylight Developer Experience 2.0
 OpenDaylight Developer Experience 2.0 OpenDaylight Developer Experience 2.0
OpenDaylight Developer Experience 2.0
 

More from Oleksii Holub

Reality-Driven Testing using TestContainers
Reality-Driven Testing using TestContainersReality-Driven Testing using TestContainers
Reality-Driven Testing using TestContainers
Oleksii Holub
 
Intro to CliWrap
Intro to CliWrapIntro to CliWrap
Intro to CliWrap
Oleksii Holub
 
Intro to CliWrap
Intro to CliWrapIntro to CliWrap
Intro to CliWrap
Oleksii Holub
 
Expression trees in C#
Expression trees in C#Expression trees in C#
Expression trees in C#
Oleksii Holub
 
Expression trees in c#
Expression trees in c#Expression trees in c#
Expression trees in c#
Oleksii Holub
 
GitHub Actions in action
GitHub Actions in actionGitHub Actions in action
GitHub Actions in action
Oleksii Holub
 
Alexey Golub - Writing parsers in c# | 3Shape Meetup
Alexey Golub - Writing parsers in c# | 3Shape MeetupAlexey Golub - Writing parsers in c# | 3Shape Meetup
Alexey Golub - Writing parsers in c# | 3Shape Meetup
Oleksii Holub
 

More from Oleksii Holub (7)

Reality-Driven Testing using TestContainers
Reality-Driven Testing using TestContainersReality-Driven Testing using TestContainers
Reality-Driven Testing using TestContainers
 
Intro to CliWrap
Intro to CliWrapIntro to CliWrap
Intro to CliWrap
 
Intro to CliWrap
Intro to CliWrapIntro to CliWrap
Intro to CliWrap
 
Expression trees in C#
Expression trees in C#Expression trees in C#
Expression trees in C#
 
Expression trees in c#
Expression trees in c#Expression trees in c#
Expression trees in c#
 
GitHub Actions in action
GitHub Actions in actionGitHub Actions in action
GitHub Actions in action
 
Alexey Golub - Writing parsers in c# | 3Shape Meetup
Alexey Golub - Writing parsers in c# | 3Shape MeetupAlexey Golub - Writing parsers in c# | 3Shape Meetup
Alexey Golub - Writing parsers in c# | 3Shape Meetup
 

Recently uploaded

Presentatie 4. Jochen Cremer - TU Delft 28 mei 2024
Presentatie 4. Jochen Cremer - TU Delft 28 mei 2024Presentatie 4. Jochen Cremer - TU Delft 28 mei 2024
Presentatie 4. Jochen Cremer - TU Delft 28 mei 2024
Dutch Power
 
Carrer goals.pptx and their importance in real life
Carrer goals.pptx  and their importance in real lifeCarrer goals.pptx  and their importance in real life
Carrer goals.pptx and their importance in real life
artemacademy2
 
Collapsing Narratives: Exploring Non-Linearity • a micro report by Rosie Wells
Collapsing Narratives: Exploring Non-Linearity • a micro report by Rosie WellsCollapsing Narratives: Exploring Non-Linearity • a micro report by Rosie Wells
Collapsing Narratives: Exploring Non-Linearity • a micro report by Rosie Wells
Rosie Wells
 
ASONAM2023_presection_slide_track-recommendation.pdf
ASONAM2023_presection_slide_track-recommendation.pdfASONAM2023_presection_slide_track-recommendation.pdf
ASONAM2023_presection_slide_track-recommendation.pdf
ToshihiroIto4
 
Tom tresser burning issue.pptx My Burning issue
Tom tresser burning issue.pptx My Burning issueTom tresser burning issue.pptx My Burning issue
Tom tresser burning issue.pptx My Burning issue
amekonnen
 
Burning Issue Presentation By Kenmaryon.pdf
Burning Issue Presentation By Kenmaryon.pdfBurning Issue Presentation By Kenmaryon.pdf
Burning Issue Presentation By Kenmaryon.pdf
kkirkland2
 
Artificial Intelligence, Data and Competition – OECD – June 2024 OECD discussion
Artificial Intelligence, Data and Competition – OECD – June 2024 OECD discussionArtificial Intelligence, Data and Competition – OECD – June 2024 OECD discussion
Artificial Intelligence, Data and Competition – OECD – June 2024 OECD discussion
OECD Directorate for Financial and Enterprise Affairs
 
Mẫu PPT kế hoạch làm việc sáng tạo cho nửa cuối năm PowerPoint
Mẫu PPT kế hoạch làm việc sáng tạo cho nửa cuối năm PowerPointMẫu PPT kế hoạch làm việc sáng tạo cho nửa cuối năm PowerPoint
Mẫu PPT kế hoạch làm việc sáng tạo cho nửa cuối năm PowerPoint
1990 Media
 
Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...
Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...
Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...
OECD Directorate for Financial and Enterprise Affairs
 
Gregory Harris' Civics Presentation.pptx
Gregory Harris' Civics Presentation.pptxGregory Harris' Civics Presentation.pptx
Gregory Harris' Civics Presentation.pptx
gharris9
 
Updated diagnosis. Cause and treatment of hypothyroidism
Updated diagnosis. Cause and treatment of hypothyroidismUpdated diagnosis. Cause and treatment of hypothyroidism
Updated diagnosis. Cause and treatment of hypothyroidism
Faculty of Medicine And Health Sciences
 
Artificial Intelligence, Data and Competition – LIM – June 2024 OECD discussion
Artificial Intelligence, Data and Competition – LIM – June 2024 OECD discussionArtificial Intelligence, Data and Competition – LIM – June 2024 OECD discussion
Artificial Intelligence, Data and Competition – LIM – June 2024 OECD discussion
OECD Directorate for Financial and Enterprise Affairs
 
Competition and Regulation in Professions and Occupations – OECD – June 2024 ...
Competition and Regulation in Professions and Occupations – OECD – June 2024 ...Competition and Regulation in Professions and Occupations – OECD – June 2024 ...
Competition and Regulation in Professions and Occupations – OECD – June 2024 ...
OECD Directorate for Financial and Enterprise Affairs
 
XP 2024 presentation: A New Look to Leadership
XP 2024 presentation: A New Look to LeadershipXP 2024 presentation: A New Look to Leadership
XP 2024 presentation: A New Look to Leadership
samililja
 
Mastering the Concepts Tested in the Databricks Certified Data Engineer Assoc...
Mastering the Concepts Tested in the Databricks Certified Data Engineer Assoc...Mastering the Concepts Tested in the Databricks Certified Data Engineer Assoc...
Mastering the Concepts Tested in the Databricks Certified Data Engineer Assoc...
SkillCertProExams
 
Presentatie 8. Joost van der Linde & Daniel Anderton - Eliq 28 mei 2024
Presentatie 8. Joost van der Linde & Daniel Anderton - Eliq 28 mei 2024Presentatie 8. Joost van der Linde & Daniel Anderton - Eliq 28 mei 2024
Presentatie 8. Joost van der Linde & Daniel Anderton - Eliq 28 mei 2024
Dutch Power
 
Competition and Regulation in Professions and Occupations – ROBSON – June 202...
Competition and Regulation in Professions and Occupations – ROBSON – June 202...Competition and Regulation in Professions and Occupations – ROBSON – June 202...
Competition and Regulation in Professions and Occupations – ROBSON – June 202...
OECD Directorate for Financial and Enterprise Affairs
 
2024-05-30_meetup_devops_aix-marseille.pdf
2024-05-30_meetup_devops_aix-marseille.pdf2024-05-30_meetup_devops_aix-marseille.pdf
2024-05-30_meetup_devops_aix-marseille.pdf
Frederic Leger
 
Gregory Harris - Cycle 2 - Civics Presentation
Gregory Harris - Cycle 2 - Civics PresentationGregory Harris - Cycle 2 - Civics Presentation
Gregory Harris - Cycle 2 - Civics Presentation
gharris9
 
Pro-competitive Industrial Policy – LANE – June 2024 OECD discussion
Pro-competitive Industrial Policy – LANE – June 2024 OECD discussionPro-competitive Industrial Policy – LANE – June 2024 OECD discussion
Pro-competitive Industrial Policy – LANE – June 2024 OECD discussion
OECD Directorate for Financial and Enterprise Affairs
 

Recently uploaded (20)

Presentatie 4. Jochen Cremer - TU Delft 28 mei 2024
Presentatie 4. Jochen Cremer - TU Delft 28 mei 2024Presentatie 4. Jochen Cremer - TU Delft 28 mei 2024
Presentatie 4. Jochen Cremer - TU Delft 28 mei 2024
 
Carrer goals.pptx and their importance in real life
Carrer goals.pptx  and their importance in real lifeCarrer goals.pptx  and their importance in real life
Carrer goals.pptx and their importance in real life
 
Collapsing Narratives: Exploring Non-Linearity • a micro report by Rosie Wells
Collapsing Narratives: Exploring Non-Linearity • a micro report by Rosie WellsCollapsing Narratives: Exploring Non-Linearity • a micro report by Rosie Wells
Collapsing Narratives: Exploring Non-Linearity • a micro report by Rosie Wells
 
ASONAM2023_presection_slide_track-recommendation.pdf
ASONAM2023_presection_slide_track-recommendation.pdfASONAM2023_presection_slide_track-recommendation.pdf
ASONAM2023_presection_slide_track-recommendation.pdf
 
Tom tresser burning issue.pptx My Burning issue
Tom tresser burning issue.pptx My Burning issueTom tresser burning issue.pptx My Burning issue
Tom tresser burning issue.pptx My Burning issue
 
Burning Issue Presentation By Kenmaryon.pdf
Burning Issue Presentation By Kenmaryon.pdfBurning Issue Presentation By Kenmaryon.pdf
Burning Issue Presentation By Kenmaryon.pdf
 
Artificial Intelligence, Data and Competition – OECD – June 2024 OECD discussion
Artificial Intelligence, Data and Competition – OECD – June 2024 OECD discussionArtificial Intelligence, Data and Competition – OECD – June 2024 OECD discussion
Artificial Intelligence, Data and Competition – OECD – June 2024 OECD discussion
 
Mẫu PPT kế hoạch làm việc sáng tạo cho nửa cuối năm PowerPoint
Mẫu PPT kế hoạch làm việc sáng tạo cho nửa cuối năm PowerPointMẫu PPT kế hoạch làm việc sáng tạo cho nửa cuối năm PowerPoint
Mẫu PPT kế hoạch làm việc sáng tạo cho nửa cuối năm PowerPoint
 
Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...
Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...
Artificial Intelligence, Data and Competition – SCHREPEL – June 2024 OECD dis...
 
Gregory Harris' Civics Presentation.pptx
Gregory Harris' Civics Presentation.pptxGregory Harris' Civics Presentation.pptx
Gregory Harris' Civics Presentation.pptx
 
Updated diagnosis. Cause and treatment of hypothyroidism
Updated diagnosis. Cause and treatment of hypothyroidismUpdated diagnosis. Cause and treatment of hypothyroidism
Updated diagnosis. Cause and treatment of hypothyroidism
 
Artificial Intelligence, Data and Competition – LIM – June 2024 OECD discussion
Artificial Intelligence, Data and Competition – LIM – June 2024 OECD discussionArtificial Intelligence, Data and Competition – LIM – June 2024 OECD discussion
Artificial Intelligence, Data and Competition – LIM – June 2024 OECD discussion
 
Competition and Regulation in Professions and Occupations – OECD – June 2024 ...
Competition and Regulation in Professions and Occupations – OECD – June 2024 ...Competition and Regulation in Professions and Occupations – OECD – June 2024 ...
Competition and Regulation in Professions and Occupations – OECD – June 2024 ...
 
XP 2024 presentation: A New Look to Leadership
XP 2024 presentation: A New Look to LeadershipXP 2024 presentation: A New Look to Leadership
XP 2024 presentation: A New Look to Leadership
 
Mastering the Concepts Tested in the Databricks Certified Data Engineer Assoc...
Mastering the Concepts Tested in the Databricks Certified Data Engineer Assoc...Mastering the Concepts Tested in the Databricks Certified Data Engineer Assoc...
Mastering the Concepts Tested in the Databricks Certified Data Engineer Assoc...
 
Presentatie 8. Joost van der Linde & Daniel Anderton - Eliq 28 mei 2024
Presentatie 8. Joost van der Linde & Daniel Anderton - Eliq 28 mei 2024Presentatie 8. Joost van der Linde & Daniel Anderton - Eliq 28 mei 2024
Presentatie 8. Joost van der Linde & Daniel Anderton - Eliq 28 mei 2024
 
Competition and Regulation in Professions and Occupations – ROBSON – June 202...
Competition and Regulation in Professions and Occupations – ROBSON – June 202...Competition and Regulation in Professions and Occupations – ROBSON – June 202...
Competition and Regulation in Professions and Occupations – ROBSON – June 202...
 
2024-05-30_meetup_devops_aix-marseille.pdf
2024-05-30_meetup_devops_aix-marseille.pdf2024-05-30_meetup_devops_aix-marseille.pdf
2024-05-30_meetup_devops_aix-marseille.pdf
 
Gregory Harris - Cycle 2 - Civics Presentation
Gregory Harris - Cycle 2 - Civics PresentationGregory Harris - Cycle 2 - Civics Presentation
Gregory Harris - Cycle 2 - Civics Presentation
 
Pro-competitive Industrial Policy – LANE – June 2024 OECD discussion
Pro-competitive Industrial Policy – LANE – June 2024 OECD discussionPro-competitive Industrial Policy – LANE – June 2024 OECD discussion
Pro-competitive Industrial Policy – LANE – June 2024 OECD discussion
 

Fallacies of unit testing

  • 1. Speaker: Alexey Golub @Tyrrrz Fallacies of Unit Testing Road to reality-driven testing
  • 4. Speaker: Alexey Golub @Tyrrrz Clarification Unit testing is not inherently bad, but the practices & beliefs surrounding it often lead to cargo cult mentality Some subjects in the talk may be opinionated and come from my own experience, which might not represent yours I will rely on “classic” definitions, where… • Unit tests target smallest separable parts of code • End-to-end tests target the application boundaries • Integration tests target everything in-between
  • 5. /whois ${speaker} Speaker: Alexey Golub @Tyrrrz • Open-source developer ✨ • Conference speaker & blogger 🌐️ • C#, F#, JavaScript 💻 • Cloud & web ☁️ • Automation & DevOps ⚙️
  • 6. Speaker: Alexey Golub @Tyrrrz Unit tests… Unit Small isolated part No formal definition Method of a class Function of a module
  • 7. Speaker: Alexey Golub @Tyrrrz Unit Unit Unit Unit Unit tests must be isolated Dependency inversion
  • 8. Speaker: Alexey Golub @Tyrrrz Unit Unit Unit Unit Abstraction Unit tests must be isolated
  • 9. Speaker: Alexey Golub @Tyrrrz Unit tests must be pure Unit DB File system
  • 10. • Unit tests achieve isolation by stubbing dependencies • Unit tests don’t cover integrations between units • Unit tests mostly avoid exercising effectful code • Unit tests are primarily useful to verify business logic Speaker: Alexey Golub @Tyrrrz
  • 11. Speaker: Alexey Golub @Tyrrrz public class LocationProvider : IDisposable { private readonly HttpClient _httpClient = new HttpClient(); // Gets location by query public async Task<Location> GetLocationAsync(string locationQuery) { /* ... */ } // Gets current location by IP public async Task<Location> GetLocationAsync() { /* ... */ } public void Dispose() => _httpClient.Dispose(); } Design is fine, but not unit-testable public class SolarCalculator : IDiposable { private readonly LocationProvider _locationProvider = new LocationProvider(); // Gets solar times for current location and specified date public async Task<SolarTimes> GetSolarTimesAsync(DateTimeOffset date) { /* //. // } public void Dispose() /> _locationProvider.Dispose(); }
  • 12. Speaker: Alexey Golub @Tyrrrz public interface ILocationProvider { Task<Location> GetLocationAsync(string locationQuery); Task<Location> GetLocationAsync(); } public class LocationProvider : ILocationProvider { private readonly HttpClient _httpClient; public LocationProvider(HttpClient httpClient) => _httpClient = httpClient; public async Task<Location> GetLocationAsync(string locationQuery) { /* ... */ } public async Task<Location> GetLocationAsync() { /* ... */ } } public interface ISolarCalculator { Task<SolarTimes> GetSolarTimesAsync(DateTimeOffset date); } public class SolarCalculator : ISolarCalculator { private readonly ILocationProvider _locationProvider; public SolarCalculator(ILocationProvider locationProvider) => _locationProvider = locationProvider; public async Task<SolarTimes> GetSolarTimesAsync(DateTimeOffset date) { /* ... */ } } Isolation through dependency inversion
  • 13. Design influence • Isolation comes at the expense of fragmentation • Objects no longer encapsulate their dependencies • Abstractions for the sake of testing, not polymorphism • Design driven by testability, rather than domain Speaker: Alexey Golub @Tyrrrz
  • 14. Speaker: Alexey Golub @Tyrrrz public class SolarCalculatorTests { [Fact] public async Task GetSolarTimesAsync_ForKyiv_ReturnsCorrectSolarTimes() { // Arrange var location = new Location(50.45, 30.52); var date = new DateTimeOffset(2019, 11, 04, 00, 00, 00, TimeSpan.FromHours(+2)); var expectedSolarTimes = new SolarTimes( new TimeSpan(06, 55, 00), new TimeSpan(16, 29, 00) ); var locationProvider = Mock.Of<ILocationProvider>(lp => lp.GetLocationAsync() == Task.FromResult(location) ); var solarCalculator = new SolarCalculator(locationProvider); // Act var solarTimes = await solarCalculator.GetSolarTimesAsync(date); // Assert solarTimes.Should().BeEquivalentTo(expectedSolarTimes); } } Implementation- aware testing Tight coupling to a specific class/method
  • 15. Consider the drawbacks • Unit tests rely on implementation details Speaker: Alexey Golub @Tyrrrz Unit Dep A Dep B Dep C Which method does it use? How is it used? When does it call it? How many times does it call it?
  • 16. Consider the drawbacks • Unit tests often lead to more complicated design Speaker: Alexey Golub @Tyrrrz Unit Unit Unit Unit Unit Unit
  • 17. Consider the drawbacks • Unit tests can get quite expensive Speaker: Alexey Golub @Tyrrrz UnitDep Mock A Mock B Mock C
  • 18. Consider the drawbacks • Unit tests are limited in purpose Speaker: Alexey Golub @Tyrrrz Payroll System CRUD application Business logic (%)
  • 19. Consider the drawbacks • Unit tests don’t exercise user behavior Speaker: Alexey Golub @Tyrrrz User API UI CLI Unit Unit Unit Unit
  • 22. Speaker: Alexey Golub @Tyrrrz Development tests need to be • Fast • Cheap • Predictable • Stable
  • 23. Speaker: Alexey Golub @Tyrrrz High-level tests are • Not Fast • Not Cheap • Not Predictable • Not Stable (allegedly)
  • 24. Handled by QAs Often avoided entirely Development testing Speaker: Alexey Golub @Tyrrrz Someone else’s problem
  • 25. Integration tests are not slow • Just because they can be, doesn’t mean they should be • Technologies make things easier • Architecture dictates testability Speaker: Alexey Golub @Tyrrrz
  • 26. Return on investment is not linear • Isolation isn’t free • Higher-level tests are often cheaper • Cheap & fast tests aren’t automatically valuable Speaker: Alexey Golub @Tyrrrz
  • 27. Business logic is not a constant • Value of unit testing is contingent on the amount of business logic • Modern software contains a lot of “glue” code • Depending on the project, the pyramid may be misleading Speaker: Alexey Golub @Tyrrrz
  • 28. Speaker: Alexey Golub @Tyrrrz Development testing Unit testing≠
  • 30. Speaker: Alexey Golub @Tyrrrz Component System Confidence (value) User Systemboundary Unit level End-to-end levelIntegration scope
  • 31. Speaker: Alexey Golub @Tyrrrz User needs to be able get solar times for their location User needs to be able get solar times during periods of midnight sun User needs to be able get solar times even if their location cannot be determined Functional requirements
  • 32. Speaker: Alexey Golub @Tyrrrz public class SolarTimesSpecs { [Fact] public async Task User_can_get_solar_times_automatically_for_their_location() { /* //. // } [Fact] public async Task User_can_get_solar_times_during_periods_of_midnight_sun() { /* //. // } [Fact] public async Task User_can_get_solar_times_if_their_location_cannot_be_determined() { /* //. // } } Functional tests
  • 33. Speaker: Alexey Golub @Tyrrrz Functional tests Powered by xUnit’s test display options ✓ Live documentation ✓ Human-readable test names ✓ No dependency on code structure
  • 34. Functional requirements can be… • Formal Software specification documents, user stories, etc. • Informal JIRA tickets, readme, API documentation, usage instructions, assumed, etc. Every software has them, in one form or the other (yes, even yours) Speaker: Alexey Golub @Tyrrrz
  • 35. Speaker: Alexey Golub @Tyrrrz Guidelines • Write tests that are as highly integrated as possible ⚙️ • Keep their speed, complexity, and stability acceptable 🚀 • Partition tests based on threads of behavior 🖱️ • Test suite should not mirror code structure 🔗
  • 36. Example Functional test suite for an ASP.NET Core application Speaker: Alexey Golub @Tyrrrz
  • 37. public class LocationProvider { private readonly HttpClient _httpClient; public LocationProvider(HttpClient httpClient) /> _httpClient = httpClient; public async Task<Location> GetLocationAsync(IPAddress ip) { // If IP is local, just don't pass anything (useful when running on localhost) var ipFormatted = !ip.IsLocal() ? ip.MapToIPv4().ToString() : ""; var json = await _httpClient.GetJsonAsync($"http://ip-api.com/json/{ipFormatted}"); var latitude = json.GetProperty("lat").GetDouble(); var longitude = json.GetProperty("lon").GetDouble(); return new Location { Latitude = latitude, Longitude = longitude }; } } Speaker: Alexey Golub @Tyrrrz
  • 38. public class SolarCalculator { private readonly LocationProvider _locationProvider; public SolarCalculator(LocationProvider locationProvider) /> _locationProvider = locationProvider; private static TimeSpan CalculateSolarTimeOffset(Location location, DateTimeOffset instant, double zenith, bool isSunrise) { /* //. // } public async Task<SolarTimes> GetSolarTimesAsync(IPAddress ip, DateTimeOffset date) { var location = await _locationProvider.GetLocationAsync(ip); var sunriseOffset = CalculateSolarTimeOffset(location, date, 90.83, true); var sunsetOffset = CalculateSolarTimeOffset(location, date, 90.83, false); var sunrise = date.ResetTimeOfDay().Add(sunriseOffset); var sunset = date.ResetTimeOfDay().Add(sunsetOffset); return new SolarTimes { Sunrise = sunrise, Sunset = sunset }; } } Speaker: Alexey Golub @Tyrrrz
  • 39. [ApiController, Route("solartimes")] public class SolarTimeController : ControllerBase { private readonly SolarCalculator _solarCalculator; private readonly CachingLayer _cachingLayer; public SolarTimeController(SolarCalculator solarCalculator, CachingLayer cachingLayer) { _solarCalculator = solarCalculator; _cachingLayer = cachingLayer; } [HttpGet("by_ip")] public async Task<IActionResult> GetByIp(DateTimeOffset? date) { var ip = HttpContext.Connection.RemoteIpAddress; var cacheKey = $"{ip},{date}"; var cachedSolarTimes = await _cachingLayer.TryGetAsync<SolarTimes>(cacheKey); if (cachedSolarTimes != null) return Ok(cachedSolarTimes); var solarTimes = await _solarCalculator.GetSolarTimesAsync(ip, date ?? DateTimeOffset.Now); await _cachingLayer.SetAsync(cacheKey, solarTimes); return Ok(solarTimes); } } Speaker: Alexey Golub @Tyrrrz
  • 40. public class CachingLayer { private readonly IConnectionMultiplexer _redis; public CachingLayer(IConnectionMultiplexer connectionMultiplexer) => _redis = connectionMultiplexer; public async Task<T> TryGetAsync<T>(string key) where T : class { var result = await _redis.GetDatabase().StringGetAsync(key); if (result.HasValue) return JsonSerializer.Deserialize<T>(result.ToString()); return null; } public async Task SetAsync<T>(string key, T obj) where T : class => await _redis.GetDatabase().StringSetAsync(key, JsonSerializer.Serialize(obj)); } Speaker: Alexey Golub @Tyrrrz
  • 41. public class Startup { // //. public void ConfigureServices(IServiceCollection services) { services.AddMvc(o /> o.EnableEndpointRouting = false); services.AddSingleton<IConnectionMultiplexer>( ConnectionMultiplexer.Connect(GetRedisConnectionString())); services.AddSingleton<CachingLayer>(); services.AddHttpClient<LocationProvider>(); services.AddTransient<SolarCalculator>(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseMvcWithDefaultRoute(); } } Speaker: Alexey Golub @Tyrrrz
  • 42. public class FakeApp : IDisposable { private readonly WebApplicationFactory<Startup> _appFactory; public HttpClient Client { get; } public FakeApp() { _appFactory = new WebApplicationFactory<Startup>(); Client = _appFactory.CreateClient(); } public void Dispose() { Client.Dispose(); _appFactory.Dispose(); } } Speaker: Alexey Golub @Tyrrrz Runs the entire HTTP pipeline in-memory Get a client to talk to our app
  • 43. public class RedisFixture : IAsyncLifetime { private string _containerId; public async Task InitializeAsync() { var result = await Cli.Wrap("docker") .WithArguments("run -d -p 6379:6379 redis") .ExecuteBufferedAsync(); _containerId = result.StandardOutput.Trim(); } public async Task ResetAsync() /> await Cli.Wrap("docker") .WithArguments($"exec {_containerId} redis-cli FLUSHALL") .ExecuteAsync(); public async Task DisposeAsync() /> await Cli.Wrap("docker") .WithArguments($"container kill {_containerId}") .ExecuteAsync(); } Speaker: Alexey Golub @Tyrrrz Ideally should bind to a random port Can also use: • Docker.NET • .NET TestContainers
  • 44. public class SolarTimeSpecs : IClassFixture<RedisFixture>, IAsyncLifetime { private readonly RedisFixture _redisFixture; public SolarTimeSpecs(RedisFixture redisFixture) => _redisFixture = redisFixture; public async Task InitializeAsync() => await _redisFixture.ResetAsync(); [Fact] public async Task User_can_get_solar_times_for_their_location_by_ip() { // Arrange using var app = new FakeApp(); // Act var response = await app.Client.GetStringAsync("/solartimes/by_ip"); var solarTimes = JsonSerializer.Deserialize<SolarTimes>(response); // Assert solarTimes.Sunset.Should().BeWithin(TimeSpan.FromDays(1)).After(solarTimes.Sunrise); solarTimes.Sunrise.Should().BeCloseTo(DateTimeOffset.Now, TimeSpan.FromDays(1)); solarTimes.Sunset.Should().BeCloseTo(DateTimeOffset.Now, TimeSpan.FromDays(1)); } } Speaker: Alexey Golub @Tyrrrz Redis is reset between tests Requirement-driven test Property-based assertions
  • 45. public class FakeIpStartupFilter : IStartupFilter { public IPAddress Ip { get; set; } = IPAddress.Parse("::1"); public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> nextFilter) { return app => { app.Use(async (ctx, next) => { ctx.Connection.RemoteIpAddress = Ip; await next(); }); nextFilter(app); }; } } Speaker: Alexey Golub @Tyrrrz
  • 46. public class FakeApp : IDisposable { private readonly WebApplicationFactory<Startup> _appFactory; private readonly FakeIpStartupFilter _fakeIpStartupFilter = new FakeIpStartupFilter(); public HttpClient Client { get; } public IPAddress ClientIp { get => _fakeIpStartupFilter.Ip; set => _fakeIpStartupFilter.Ip = value; } public FakeApp() { _appFactory = new WebApplicationFactory<Startup>().WithWebHostBuilder(o => { o.ConfigureServices(s => { s.AddSingleton<IStartupFilter>(_fakeIpStartupFilter); }); }); Client = _appFactory.CreateClient(); } // ... } Speaker: Alexey Golub @Tyrrrz Inject fake IP provider into pipeline
  • 47. [Fact] public async Task User_can_get_solar_times_for_their_location_by_ip() { // Arrange using var app = new FakeApp { ClientIp = IPAddress.Parse("20.112.101.1") }; var date = new DateTimeOffset(2020, 07, 03, 0, 0, 0, TimeSpan.FromHours(-5)); var expectedSunrise = new DateTimeOffset(2020, 07, 03, 05, 20, 37, TimeSpan.FromHours(-5)); var expectedSunset = new DateTimeOffset(2020, 07, 03, 20, 28, 54, TimeSpan.FromHours(-5)); // Act var query = new QueryBuilder { {"date", date.ToString("O", CultureInfo.InvariantCulture)} }; var response = await app.Client.GetStringAsync($"/solartimes/by_ip{query}"); var solarTimes = JsonSerializer.Deserialize<SolarTimes>(response); // Assert solarTimes.Sunrise.Should().BeCloseTo(expectedSunrise, TimeSpan.FromSeconds(1)); solarTimes.Sunset.Should().BeCloseTo(expectedSunset, TimeSpan.FromSeconds(1)); } Speaker: Alexey Golub @Tyrrrz Strict assertions
  • 48. Speaker: Alexey Golub @Tyrrrz ✓ No mocks ✓ Real infrastructural dependencies ✓ Full HTTP pipeline ✓ High confidence ✓ Reasonable cost & speed
  • 49. Speaker: Alexey Golub @Tyrrrz The goal is not to cover everything end-to-end, but to gain the highest return on investment If you can test at the highest level, do it. If not, reduce scope until the results are acceptable. Confidence Usability
  • 50. Summary 1. Think critically and challenge best practices 2. Don’t rely on the test pyramid too much 3. Separate tests by functionality, rather than by classes or scope 4. Aim for highest level of integration with reasonable cost, speed, reliability 5. Avoid sacrificing software design for testability 6. Consider mocking only as a last resort Speaker: Alexey Golub @Tyrrrz
  • 51. Resources • Unit Testing is Overrated (Alexey Golub) https://tyrrrz.me/blog/unit-testing-is-overrated • Write tests. Not too many. Mostly integration (Kent C. Dodds) https://kentcdodds.com/blog/write-tests • Mocking is a Code Smell (Eric Elliott) https://medium.com/javascript-scene/mocking-is-a-code-smell-944a70c90a6a • Test-induced design damage (David Heinemeier Hansson) https://dhh.dk/2014/test-induced-design-damage.html Speaker: Alexey Golub @Tyrrrz
  • 52. Thank you! Speaker: Alexey Golub @Tyrrrz