Effective unit testing
Upcoming SlideShare
Loading in...5
×
 

Effective unit testing

on

  • 613 views

Some notes taken from the book "Effective Unit Testing: A Guide for Java Developers"

Some notes taken from the book "Effective Unit Testing: A Guide for Java Developers"

Statistics

Views

Total Views
613
Views on SlideShare
613
Embed Views
0

Actions

Likes
0
Downloads
8
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Effective unit testing Effective unit testing Presentation Transcript

  • Roberto Casadei 2013-05-30Roberto CasadeiNotes taken from:Effective Unit Testing: A guide for Java developersEffective Unit Testing
  • Effective Unit Testing 2TestingExpressing and validating assumptions and intended behaviorof the codeChecking what code does against what it should doTests help us catch mistakesTests help us shape our design to actual useTests help us avoid gold-plating by being explicit about whatthe required behavior isThe biggest value of writing a test lies not in the resulting testbut in what we learn from writing it
  • Effective Unit Testing 3The value of having testsFirst step: (automated) unit tests as a quality toolHelps to catch mistakesSafety net against regressionfailing the build process when a regression is foundSecond step: unit tests as a design toolInforms and guides the design of the code towards its actual purposeand useFrom design-code-test to test-code-refactor (i.e. TDD) a.k.a red-green-refactorThe quality of test code itself affects productivity
  • Effective Unit Testing 4Test-Driven Development (TDD)Direct results:Usable codeLean code, as production code only implements whats required by thescenario its used forSketching a scenario into executable code is a design activityA failing test gives you a clear goalTest code becomes a client for production code, expressing your needsin form of a concrete exampleBy writing only enough code to make the test pass, you keep yourdesign simple and fit-for-purpose
  • Effective Unit Testing 5Behaviour-Driven Development (BDD)Born as a correction of TDD vocabulary“Test” word as source for misunderstandingsNow, commonly integrated with business analysis andspecification activities at requirements levelAcceptance tests as examples that anyone can read
  • Effective Unit Testing 6Not just tests but good tests (1)ReadabilityMantainabilityTest-code organization and structureNot just structure but a useful structureGood mapping with your domain and your abstractionsWhat matters is whether the structure of your code helps you locate theimplementation of higher-level concepts quickly and reliablySo, pay attention to:Relevant test classes for task at handAppropriate test methods for those classesLifecycle of objects in those methods
  • Effective Unit Testing 7Not just tests but good tests (2)It should be clear what your tests are actually testingDo not blindly trust the names of the testsThe goal is not 100% coverage → testing the “rightthings”A test that has never failed is of little value – its probably nottesting anythingA test should have only one reason to failbecause we want to know why it failed
  • Effective Unit Testing 8Not just tests but good tests (3)Test isolation is importantBe extra careful when your tests depend on things such as:Time, randomness, concurrency, infrastructure, pre-existing data, persistence,networkingExamples of measures:Test doublesKeep test code and the resources it uses togetherMaking tests set up the context they needUse in-memory database for integration tests that require persistenceIn order to rely on your tests, they need to be repeatable
  • Effective Unit Testing 9Test Doubles
  • Effective Unit Testing 10Test doublesObjects to be substituted for the real implementation for testingpurposesReplacing the code around what you want to test to gain full control if itscontext/environmentEssential for good test automationAllowing isolation of code under testFrom code it interacts with, its collaborators, and dependencies in generalSpeeding-up test executionMaking random behavior deterministicSimulating particular conditions that would be difficult to createObserving state & interaction otherwise invisible
  • Effective Unit Testing 11Kinds of test doublesStubs: unusually short thingsFake objects: do it without side effectsTest spies: reveal information that otherwise would behiddenMocks: test spies configured to behave in a certain wayunder certain circumstances
  • Effective Unit Testing 12Stubs(noun) def. “a truncated or unusually short thing”A stub is simple impl that stands for a real imple.g. An object with methods that do nothing or return a defaultvalueBest suited for cutting off irrelevant collaborators
  • Effective Unit Testing 13Fake objectsReplicating the behavior of the real thing without the sideeffects and other consequences of using the real thingFast alternative for situations where the real thing isdifficult or cumbersome to use
  • Effective Unit Testing 14Test spiesBuilt to record what happens when using themE.g. they are useful when none of the objects passed inas arguments to certain operations can reveal throughtheir API what you want to know
  • Effective Unit Testing 15Mock objectsMocks are test spies that specify the expected interactiontogether with the behavior that results from themE.g. A mock for UserRepository interface might be told to …return null when findById() is invoked with param 123, andreturn a given User instance when called with 124
  • Effective Unit Testing 16Choosing the right test doubleAs usual, it dependsRule of thumb: stub queries; mock actions
  • Effective Unit Testing 17Structuring unit testsArrange-act-assertArrange your objects and collaboratorsMake them work (trigger an action)Make assertions on the outcomeBDD evolves it in given-when-thenGiven a contextWhen something happensThen we expect certain outcome
  • Effective Unit Testing 18Check behavior, not implementationA test should test..just one thing, and..test it well, while..communicating its intent clearlyWhats the desired behavior you want to verify?Whats just an implementation detail?
  • Effective Unit Testing 19Test Smells
  • Effective Unit Testing 20ReadabilityWhyAccidental complexity adds cognitive loadGoalReading test code shouldnt be hard workHowThe intent and purpose of test code should be explicit or easily deducibleConsiderLevel of abstractionSingle Responsibility Principle (also applies to tests)
  • Effective Unit Testing 21Readability smells (1)Primitive assertionsAssertions that use a level of abstraction that is too lowE.g. Testing structural details of resultsTwin of primitive obsession code smell (which refers to use of primitive types to represent higher-level concepts)Also the abstraction level of the testing API mattersGeneral advice: keep a single level of abstraction in test methodsHyperassertionsAssertions that are too broadmake it difficult to identify the intent and essence of the testmay fail if small details change, thus making it difficult to find out whyApproach: remove irrelevant details + divide-et-impera
  • Effective Unit Testing 22Readability smells (2)Incidental detailsThe test intent is mixed up with nonessential informationApproachExtracts nonessential information into private helpers and setup methodsGive things appropriate, descriptive namesStrive for a single level of abstractions in a test methodSetup sermonSimilar to Incidental details but focuses on the setup of a tests fixture (= the context in which agiven test executes), i.e. on the @Before and @BeforeClass (setup) methodsMagic numbersGenerally, literal values does not communicate their purpose wellApproach: replace literals using costants with informative names that make their purpose explicit
  • Effective Unit Testing 23Readability smells (3)Split personalityWhen a test embodies multiple tests in itselfA test should only check one thing and check it wellso that whats wrong could be easily locatedApproach: divide-et-imperaSplit logicTest code (logic or data) is scattered in multiple placesApproach:Inline the data/logic into the test that uses it
  • Effective Unit Testing 24MaintainabilityTest code requires quality (as production code)Maintainability of testsis related to test readabilityis related to structureLook fortest smells that add cognitive loadtest smells that make for a mantainance nightmaretest smells that cause erratic failures
  • Effective Unit Testing 25Mantainability smells (1)Duplicationneedless repetition of concepts or their representationsall “copies” need to be synchronizedExamples:Literal duplication → extract variablesStructural duplication (same logic operating with different data istances) → extract methodsSometimes, it may be better to leave some duplication in favor of better readabilityConditional logiccan be hard to understand and error-proneControl structures can be essential in test helpers but, in test methods, these structures tend to be a majordistractionThread.sleep()It slows down your test; so, you should use synchronization mechanisms such as count-down-latches or barries
  • Effective Unit Testing 26Mantainability smells (2)Flaky testsTests that fails intermittentlyDoes the behavior depend on time/concurrency/network/…?When you have a source for trouble, you can1) Avoid it 2) Control it 3) Isolate itUnportable file pathsPossibly, use relative paths (e.g. evaluated against the projects root dir)You could also put resources on Javas classpath and look them up viagetClass().getResource(filename).getFile()Persistent temp filesEven though you should try to avoid using physical files altogether if not essential, remember todelete temp files during teardown
  • Effective Unit Testing 27Maintainability smells (3)Pixel perfectionIt refers to tests that assert against (hardcoded) low-level details eventhough the test would semantically be at a higher-levelyou may require a fuzzy match instead of a perfect matchFrom Parametrized-Test pattern to Parametrized-MessSome frameworks might not allow youto trace a test failure back to the specific data set causing itto express data sets in a readable and concise wayLack of cohesion in test methods→ each test in a test-case should use the same text fixture
  • Effective Unit Testing 28TrustworthinessWe need to trust our tests so that we can feel confidentin evolving/modifying/refactoring codeLook for test code that deliver a false sense of securityMisleading you to think everything is fine when its not
  • Effective Unit Testing 29Trustworthiness smells (1)Commented-out testsTry to understand and validate their purpose, or delete themMisleading commentsMay deliver false assumptionsDo not comment what the test does, as the test code should show that clearly and promptlyInstead, comments explaining the rationale may be usefulNever-failing testsHave no valueE.g. forgetting fail() in a try{}catch{}Shallow promisesTests that do much less than what they say they do
  • Effective Unit Testing 30Trustworthiness smells (2)Lowered expectationsTests asserting for loose conditions (vague assertions, …) give a false sense of security→ raise the bar by making the assertions more specific/precisePlatform prejudiceA failure to treat all platforms equalMeasures: different tests for different platformsConditional testIts a test thats hiding a secret conditional within a test method, making the test logicdifferent from what its name would suggestPlatform prejudice is an example (the specific test depends on the platform)As a rule of thumb, all branches in a test method should have a chance to fail
  • Effective Unit Testing 31Some advanced stuff
  • Effective Unit Testing 32Testable designDesign decisions can foster or hinder testabilityPrinciples supporting testable designModularitySOLIDSingle responsability principle → a class should have only a single responsibilityOpen/closed principle → sw entities should be open for extension, but closed for modification– you can change what a class does without changing its source codeLiskov substitution principle → objects in a program should be replaceable with instances of theirsubtypes without altering the correctness of that programInteface segregation principle → many client-specific interfaces are better than one general-purposeinterfaceDependency inversion principle → as a way for depending on abstractions rather than on concretions– great for testability!
  • Effective Unit Testing 33Testability issuesTestability issuesrestricted accessprivate/protected methodsinability to observe the outcome (e.g. side effects), void methodsinability to substitute parts of an implementationinability to substitute a collaboratorinability to replace some functionality
  • Effective Unit Testing 34Guidelines for testable designAvoid complex private methodsAvoid final methodsAvoid static methods (if you foresee chance for stub)Use new with careit hardcodes the implementation class → use IoC if possibleAvoid logic in constructorsAvoid the Singleton patternFavor composition over inheritanceWrap external librariesAvoid service lookups (factory classes)as the collaborator is obtained internally to the method ( service = MyFactory.lookupService() ), it may bedifficult to replace the service
  • Effective Unit Testing 35JUnit 4
  • Effective Unit Testing 36JUnit4 basicsPackage: org.junitTest classes are POJO classesAnnotations@Test (org.junit.Test)@Before: marked methods are exec before each test method run@After: marked methods are exec after each test method runUsing assertions & matchersimport static org.junit.Assert.*;import static org.hamcrest.CoreMatchers.*;So that in your test methods you can write something asassertThat( true, is( not(false) ) ); 
  • Effective Unit Testing 37Parametrized-Test pattern in JUnitMark the test class with@RunWith(org.junit.runners.Parametrized.class)Define private fields and a constructor that accepts, in order, your parameterspublic MyParamTestCase(int k, String name) { this.k=k; … }Define a method that returns your all your parameter data@org.junit.runners.Parametrized.Parameterspublic static Collection<Object[]> data(){return Arrays.asList( new Object[][] { { 10, “roby”}, … } );}Define a @Test method that works against the private fields that are defined tocontain the parameters.