SlideShare a Scribd company logo
Secret unit testing tools no one ever told you about
The secret unit testing tools no one ever told you about
Dror Helper (@dhelper)
Demos: https://github.com/dhelper/SecretUnitTestingToolsDemos
About.ME
Consultant @CodeValue
Developing software (professionally) since 2002
Mocking code since 2008
Clean coder & Test Driven Developer
Pluralsight Author
Blogger: http://blog.drorhelper.com
Why should I care about tools?
Background: unit testing tools
Well known unit testing tools
Server
Dev Machine
Source ControlBuild Server
Test Runner
Code
Coverage
Build Agent
Unit Testing
Framework
Isolation
Framework
?
xUnit test framework
public class BeforeAndAfter {
[SetUp]
public void Initialize() {
}
[TearDown]
public void Cleanup() {
}
[Test]
public void test1() {
}
[Test]
public void test2() {
}
}
Test Suite
Fixture
Test Case
Test Case
Test Case
Test Case
Test Case
Fixture
Test Case
Test Case
Fixture
Test Case
Test Case
Test Case
Mocking Frameworks
Unit test
Code under test
DependencyFake object(s)
What Mocking framework do?
1. Create Fake objects
2. Set behavior on fake objects
3. Verify method was called
And more...
[Test]
public void Calculate_ReturnTwoValidNumbers_ServerCalled()
{
IDataAccess fakeDataAccess = A.Fake<IDataAccess>();
A.CallTo(() => fakeDataAccess.GetData(A<string>.Ignored))
.Returns(new Tuple<int, int>(2, 3));
var fakeCalculatorService = A.Fake<ICalculatorService>();
var cut = new DistrobutedCalculator(fakeDataAccess, fakeCalculatorService);
cut.Calculate();
A.CallTo(() => fakeCalculatorService.Add(2,3)).MustHaveHappened();
}
Definition: unit tests
Automated piece of code that invokes
a unit of work in the system and then
checks a single assumption about the
behavior of that unit of work
[Roy Osherove, The Art Of Unit Testing]
Unit test structure
[Test]
public void MyTest()
{
}
No guidance  Fragile tests  Stop unit testing
What about AAA?
[Test]
public async void GetUserFromUrl() {
var clientFake = A.Fake<IJsonClient>();
A.CallTo(() => clientFake.HttpGetUncompressedAsync(A<string>.Ignored))
.Returns(Task.FromResult(JsonResult));
var userRepository = new UserRepository(clientFake);
var user = await userRepository.GetUser(11361);
var expected = new User {
Id=11361, DisplayName = "Dror Helper", ImageUrl=DefaultAvatar, Reputation=13904
};
Assert.That(user, Is.EqualTo(expected));
}
 Act
 Assert
