SlideShare a Scribd company logo
1 of 34
Download to read offline
Unit Testing Best Practices
Tomaš Maconko
What do we know about UT?
• What is it?
• Why do we need it?
• Who use it?
• Who knows how to use it?
Poor statistics
• What is the good average code-coverage?
• 70-80%
• What is the actual average code-coverage?
• 10%
Unit Tests
• Small chunk of code
• Automatized test
• Determines if specific module
is fit for use
• Run fast
http://martinfowler.com/bliki/images/unitTest
Purpose
• Finds problems early
• Facilitates change
• Documentation
• Simplifies integration
• Design
Common issues
• Overdesign
• Run slowly
• Hard to understand
• Tests the same thing repeatedly
#1: Issue
#1: Issue
• Huge projects
• Many references
• Hundreds of files
• Thousands of tests
• Hard to focus
• Takes time to build the whole solution
#1: Solution
• Group up the files by certain criteria
• Put each tests projects beside logic project
• Consider hard cohesion criteria
#1: Benefits
• Small tests projects
• Quick builds and tests runs
• Easier to understand
#2: Problem
Seems to be not so bad?
#2: Problem
• Non-common classes in common unit tests project
• Redundant dependencies through common unit tests project
• It references several projects, that also reference several
projects, that ... So after every change on referenced project we
have to build the whole chain.
• It takes time to build
#2: Solution
• Remove the code from common unit tests project that is not-
common and put it to each tests project that uses that code
• Consider some code duplication – you can always copy the
certain code to each project – you don’t always have to be DRY
• Remove common unit tests project to remove the additional
reference build chain
#2: Benefits
• Less projects to build = less time to build
• Utilities are in the same project with tests that use it
• Changes used for specific tests projects utility does not affect
other projects
#3: Issue
If the tests passed, then it would be ok. But what if it fails? Do you
understand what the test should do? Or you want to go to look at code..?
#3: Issue
Can you understand it?
#3: Issue
• Classes contain “Test” postfix when there are many tests in class
• Not always obvious what are the conditions and expected
results
• Soooo hard to analyze the problem if the test is reeaaally big
#3: Solution
• Write what you test or why you test
• Use naming conventions for test methods:
• [Class]_[Method]_[Conditions]_[ExpectedResults]
• [Api]_[Conditions]_[ExpectedResults]
• Use naming conventions for test classes:
• [Testable]Tests
• In some cases write comments
• Add simple comments “Arrange, Act, Assert” to outline the test
sections
#3: Benefits
• Clear conditions and results
• Better understanding of test
#4: Issue
SETUP
#4: Issue
#4: Issue
• Mocks add a lot of confusion to code
• Many lines of useless and repeated code
• You have to put effort to mock the data in a right way
• And…
#4: Issue
• WTF exceptions
Moq.MockException: Wrong external request.
Expected invocation on the mock once, but was 0 times: r => r.AddToLog(It.Is<DefaultLogData>(d =>
(((((((((((d.CorrelationId == String.Empty && d.Identifier == String.Empty) && d.OperationId == Guid.Empty) && d.Method
== ._method) && (Int32)d.ObjectType == 0) && d.StatusCode == "0") && d.StatusCodeDescription == String.Empty) &&
d.SubStatusCode == null) && d.SubStatusCodeDescription == null) && d.ExternalType ==
.ExternalRequest.GetType().FullName) && d.Milliseconds == 0) && .ContainsAllStrings(d.SerializedExternalObject, new[] {
.ExternalRequest.SampleData })) && d.Url == "http://dummyservice.payex.com/"))
Configured setups:
r => r.AddToLog(It.IsAny<DefaultLogData>()), Times.Exactly(4)
Performed invocations:
IDefaultLogRepository.AddToLog(Infrastructure.MPS.Common.Logging.TransportClasses.DefaultLogData)
IDefaultLogRepository.AddToLog(Infrastructure.MPS.Common.Logging.TransportClasses.DefaultLogData)
IDefaultLogRepository.AddToLog(Infrastructure.MPS.Common.Logging.TransportClasses.DefaultLogData)
IDefaultLogRepository.AddToLog(Infrastructure.MPS.Common.Logging.TransportClasses.DefaultLogData)
#4: Issue
• Do not combine Times.Never with argument matching – tests can pass the
verification by because of value mismatch
_logRepository.Verify(r => r.LogRequest(It.Is<FinancialRequestLogData>(
l => l.CurrentTransactionNumber == 0 &&
l.OriginalTransactionNumber == 0 &&
l.WorkflowExtension == string.Empty && l.OrderId == null &&
l.OrderRef == null && l.Operation == default(FinancialOperation) &&
string.IsNullOrWhiteSpace(l.SerializedExternal) &&
!string.IsNullOrEmpty(l.SerializedInternal) && l.Url == Url)),
Times.Never, "Wrong request.");
#4: Solution
• Do not use entity mocks – write you own overridden class with
predefined data
• You can always use builder pattern (object mother pattern) or
fixture to build needed class for testing, either using mocking
framework or overridden classes
• If you still want to use mocking frameworks, prefer the explicit
assert instead of complicated verification
#4: Solution
#4: Benefits
• Less redundant code
• Code is more clear
• Asserts are more readable
#5: Problem
Cool little test isn’t it? Lets look inside…
#5: Problem
#5: Solution
• Do not overcommit to reduce the code
• Keep code clean, but clear
• Remember, other people can have to debug or refactor you
code
#6: Problem
// In BusinessLogic:
var hasher = new MyHasher();
var hashBuilder = new StringBuilder();
hashBuilder.Append(clientId);
hashBuilder.Append(clientFirstName);
hashBuilder.Append(secretKey);
var hash = hasher.GetHash(hashBuilder.ToString());
// In UnitTests
var hasher = new SimilarMyHasher();
string hash = clientId + secretKey;
var hash = hasher.GetUtHash(hash);
• Duplicated logic creates tests instability
#6: Solution
• Do not test the logic that is tested in another test
• Reuse the logic for specific cases from real business logic
• Duplicate the stuff only if you want to check if method returns
the right data
• Last example could be useful when checking if business logic
hash calculation is correct
Conclusion
• Group tests depending on their cohesion – integration, API,
requirement, ...
• Create self-documenting tests
• Do not test several things in the same test
• If you feel your test will create many WTFs/min
– refactor it! – REFACTOR TILL YOU DROP
Keep #EnjoyIT
Contact me, if you have questions:
• t.maconko@ba.lt
• www.ba.lt

More Related Content

What's hot

Unit testing best practices
Unit testing best practicesUnit testing best practices
Unit testing best practices
nickokiss
 
Unit Testing Fundamentals
Unit Testing FundamentalsUnit Testing Fundamentals
Unit Testing Fundamentals
Richard Paul
 
Java Unit Test and Coverage Introduction
Java Unit Test and Coverage IntroductionJava Unit Test and Coverage Introduction
Java Unit Test and Coverage Introduction
Alex Su
 
Unit testing, UI testing and Test Driven Development in Visual Studio 2012
Unit testing, UI testing and Test Driven Development in Visual Studio 2012Unit testing, UI testing and Test Driven Development in Visual Studio 2012
Unit testing, UI testing and Test Driven Development in Visual Studio 2012
Jacinto Limjap
 

What's hot (20)

Unit testing - the hard parts
Unit testing - the hard partsUnit testing - the hard parts
Unit testing - the hard parts
 
Automated Unit Testing
Automated Unit TestingAutomated Unit Testing
Automated Unit Testing
 
New Features Of Test Unit 2.x
New Features Of Test Unit 2.xNew Features Of Test Unit 2.x
New Features Of Test Unit 2.x
 
Unit testing best practices
Unit testing best practicesUnit testing best practices
Unit testing best practices
 