Arrange
Test setup (Arrange)
Arrange issues
[TestMethod]
public async Task LoadUser_ReputationStaysTheSame_ReputationTrendSame() {
var user1 = new User {ImageUrl = "http://dummy.jpg", Reputation = 10};
var user2 = new User {ImageUrl = "http://dummy.jpg", Reputation = 10};
var fakeUserRepository = new FakeUserRepository(new[] { user1, user2 });
var viewModel = await InvokeAsync(() => new UserDetailsViewModel(fakeUserRepository));
await viewModel.LoadUser();
await viewModel.LoadUser();
var result = await InvokeAsync(() => ((SolidColorBrush)viewModel.ReputationTrend).Color);
Assert.AreEqual(Colors.White, result);
}
Solution: Setup/TearDown
[TestInitialize]
public async Task InitilizeUserViewModel() {
var user1 = new User { ImageUrl = "http://dummy.jpg", Reputation = 10 };
var user2 = new User { ImageUrl = "http://dummy.jpg", Reputation = 10 };
var fakeUserRepository = new FakeUserRepository(new[] { user1, user2 });
_viewModel = await InvokeAsync(() => new UserDetailsViewModel(fakeUserRepository));
}
[TestMethod]
public async Task LoadUser_ReputationStaysTheSame_ReputationTrendSame() {
await _viewModel.LoadUser();
await _viewModel.LoadUser();
var result = InvokeAsync(() => ((SolidColorBrush)_viewModel.ReputationTrend).Color);
Assert.AreEqual(Colors.White, result);
}
You shouldn’t use Setup in unit tests
private UserDetailsViewModel _viewModel;
[TestInitialize]
public async Task InitilizeUserViewModel() {
var user1 = new User { ImageUrl = "http://dummy.jpg", Reputation = 10 };
var user2 = new User { ImageUrl = "http://dummy.jpg", Reputation = 10 };
var fakeUserRepository = new FakeUserRepository(new[] { user1, user2 });
_viewModel = await InvokeAsync(() => new UserDetailsViewModel(fakeUserRepository));
}
[TestMethod]
public async Task LoadUser_ReputationStaysTheSame_ReputationTrendSame() {
await _viewModel.LoadUser();
await _viewModel.LoadUser();
var result = InvokeAsync(() => ((SolidColorBrush)_viewModel.ReputationTrend).Color);
Assert.AreEqual(Colors.White, result);
}
Solution: Extract to methods
[TestMethod]
public async Task LoadUser_ReputationStaysTheSame_ReputationTrendSame() {
var user1 = CreateUser(reputation: 10);
var user2 = CreateUser(reputation: 10);
var fakeUserRepository = new FakeUserRepository(new[] { user1, user2 });
var viewModel = await InvokeAsync(() => new UserDetailsViewModel(fakeUserRepository));
await viewModel.LoadUser();
await viewModel.LoadUser();
var result = await InvokeAsync(() => ((SolidColorBrush)viewModel.ReputationTrend).Color);
Assert.AreEqual(Colors.White, result);
}
Over extraction
[TestMethod]
public async Task LoadUser_ReputationStaysTheSame() {
var viewModel = InitializeSystem(10, 10);
await viewModel.LoadUser();
await viewModel.LoadUser();
CheckColor(Colors.White, viewModel);
}
The problem with factories
private User CreateUser(int reputation) {
return new User {
ImageUrl = "http://dummy.jpg",
Reputation = reputation
};
}
private User CreateUser(int reputation, string imageUrl) {
return new User
{
ImageUrl = imageUrl,
Reputation = reputation
};
}
Builder pattern
class UserBuilder
{
private int _id, _reputation;
private string _name, _imageUrl;
public UserBuilder() {
_id = 1;
_name = "dummy";
_imageUrl = "http://dummy.jpg";
}
User Build() {
return new User {
Id = _id, Name = _name, ImageUrl = _imageUrl, Reputation = _reputation
};
}
}
Builder pattern (cont.)
class UserBuilder
{
...
public UserBuilder WithName(string displayName) {
_displayName = displayName;
return this;
}
public UserBuilder WithReputation(int reputation) {
_reputation = reputation;
return this;
}
...
}
var user1 = new UserBuilder()
.WithReputation(10)
.Build();
Tool: AutoMocking Containers
Test Container SUT
http://blog.ploeh.dk/2013/03/11/auto-mocking-container/
New()
Configure
CreateCreate SUT
Act
Automocking with AutoFixture
[Fact]
public void YellIfTouchHotIron() {
var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization());
Fake<IMouth> fakeMouth = fixture.Freeze<Fake<IMouth>>();
Fake<IHand> fakeHand = fixture.Freeze<Fake<IHand>>();
A.CallTo(() => fakeHand.FakedObject.TouchIron(A<Iron>._)).Throws<BurnException>();
var brain = fixture.Create<Brain>();
brain.TouchIron(new Iron {IsHot = true});
A.CallTo(() => fakeMouth.FakedObject.Yell()).MustHaveHappened();
}
https://helpercode.com/2013/12/22/on-object-creation-and-unit-tests/
Problem: Complex inputs
• Big objects
• Deep objects
• Need for precision
• Lack of knowledge
Solution: use trivial inputs
Sometimes missing the point
Not always enough for “real test”
Solution: Serialization
Supported in most programming languages (XML, json).
Need development  testing delayed
Production code change  indefinitely delayed
Tool: Export using OzCode
Verification (Assert)
Can you spot the problem?
[TestMethod]
public void PerformSomeActionReturns42()
{
var myClass = ...
bool initOk = myClass.Initialize();
var result = myClass.PerformSomeAction();
Assert.IsTrue(initOk);
Assert.AreEqual(42, result);
}
http://stackoverflow.com/q/26400537/11361
Can you spot the problem?
[TestMethod]
public void TestPasswordComplexity()
{
var result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "1!").Result; //Changes the password.
Assert.IsFalse(result.Succeeded);
result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "123456789").Result; //Changes the password.
Assert.IsFalse(result.Succeeded);
result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "123456789!").Result; //Changes the password.
Assert.IsFalse(result.Succeeded);
result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "abcdefghijk").Result; //Changes the password.
Assert.IsFalse(result.Succeeded);
result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "abcdefghijK1!").Result; //Changes the password.
Assert.IsTrue(result.Succeeded);
}
http://stackoverflow.com/q/26400537/11361
How many Assert(s) per test?
One Assert Per Test!
Two Assert == Two Tests  Usually ???
”(…the Code is more)
what you'd call guidelines
than actual rules”
Sometimes multiple asserts make sense
[TestMethod]
public void CompareTwoAsserts()
{
var actual = GetNextMessage();
Assert.AreEqual(1, actual.Id);
Assert.AreEqual("str-1", actual.Content);
}
public class AssertAll
{
public static void Execute(params Action[] assertionsToRun)
{
var errorMessages = new List<exception>();
foreach (var action in assertionsToRun)
{
try
{
action.Invoke();
}
catch (Exception exc)
{
errorMessages.Add(exc);
}
}
if(errorMessages.Any())
{
var separator = string.Format("{0}{0}", Environment.NewLine);
string errorMessage = string.Join(separator, errorMessages);
Assert.Fail(string.Format("The following conditions failed:{0}{1}", Environment.NewLine, errorMessage));
}
}
}
http://blog.drorhelper.com/2011/02/multiple-asserts-done-right.html
Using AssertAll
[TestMethod]
public void CompareTwoAsserts()
{
var actual = CreateMessage();
AssertAll.Execute(
() => Assert.AreEqual(1, actual.Id),
() => Assert.AreEqual("str-1", actual.Content);
}
Some frameworks are catching up!
https://github.com/nunit/docs/wiki/Multiple-Asserts
Choosing the right Assert?
IsTrue vs. AreEqual
Parameter ordering confusion
StringAssert/CollectionAssert
It’s all about proper error messages
Tool: 3rd party assertion libraries
Better error messages
Readability
Multiple asserts*
×Additional dependency
×Limited UT framework support
×System.Object “SPAMED” by extension methods
Shouldly
[Fact]
public void AddTest()
{
var calculator = new Calculator();
var result = calculator.Add(2, 3);
Assert.Equal(6, result);
}
[Fact]
public void AddTest_Shouldly()
{
var calculator = new Calculator();
var result = calculator.Add(2, 3);
result.ShouldBe(6);
}
https://github.com/shouldly/shouldly
Shouldly.ShouldAssertException
result
should be
6
but was
5
Xunit.Sdk.EqualException
Assert.Equal() Failure
Expected: 6
Actual: 5
FluentAssertions
[Fact]
public void CompareTwoObjects()
{
var customer1 = new Customer("cust-1", "John Doe");
var customer2 = new Customer("cust-2", "John Doe");
customer1.ShouldBeEquivalentTo(customer2,
o => o.Excluding(customer => customer.Id));
}
http://www.fluentassertions.com/
AssertHelper
[Test]
public void CheckCompare()
{
var myClass = new MyClass();
Expect.That(() => myClass.ReturnFive() == 10);
}
[Test]
public void CheckTrue()
{
var myClass = new MyClass();
Expect.That(() => myClass.ReturnFalse() == true);
}
[Test]
public void StringStartsWith() {
var s1 = "1234567890";
Expect.That(() => s1.StartsWith("456"));
}
[Test]
public void CollectionContains()
{
var c1 = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
Expect.That(() => c1.Contains(41));
}
https://github.com/dhelper/AssertHelper
Test organization
Test structure issues
What to call the test?
AAA is not mandatory
What should I test?
How to avoid unreadable, complicated tests?
- Unit testing framework provide no structure
The BDD approach
Step
Definitions
Specifications == focused test
Feature: Addition
In order to avoid silly mistakes
As a math idiot
I want to be told the sum of two numbers
Scenario: Add two numbers
Given I have entered 50 into the calculator
And I have entered 70 into the calculator
When I press add
Then the result should be 120 on the screen
BDD Example: SpecFlow
http://www.specflow.org/
Tool: BDDfy
[TestClass]
public class CardHasBeenDisabled {
private Card _card;
private Atm _subject;
void GivenTheCardIsDisabled() {
_card = new Card(false, 100);
_subject = new Atm(100);
}
void WhenTheAccountHolderRequestsMoney() {
_subject.RequestMoney(_card, 20);
}
void ThenTheAtmShouldRetainTheCard() {
Assert.IsTrue(_subject.CardIsRetained);
}
void AndTheAtmShouldSayTheCardHasBeenRetained() {
Assert.AreEqual(DisplayMessage.CardIsRetained, _subject.Message);
}
[TestMethod]
public void Execute()
{
this.BDDfy();
}
}
Test execution
Everybody needs a CI server
Unit tests without a CI server are a waste of time
- if you're running all of the tests all of the time
locally you're a better man then I am
Tool: Continuous testing
DotCover
Typemock Runner
nCrunch
VS2017 (Enterprise)
The right tools will help you write good tests
Arrange
Builder Pattern
AutoMocking
Containers
Export
Assert
Shouldly
FluentAssertions
AssertHelper
Test
Structure
BDDfy
Continuous
Testing
Typemock
Runner
DotCover
nCrunch
Live Unit Testing
Dror Helper | @ dhelper | http://helpercode.com
Thank you!
Demos: https://github.com/dhelper/SecretUnitTestingToolsDemos
Secret unit testing tools no one ever told you about
Secret unit testing tools no one ever told you about

More Related Content

PPTX
The secret unit testing tools no one has ever told you about
PPTX
Unit testing patterns for concurrent code
PPTX
Building unit tests correctly
PDF
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
PDF
Stop Making Excuses and Start Testing Your JavaScript
PDF
Redux for ReactJS Programmers
PDF
droidparts
ODP
Unit testing with Easymock
The secret unit testing tools no one has ever told you about
Unit testing patterns for concurrent code
Building unit tests correctly
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Stop Making Excuses and Start Testing Your JavaScript
Redux for ReactJS Programmers
droidparts
Unit testing with Easymock

What's hot (20)

PDF
Android programming -_pushing_the_limits
PDF
33rd Degree 2013, Bad Tests, Good Tests
PDF
How to fake_properly
PPTX
The secret unit testing tools no one ever told you about
DOCX
201913046 wahyu septiansyah network programing
PDF
Server1
PPTX
Code generation for alternative languages
PPT
2012 JDays Bad Tests Good Tests
PDF
Introduction to web programming for java and c# programmers by @drpicox
PDF
CDI: How do I ?
PDF
Internet Technology (Practical Questions Paper) [CBSGS - 75:25 Pattern] {Mast...
PDF
Magic methods
PDF
Functional Testing for React Native Apps
PDF
Basic Tutorial of React for Programmers
PPTX
JavaScript Proven Practises
PPTX
PHP 5 Magic Methods
PDF
Advanced java practical semester 6_computer science
PDF
GMock framework
PDF
Rechecking SharpDevelop: Any New Bugs?
PDF
JUnit Kung Fu: Getting More Out of Your Unit Tests
Android programming -_pushing_the_limits
33rd Degree 2013, Bad Tests, Good Tests
How to fake_properly
The secret unit testing tools no one ever told you about
201913046 wahyu septiansyah network programing
Server1
Code generation for alternative languages
2012 JDays Bad Tests Good Tests
Introduction to web programming for java and c# programmers by @drpicox
CDI: How do I ?
Internet Technology (Practical Questions Paper) [CBSGS - 75:25 Pattern] {Mast...
Magic methods
Functional Testing for React Native Apps
Basic Tutorial of React for Programmers
JavaScript Proven Practises
PHP 5 Magic Methods
Advanced java practical semester 6_computer science
GMock framework
Rechecking SharpDevelop: Any New Bugs?
JUnit Kung Fu: Getting More Out of Your Unit Tests
Ad

Similar to Secret unit testing tools no one ever told you about (20)

ODP
Grails unit testing
PPTX
Secret unit testing tools
PPT
Security Testing
PDF
Android TDD
PPTX
Tdd & unit test
PDF
Android testing
PDF
Breaking Dependencies to Allow Unit Testing
PDF
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
PPTX
Testing with VS2010 - A Bugs Life
PDF
Testing in android
PPTX
Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...
PPTX
Breaking Dependencies to Allow Unit Testing
PPTX
Testing ASP.NET - Progressive.NET
PDF
Unit Testing
PPT
Unit testing
PPT
Testing And Drupal
PDF
EPHPC Webinar Slides: Unit Testing by Arthur Purnama
PPTX
Code to DI For - Dependency Injection for Modern Applications
PDF
Тестирование и Django
PDF
UI-Testing - Selenium? Rich-Clients? Containers? @APEX connect 2018
Grails unit testing
Secret unit testing tools
Security Testing
Android TDD
Tdd & unit test
Android testing
Breaking Dependencies to Allow Unit Testing
Breaking Dependencies To Allow Unit Testing - Steve Smith | FalafelCON 2014
Testing with VS2010 - A Bugs Life
Testing in android
Solit 2013, Автоматизация тестирования сложных систем: mixed mode automated t...
Breaking Dependencies to Allow Unit Testing
Testing ASP.NET - Progressive.NET
Unit Testing
Unit testing
Testing And Drupal
EPHPC Webinar Slides: Unit Testing by Arthur Purnama
Code to DI For - Dependency Injection for Modern Applications
Тестирование и Django
UI-Testing - Selenium? Rich-Clients? Containers? @APEX connect 2018
Ad

More from Dror Helper (20)

PPTX
Unit testing patterns for concurrent code
PPTX
Debugging with visual studio beyond 'F5'
PPTX
From clever code to better code
PPTX
From clever code to better code
PPTX
A software developer guide to working with aws
PPTX
The role of the architect in agile
PDF
Harnessing the power of aws using dot net core
PPTX
Developing multi-platform microservices using .NET core
PPTX
Harnessing the power of aws using dot net
PPTX
C++ Unit testing - the good, the bad & the ugly
PPTX
Working with c++ legacy code
PPTX
Visual Studio tricks every dot net developer should know
PPTX
Electronics 101 for software developers
PPTX
Navigating the xDD Alphabet Soup
PPTX
Who’s afraid of WinDbg
PPTX
Designing with tests
PPTX
Building unit tests correctly with visual studio 2013
PPTX
Writing clean code in C# and .NET
PPTX
Using FakeIteasy
PPTX
Battle of The Mocking Frameworks
Unit testing patterns for concurrent code
Debugging with visual studio beyond 'F5'
From clever code to better code
From clever code to better code
A software developer guide to working with aws
The role of the architect in agile
Harnessing the power of aws using dot net core
Developing multi-platform microservices using .NET core
Harnessing the power of aws using dot net
C++ Unit testing - the good, the bad & the ugly
Working with c++ legacy code
Visual Studio tricks every dot net developer should know
Electronics 101 for software developers
Navigating the xDD Alphabet Soup
Who’s afraid of WinDbg
Designing with tests
Building unit tests correctly with visual studio 2013
Writing clean code in C# and .NET
Using FakeIteasy
Battle of The Mocking Frameworks

Recently uploaded (20)

PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
Design an Analysis of Algorithms I-SECS-1021-03
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
How Creative Agencies Leverage Project Management Software.pdf
PPTX
ai tools demonstartion for schools and inter college
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
top salesforce developer skills in 2025.pdf
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
Upgrade and Innovation Strategies for SAP ERP Customers
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
Design an Analysis of Algorithms I-SECS-1021-03
CHAPTER 2 - PM Management and IT Context
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
How to Choose the Right IT Partner for Your Business in Malaysia
How Creative Agencies Leverage Project Management Software.pdf
ai tools demonstartion for schools and inter college
Wondershare Filmora 15 Crack With Activation Key [2025
PTS Company Brochure 2025 (1).pdf.......
top salesforce developer skills in 2025.pdf
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Which alternative to Crystal Reports is best for small or large businesses.pdf
EN-Survey-Report-SAP-LeanIX-EA-Insights-2025.pdf
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...

Secret unit testing tools no one ever told you about

  • 2. The secret unit testing tools no one ever told you about Dror Helper (@dhelper) Demos: https://github.com/dhelper/SecretUnitTestingToolsDemos
  • 3. About.ME Consultant @CodeValue Developing software (professionally) since 2002 Mocking code since 2008 Clean coder & Test Driven Developer Pluralsight Author Blogger: http://blog.drorhelper.com
  • 4. Why should I care about tools?
  • 6. Well known unit testing tools
  • 7. Server Dev Machine Source ControlBuild Server Test Runner Code Coverage Build Agent Unit Testing Framework Isolation Framework ?
  • 8. xUnit test framework public class BeforeAndAfter { [SetUp] public void Initialize() { } [TearDown] public void Cleanup() { } [Test] public void test1() { } [Test] public void test2() { } } Test Suite Fixture Test Case Test Case Test Case Test Case Test Case Fixture Test Case Test Case Fixture Test Case Test Case Test Case
  • 9. Mocking Frameworks Unit test Code under test DependencyFake object(s)
  • 10. What Mocking framework do? 1. Create Fake objects 2. Set behavior on fake objects 3. Verify method was called And more...
  • 11. [Test] public void Calculate_ReturnTwoValidNumbers_ServerCalled() { IDataAccess fakeDataAccess = A.Fake<IDataAccess>(); A.CallTo(() => fakeDataAccess.GetData(A<string>.Ignored)) .Returns(new Tuple<int, int>(2, 3)); var fakeCalculatorService = A.Fake<ICalculatorService>(); var cut = new DistrobutedCalculator(fakeDataAccess, fakeCalculatorService); cut.Calculate(); A.CallTo(() => fakeCalculatorService.Add(2,3)).MustHaveHappened(); }
  • 12. Definition: unit tests Automated piece of code that invokes a unit of work in the system and then checks a single assumption about the behavior of that unit of work [Roy Osherove, The Art Of Unit Testing]
  • 14. No guidance  Fragile tests  Stop unit testing
  • 15. What about AAA? [Test] public async void GetUserFromUrl() { var clientFake = A.Fake<IJsonClient>(); A.CallTo(() => clientFake.HttpGetUncompressedAsync(A<string>.Ignored)) .Returns(Task.FromResult(JsonResult)); var userRepository = new UserRepository(clientFake); var user = await userRepository.GetUser(11361); var expected = new User { Id=11361, DisplayName = "Dror Helper", ImageUrl=DefaultAvatar, Reputation=13904 }; Assert.That(user, Is.EqualTo(expected)); }  Act  Assert Arrange
  • 17. Arrange issues [TestMethod] public async Task LoadUser_ReputationStaysTheSame_ReputationTrendSame() { var user1 = new User {ImageUrl = "http://dummy.jpg", Reputation = 10}; var user2 = new User {ImageUrl = "http://dummy.jpg", Reputation = 10}; var fakeUserRepository = new FakeUserRepository(new[] { user1, user2 }); var viewModel = await InvokeAsync(() => new UserDetailsViewModel(fakeUserRepository)); await viewModel.LoadUser(); await viewModel.LoadUser(); var result = await InvokeAsync(() => ((SolidColorBrush)viewModel.ReputationTrend).Color); Assert.AreEqual(Colors.White, result); }
  • 18. Solution: Setup/TearDown [TestInitialize] public async Task InitilizeUserViewModel() { var user1 = new User { ImageUrl = "http://dummy.jpg", Reputation = 10 }; var user2 = new User { ImageUrl = "http://dummy.jpg", Reputation = 10 }; var fakeUserRepository = new FakeUserRepository(new[] { user1, user2 }); _viewModel = await InvokeAsync(() => new UserDetailsViewModel(fakeUserRepository)); } [TestMethod] public async Task LoadUser_ReputationStaysTheSame_ReputationTrendSame() { await _viewModel.LoadUser(); await _viewModel.LoadUser(); var result = InvokeAsync(() => ((SolidColorBrush)_viewModel.ReputationTrend).Color); Assert.AreEqual(Colors.White, result); }
  • 19. You shouldn’t use Setup in unit tests private UserDetailsViewModel _viewModel; [TestInitialize] public async Task InitilizeUserViewModel() { var user1 = new User { ImageUrl = "http://dummy.jpg", Reputation = 10 }; var user2 = new User { ImageUrl = "http://dummy.jpg", Reputation = 10 }; var fakeUserRepository = new FakeUserRepository(new[] { user1, user2 }); _viewModel = await InvokeAsync(() => new UserDetailsViewModel(fakeUserRepository)); } [TestMethod] public async Task LoadUser_ReputationStaysTheSame_ReputationTrendSame() { await _viewModel.LoadUser(); await _viewModel.LoadUser(); var result = InvokeAsync(() => ((SolidColorBrush)_viewModel.ReputationTrend).Color); Assert.AreEqual(Colors.White, result); }
  • 20. Solution: Extract to methods [TestMethod] public async Task LoadUser_ReputationStaysTheSame_ReputationTrendSame() { var user1 = CreateUser(reputation: 10); var user2 = CreateUser(reputation: 10); var fakeUserRepository = new FakeUserRepository(new[] { user1, user2 }); var viewModel = await InvokeAsync(() => new UserDetailsViewModel(fakeUserRepository)); await viewModel.LoadUser(); await viewModel.LoadUser(); var result = await InvokeAsync(() => ((SolidColorBrush)viewModel.ReputationTrend).Color); Assert.AreEqual(Colors.White, result); }
  • 21. Over extraction [TestMethod] public async Task LoadUser_ReputationStaysTheSame() { var viewModel = InitializeSystem(10, 10); await viewModel.LoadUser(); await viewModel.LoadUser(); CheckColor(Colors.White, viewModel); }
  • 22. The problem with factories private User CreateUser(int reputation) { return new User { ImageUrl = "http://dummy.jpg", Reputation = reputation }; } private User CreateUser(int reputation, string imageUrl) { return new User { ImageUrl = imageUrl, Reputation = reputation }; }
  • 23. Builder pattern class UserBuilder { private int _id, _reputation; private string _name, _imageUrl; public UserBuilder() { _id = 1; _name = "dummy"; _imageUrl = "http://dummy.jpg"; } User Build() { return new User { Id = _id, Name = _name, ImageUrl = _imageUrl, Reputation = _reputation }; } }
  • 24. Builder pattern (cont.) class UserBuilder { ... public UserBuilder WithName(string displayName) { _displayName = displayName; return this; } public UserBuilder WithReputation(int reputation) { _reputation = reputation; return this; } ... } var user1 = new UserBuilder() .WithReputation(10) .Build();
  • 25. Tool: AutoMocking Containers Test Container SUT http://blog.ploeh.dk/2013/03/11/auto-mocking-container/ New() Configure CreateCreate SUT Act
  • 26. Automocking with AutoFixture [Fact] public void YellIfTouchHotIron() { var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization()); Fake<IMouth> fakeMouth = fixture.Freeze<Fake<IMouth>>(); Fake<IHand> fakeHand = fixture.Freeze<Fake<IHand>>(); A.CallTo(() => fakeHand.FakedObject.TouchIron(A<Iron>._)).Throws<BurnException>(); var brain = fixture.Create<Brain>(); brain.TouchIron(new Iron {IsHot = true}); A.CallTo(() => fakeMouth.FakedObject.Yell()).MustHaveHappened(); } https://helpercode.com/2013/12/22/on-object-creation-and-unit-tests/
  • 27. Problem: Complex inputs • Big objects • Deep objects • Need for precision • Lack of knowledge
  • 28. Solution: use trivial inputs Sometimes missing the point Not always enough for “real test”
  • 29. Solution: Serialization Supported in most programming languages (XML, json). Need development  testing delayed Production code change  indefinitely delayed
  • 32. Can you spot the problem? [TestMethod] public void PerformSomeActionReturns42() { var myClass = ... bool initOk = myClass.Initialize(); var result = myClass.PerformSomeAction(); Assert.IsTrue(initOk); Assert.AreEqual(42, result); } http://stackoverflow.com/q/26400537/11361
  • 33. Can you spot the problem? [TestMethod] public void TestPasswordComplexity() { var result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "1!").Result; //Changes the password. Assert.IsFalse(result.Succeeded); result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "123456789").Result; //Changes the password. Assert.IsFalse(result.Succeeded); result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "123456789!").Result; //Changes the password. Assert.IsFalse(result.Succeeded); result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "abcdefghijk").Result; //Changes the password. Assert.IsFalse(result.Succeeded); result = _UserManager.ChangePasswordAsync(_TestUser.Id, "Password123!", "abcdefghijK1!").Result; //Changes the password. Assert.IsTrue(result.Succeeded); } http://stackoverflow.com/q/26400537/11361
  • 34. How many Assert(s) per test? One Assert Per Test! Two Assert == Two Tests  Usually ??? ”(…the Code is more) what you'd call guidelines than actual rules”
  • 35. Sometimes multiple asserts make sense [TestMethod] public void CompareTwoAsserts() { var actual = GetNextMessage(); Assert.AreEqual(1, actual.Id); Assert.AreEqual("str-1", actual.Content); }
  • 36. public class AssertAll { public static void Execute(params Action[] assertionsToRun) { var errorMessages = new List<exception>(); foreach (var action in assertionsToRun) { try { action.Invoke(); } catch (Exception exc) { errorMessages.Add(exc); } } if(errorMessages.Any()) { var separator = string.Format("{0}{0}", Environment.NewLine); string errorMessage = string.Join(separator, errorMessages); Assert.Fail(string.Format("The following conditions failed:{0}{1}", Environment.NewLine, errorMessage)); } } } http://blog.drorhelper.com/2011/02/multiple-asserts-done-right.html
  • 37. Using AssertAll [TestMethod] public void CompareTwoAsserts() { var actual = CreateMessage(); AssertAll.Execute( () => Assert.AreEqual(1, actual.Id), () => Assert.AreEqual("str-1", actual.Content); }
  • 38. Some frameworks are catching up! https://github.com/nunit/docs/wiki/Multiple-Asserts
  • 39. Choosing the right Assert? IsTrue vs. AreEqual Parameter ordering confusion StringAssert/CollectionAssert It’s all about proper error messages
  • 40. Tool: 3rd party assertion libraries Better error messages Readability Multiple asserts* ×Additional dependency ×Limited UT framework support ×System.Object “SPAMED” by extension methods
  • 41. Shouldly [Fact] public void AddTest() { var calculator = new Calculator(); var result = calculator.Add(2, 3); Assert.Equal(6, result); } [Fact] public void AddTest_Shouldly() { var calculator = new Calculator(); var result = calculator.Add(2, 3); result.ShouldBe(6); } https://github.com/shouldly/shouldly Shouldly.ShouldAssertException result should be 6 but was 5 Xunit.Sdk.EqualException Assert.Equal() Failure Expected: 6 Actual: 5
  • 42. FluentAssertions [Fact] public void CompareTwoObjects() { var customer1 = new Customer("cust-1", "John Doe"); var customer2 = new Customer("cust-2", "John Doe"); customer1.ShouldBeEquivalentTo(customer2, o => o.Excluding(customer => customer.Id)); } http://www.fluentassertions.com/
  • 43. AssertHelper [Test] public void CheckCompare() { var myClass = new MyClass(); Expect.That(() => myClass.ReturnFive() == 10); } [Test] public void CheckTrue() { var myClass = new MyClass(); Expect.That(() => myClass.ReturnFalse() == true); } [Test] public void StringStartsWith() { var s1 = "1234567890"; Expect.That(() => s1.StartsWith("456")); } [Test] public void CollectionContains() { var c1 = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; Expect.That(() => c1.Contains(41)); } https://github.com/dhelper/AssertHelper
  • 45. Test structure issues What to call the test? AAA is not mandatory What should I test? How to avoid unreadable, complicated tests? - Unit testing framework provide no structure
  • 47. Specifications == focused test Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two numbers Scenario: Add two numbers Given I have entered 50 into the calculator And I have entered 70 into the calculator When I press add Then the result should be 120 on the screen
  • 49. Tool: BDDfy [TestClass] public class CardHasBeenDisabled { private Card _card; private Atm _subject; void GivenTheCardIsDisabled() { _card = new Card(false, 100); _subject = new Atm(100); } void WhenTheAccountHolderRequestsMoney() { _subject.RequestMoney(_card, 20); } void ThenTheAtmShouldRetainTheCard() { Assert.IsTrue(_subject.CardIsRetained); } void AndTheAtmShouldSayTheCardHasBeenRetained() { Assert.AreEqual(DisplayMessage.CardIsRetained, _subject.Message); } [TestMethod] public void Execute() { this.BDDfy(); } }
  • 51. Everybody needs a CI server Unit tests without a CI server are a waste of time - if you're running all of the tests all of the time locally you're a better man then I am
  • 52. Tool: Continuous testing DotCover Typemock Runner nCrunch VS2017 (Enterprise)
  • 53. The right tools will help you write good tests Arrange Builder Pattern AutoMocking Containers Export Assert Shouldly FluentAssertions AssertHelper Test Structure BDDfy Continuous Testing Typemock Runner DotCover nCrunch Live Unit Testing
  • 54. Dror Helper | @ dhelper | http://helpercode.com Thank you! Demos: https://github.com/dhelper/SecretUnitTestingToolsDemos

Editor's Notes

  • #36: - Two asserts – no idea what caused the failure - Test is testing several things
  • #37: Explain about Using Object.Equals & Object.ToString Demo?
  • #41: - Two asserts – no idea what caused the failure Test is testing several things Left or right? InstanceOf MSTest vs. NUnit
  • #48: Organized Overhead
  • #49: Organized Overhead
  • #50: The only problem: overhead