Unit Testing Fundamentals
Unit Testing FundamentalsUnit Testing Fundamentals
Unit Testing Fundamentals
 
Unit testing with JUnit
Unit testing with JUnitUnit testing with JUnit
Unit testing with JUnit
 
Clean Unit Test Patterns
Clean Unit Test PatternsClean Unit Test Patterns
Clean Unit Test Patterns
 
Java Unit Test and Coverage Introduction
Java Unit Test and Coverage IntroductionJava Unit Test and Coverage Introduction
Java Unit Test and Coverage Introduction
 
Unit testing with Junit
Unit testing with JunitUnit testing with Junit
Unit testing with Junit
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 
Embrace Unit Testing
Embrace Unit TestingEmbrace Unit Testing
Embrace Unit Testing
 
Unit test
Unit testUnit test
Unit test
 
Unit Testing 101
Unit Testing 101Unit Testing 101
Unit Testing 101
 
An Introduction to Unit Testing
An Introduction to Unit TestingAn Introduction to Unit Testing
An Introduction to Unit Testing
 
Understanding Unit Testing
Understanding Unit TestingUnderstanding Unit Testing
Understanding Unit Testing
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 
Unit Tests And Automated Testing
Unit Tests And Automated TestingUnit Tests And Automated Testing
Unit Tests And Automated Testing
 
Test driven development in .Net - 2010 + Eclipse
Test driven development in .Net - 2010 + EclipseTest driven development in .Net - 2010 + Eclipse
Test driven development in .Net - 2010 + Eclipse
 
.Net Unit Testing with Visual Studio 2010
.Net Unit Testing with Visual Studio 2010.Net Unit Testing with Visual Studio 2010
.Net Unit Testing with Visual Studio 2010
 
Unit testing, UI testing and Test Driven Development in Visual Studio 2012
Unit testing, UI testing and Test Driven Development in Visual Studio 2012Unit testing, UI testing and Test Driven Development in Visual Studio 2012
Unit testing, UI testing and Test Driven Development in Visual Studio 2012
 

Similar to Unit Testing Best Practices

Test-Driven Development
Test-Driven DevelopmentTest-Driven Development
Test-Driven Development
Meilan Ou
 
An Introduction to unit testing
An Introduction to unit testingAn Introduction to unit testing
An Introduction to unit testing
Steven Casey
 

Similar to Unit Testing Best Practices (20)

Test-Driven Development
Test-Driven DevelopmentTest-Driven Development
Test-Driven Development
 
A Brief Introduction to Test-Driven Development
A Brief Introduction to Test-Driven DevelopmentA Brief Introduction to Test-Driven Development
A Brief Introduction to Test-Driven Development
 
Intro to TDD
Intro to TDDIntro to TDD
Intro to TDD
 
Get Testing with tSQLt - SQL In The City Workshop 2014
Get Testing with tSQLt - SQL In The City Workshop 2014Get Testing with tSQLt - SQL In The City Workshop 2014
Get Testing with tSQLt - SQL In The City Workshop 2014
 
An Introduction to unit testing
An Introduction to unit testingAn Introduction to unit testing
An Introduction to unit testing
 
An Introduction to Unit Testing
An Introduction to Unit TestingAn Introduction to Unit Testing
An Introduction to Unit Testing
 
Bigger Unit Test Are Better
Bigger Unit Test Are BetterBigger Unit Test Are Better
Bigger Unit Test Are Better
 
An Introduction To Software Development - Test Driven Development, Part 1
An Introduction To Software Development - Test Driven Development, Part 1An Introduction To Software Development - Test Driven Development, Part 1
An Introduction To Software Development - Test Driven Development, Part 1
 
Automated testing in javascript
Automated testing in javascriptAutomated testing in javascript
Automated testing in javascript
 
TDD - Christchurch APN May 2012
TDD - Christchurch APN May 2012TDD - Christchurch APN May 2012
TDD - Christchurch APN May 2012
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
TDD and Related Techniques for Non Developers (2012)
TDD and Related Techniques for Non Developers (2012)TDD and Related Techniques for Non Developers (2012)
TDD and Related Techniques for Non Developers (2012)
 
Start with passing tests (tdd for bugs) v0.5 (22 sep 2016)
Start with passing tests (tdd for bugs) v0.5 (22 sep 2016)Start with passing tests (tdd for bugs) v0.5 (22 sep 2016)
Start with passing tests (tdd for bugs) v0.5 (22 sep 2016)
 
Adopting tdd in the workplace
Adopting tdd in the workplaceAdopting tdd in the workplace
Adopting tdd in the workplace
 
Adopting tdd in the workplace
Adopting tdd in the workplaceAdopting tdd in the workplace
Adopting tdd in the workplace
 
Testing with laravel
Testing with laravelTesting with laravel
Testing with laravel
 
Ch11lect1 ud
Ch11lect1 udCh11lect1 ud
Ch11lect1 ud
 
Unit Testing talk
Unit Testing talkUnit Testing talk
Unit Testing talk
 
Clean code presentation
Clean code presentationClean code presentation
Clean code presentation
 
An Introduction To Software Development - Final Review
An Introduction To Software Development - Final ReviewAn Introduction To Software Development - Final Review
An Introduction To Software Development - Final Review
 

Unit Testing Best Practices

  • 1. Unit Testing Best Practices Tomaš Maconko
  • 2. What do we know about UT? • What is it? • Why do we need it? • Who use it? • Who knows how to use it?
  • 3. Poor statistics • What is the good average code-coverage? • 70-80% • What is the actual average code-coverage? • 10%
  • 4. Unit Tests • Small chunk of code • Automatized test • Determines if specific module is fit for use • Run fast http://martinfowler.com/bliki/images/unitTest
  • 5. Purpose • Finds problems early • Facilitates change • Documentation • Simplifies integration • Design
  • 6. Common issues • Overdesign • Run slowly • Hard to understand • Tests the same thing repeatedly
  • 8. #1: Issue • Huge projects • Many references • Hundreds of files • Thousands of tests • Hard to focus • Takes time to build the whole solution
  • 9. #1: Solution • Group up the files by certain criteria • Put each tests projects beside logic project • Consider hard cohesion criteria
  • 10. #1: Benefits • Small tests projects • Quick builds and tests runs • Easier to understand
  • 11. #2: Problem Seems to be not so bad?
  • 12. #2: Problem • Non-common classes in common unit tests project • Redundant dependencies through common unit tests project • It references several projects, that also reference several projects, that ... So after every change on referenced project we have to build the whole chain. • It takes time to build
  • 13. #2: Solution • Remove the code from common unit tests project that is not- common and put it to each tests project that uses that code • Consider some code duplication – you can always copy the certain code to each project – you don’t always have to be DRY • Remove common unit tests project to remove the additional reference build chain
  • 14. #2: Benefits • Less projects to build = less time to build • Utilities are in the same project with tests that use it • Changes used for specific tests projects utility does not affect other projects
  • 15. #3: Issue If the tests passed, then it would be ok. But what if it fails? Do you understand what the test should do? Or you want to go to look at code..?
  • 16. #3: Issue Can you understand it?
  • 17. #3: Issue • Classes contain “Test” postfix when there are many tests in class • Not always obvious what are the conditions and expected results • Soooo hard to analyze the problem if the test is reeaaally big
  • 18. #3: Solution • Write what you test or why you test • Use naming conventions for test methods: • [Class]_[Method]_[Conditions]_[ExpectedResults] • [Api]_[Conditions]_[ExpectedResults] • Use naming conventions for test classes: • [Testable]Tests • In some cases write comments • Add simple comments “Arrange, Act, Assert” to outline the test sections
  • 19. #3: Benefits • Clear conditions and results • Better understanding of test
  • 22. #4: Issue • Mocks add a lot of confusion to code • Many lines of useless and repeated code • You have to put effort to mock the data in a right way • And…
  • 23. #4: Issue • WTF exceptions Moq.MockException: Wrong external request. Expected invocation on the mock once, but was 0 times: r => r.AddToLog(It.Is<DefaultLogData>(d => (((((((((((d.CorrelationId == String.Empty && d.Identifier == String.Empty) && d.OperationId == Guid.Empty) && d.Method == ._method) && (Int32)d.ObjectType == 0) && d.StatusCode == "0") && d.StatusCodeDescription == String.Empty) && d.SubStatusCode == null) && d.SubStatusCodeDescription == null) && d.ExternalType == .ExternalRequest.GetType().FullName) && d.Milliseconds == 0) && .ContainsAllStrings(d.SerializedExternalObject, new[] { .ExternalRequest.SampleData })) && d.Url == "http://dummyservice.payex.com/")) Configured setups: r => r.AddToLog(It.IsAny<DefaultLogData>()), Times.Exactly(4) Performed invocations: IDefaultLogRepository.AddToLog(Infrastructure.MPS.Common.Logging.TransportClasses.DefaultLogData) IDefaultLogRepository.AddToLog(Infrastructure.MPS.Common.Logging.TransportClasses.DefaultLogData) IDefaultLogRepository.AddToLog(Infrastructure.MPS.Common.Logging.TransportClasses.DefaultLogData) IDefaultLogRepository.AddToLog(Infrastructure.MPS.Common.Logging.TransportClasses.DefaultLogData)
  • 24. #4: Issue • Do not combine Times.Never with argument matching – tests can pass the verification by because of value mismatch _logRepository.Verify(r => r.LogRequest(It.Is<FinancialRequestLogData>( l => l.CurrentTransactionNumber == 0 && l.OriginalTransactionNumber == 0 && l.WorkflowExtension == string.Empty && l.OrderId == null && l.OrderRef == null && l.Operation == default(FinancialOperation) && string.IsNullOrWhiteSpace(l.SerializedExternal) && !string.IsNullOrEmpty(l.SerializedInternal) && l.Url == Url)), Times.Never, "Wrong request.");
  • 25. #4: Solution • Do not use entity mocks – write you own overridden class with predefined data • You can always use builder pattern (object mother pattern) or fixture to build needed class for testing, either using mocking framework or overridden classes • If you still want to use mocking frameworks, prefer the explicit assert instead of complicated verification
  • 27. #4: Benefits • Less redundant code • Code is more clear • Asserts are more readable
  • 28. #5: Problem Cool little test isn’t it? Lets look inside…
  • 30. #5: Solution • Do not overcommit to reduce the code • Keep code clean, but clear • Remember, other people can have to debug or refactor you code
  • 31. #6: Problem // In BusinessLogic: var hasher = new MyHasher(); var hashBuilder = new StringBuilder(); hashBuilder.Append(clientId); hashBuilder.Append(clientFirstName); hashBuilder.Append(secretKey); var hash = hasher.GetHash(hashBuilder.ToString()); // In UnitTests var hasher = new SimilarMyHasher(); string hash = clientId + secretKey; var hash = hasher.GetUtHash(hash); • Duplicated logic creates tests instability
  • 32. #6: Solution • Do not test the logic that is tested in another test • Reuse the logic for specific cases from real business logic • Duplicate the stuff only if you want to check if method returns the right data • Last example could be useful when checking if business logic hash calculation is correct
  • 33. Conclusion • Group tests depending on their cohesion – integration, API, requirement, ... • Create self-documenting tests • Do not test several things in the same test • If you feel your test will create many WTFs/min – refactor it! – REFACTOR TILL YOU DROP
  • 34. Keep #EnjoyIT Contact me, if you have questions: • t.maconko@ba.lt • www.ba.lt