Testing

Testing is baked into everything I do. We write test first and have testers do checks on our applications.

What are tests

  • Make sure code does what it is meant to do

  • Passing tests don’t guarantee that the code works

  • Program testing can be used to show the presence of bugs, but never to show their absence!

Why write tests

  • Give us confidence in system

  • Give us confidence to change code without breaking it

  • the tests are the spec

  • Know when we are done

  • faster feedback

  • Prevent bugs, spot bugs early, reduce costs

  • Document behaviour (business flows), show examples of how system works

  • Enforce coding behaviour (architecture, styles)

  • Allows for refactoring, by allowing changes and know if they break the behaviour

  • If a bug or something unexpected happened, a test will prevent this from happenning again

  • No manually checking code yourself

  • A set of tests can be used in CI to check that system does what it is supposed to do, and that version can be used to be deployed

  • As a reminder to be sure you want to make that change

    • ie adding a new enum, and checking methods that use enum classes need to be added or not

  • If wanting to refactor a code, writing all possible tests cases to check all behaviour has been documented first then can refactor it

    • Legacy code

  • By having passing tests, our code becomes better designed

  • Auto generated code (ie from IDE) does not fail when the class (or module) is updated

    • ie equals is auto generated for a class, but is class is updated and dev forgot to update equals

  • Learning a new library

    • learning tests

  • protecting against bugs found in library use, if compiler does not do it first

Good unit tests

  • what is a unit?

    • For some it is a class or method

    • Others it is not touching out side tech (Infrastructure)

    • It can be multiple objects under being invoked from a single object (ie testing use cases without db etc)

  • Tell the reader what you’re testing, not that you’re testing

    • Dont need to have the "test" in name as it is in the annotation

  • tell the reader what behavior, property, capability, etc. is under test.

  • Identify the cases of behavior, and test only one case per test case.

  • Goals

    • sensitive - fail for any bug

      • high coverage

      • correct tests

    • specific - precise failures

      • expressive

      • isolate & robust

      • low overlap

    • fast

    • few to cover all the cases

      • small

      • dry

    • Can run them all in very quickly

  • Priorities

    • code that you fear, hard to understand

    • distant corner in the logic

    • a bug, before fixing it

    • for/if/while

    • exception thrown

    • method that delegates

    • trivial code ie getters, setters

    • legacy code, with 0 bugs, 0 changes

  • the purpose of your test

    • test that it works

    • and what it means to work

  • Readable test names

    • consider using underscores to improve readability of test names

    • @DisplayName("adfa") can be used in junit 5

  • the meaning of failure should be clear: it should mean the code doesn’t work.

    • Add why it failed message, if default is not good enough

  • unit test shouldn’t depend on things that can’t be controlled within the test.

    • ie Filesystem? Network? Database? Asynchronous ordering? edit config files?

      • Theses should be mocked or stubbed

    • unit under test shouldn’t depend on things that could cause failure when the code is correct

  • watch out for overfitting tests. You know the ones: brittle assertions on implementation details rather than required features. You update something—spelling, a magic value, a quality outcome—and tests fail.

  • Dont have underfitting tests too. They’re vague, passing at the drop of a hat, even with code that’s wildly and obviously wrong.

    • Using prod code to use as expected

    • never failing tests

  • Should be treated as prod code

    • Factor out the repetition. Use it to group tests into inner classes with @Nested.

Test harness and Test frameworks

  • Test harness can be explained as a unit testing tool that helps in the automation and execution of unit test cases.

    • It enables the automation of unit tests for any software application

  • Advantages of test harness

    • helps to automate the overall testing process to the maximum extent.

    • capable to execute test suites which are composed of multiple test cases.

    • capable to generate test report either by its own as in case of TestNG or through another third party tool or development as in the case of Junit.

    • composed of drivers and stubs which are small programs, therefore, they are capable to Support debugging of code.

    • tools can capture the test results for an entire suite as well as individual test present inside the suite or standalone test.

    • helps developers in the early identification of a defect at the code

    • enables developers to measure the code coverage at code or elementary level during application development.

    • helpful in terms of increasing the system productivity through the test automation

    • enhance the quality of software components or overall software application

    • help to cover complex test scenarios which are very difficult to handle through tradition testing approach.

    • adds confidence to the organization marketing team towards the robustness of the application software as it is well tested right at unit level.

  • Disdavantages of test harness

    • tools do not support record and play features.

    • Testers using test harness tools are expected to have knowledge of programming languages

    • it incurs a cost for writing automates test suites or test cases through skilled developers.

  • Where test harness is used

    • Automation testing:

      • developers develop test scripts

      • test scripts require test data along with setting up of certain test parameters which are necessary to execute the automation test scripts

      • After these test scripts are executed, the test results are captured and analyzed in the form of test reports.

    • Integration testing:

      • the two modules within the same application or different applications are integrated and testing is conducted to make sure both modules which are developed separately are working smoothly as a single unit when integrated.

      • test cases are written and executed as automation script or through tools such as Junit

      • results are recorded as a report and analyzed for exit criteria of the overall integration testing to be successful.

  • Example of test harness

    • Junit: It is an API written in JAVA language which is used to design and implement test cases to conduct the unit testing for JAVA program or JAVA based applications. They are very frequently used to test applications which are built in JAVA language.

  • Test Automation framewokr

    • a set of procedures, processes, intellectual concept and environment through which the tester can design and implement automated tests.

    • composed of information such as the test library, automated testing, testing tools, best practices, and testing platform

    • It allows tester can manually “Record & Playback” script in this framework

    • categorized into

      • Data-driven testing

      • Modularity driven testing

      • Hybrid testing

      • Code driven testing

      • module driven testing

      • behavior driven testing

      • Keyword driven testing.

    • Exmaples

      • Cucumber, Robot Framework, etc

      • yatspec

Methodologies

Test Driven Development (TDD)

  • starting with unit test to drive development

  • For unit tests

  • Test behaviour not methods

  • Process: Red - Green - Refactor

    • RED - Failing test

      • ie expected this got something else

    • GREEN - Pass test

      • Write production code to make test pass

    • REFACTOR - Tidy up code

      • i.e. better designed, better performant

  • No production code with tests

  • Have mutation test code coverage (ie Pi test) to check all production code is covered by tests

Acceptance Test Driven Development (ATDD)

  • Starting with acceptance test to drive code development

    • Red - Green - Refactor

  • Yatspec (java library)

    • Allows to have readable output for non technical people (ie html and in english)

    • Can Draw sequence diagrams ( use plantUML) show interactions between different apps

    • Table tests available

  • Syntax: Given - When - then

    • Given: setup dependecies ie databases/3rd party apis

      • Use of Wiremock to stub 3rd party apis

      • Use of test database instead of production or a stub

    • When: Run the application, and interact with it ie make a request to end point

    • Then: Assertions, assert on output of request, database changes, verify apis called

      • Use of Junit and matching libraries ie hamcrest/assertj

  • AKA Behaviour Driven Development (BDD)

Types of Tests

  • unit testing is important for writing code, functional and integration tests are important for shipping code”. Prevent new bugs created

White Box Testing

Types of test

  • Acceptance Tests - testing features of apps

    • Whole app run, testing visible outputs

    • Visible for testers and Business to check that code does what it wants in less technical language but in business language

    • Stubbing out other dependencies

  • Documentation tests - Having visible tests for business functionallity at unit level exposed *

  • Module test

  • unit tests

    • Avoid using static methods as cannot mock and might be expensive to use (ie long test times, use resources)

    • links

      • Sandi metz https://www.youtube.com/watch?v=URSWYvyc42M

  • Static Analysis Tests - testing the code it self not the Behaviour

    • findbugs

      • https://self-learning-java-tutorial.blogspot.co.uk/2018/03/a-short-tutorial-on-findbugs.html

      • http://findbugs.sourceforge.net/

    • code style (PMD)

    • code coverage

      • line (Intellj)

      • mutation (Pitest)

  • Consumer driven contract testing (PACT tests)

    • test apis between internal services used between apps

    • Useful for microservices

  • Learning Tests

    • http://tomhesl.in/learning-tests/

    • Use of tests to learn and document open source libraries

    • Use of test to find out if library should be used

  • Smoke/confidence Tests

  • Performance tests

Builds

  • Run all Tests

  • flickery tests

Making code more testable

  • need to extract out any dependecies, especially those that are being newed up in the class under test (so they can be mocked)

Black Box Testing

  • Component testing

    • Manual testing of application where other apps are stubbed (using stubbing application)

  • Use of postman application, to send requests to api

  • Use of selenium to test front end guide

  • Integration testing

    • Have all dependencies up and running and not using a stub

  • Release testing

  • Testing in different environments in the pipeline

  • Smoke Tests

    • Run actual workflow, and see if works (something that does not affect other services but does some interal business logic)

Stubs

  • used in component testing, but mocking out other dependencies through this

  • bottom up

    • have app up but stub mocks all dependencies

  • Top down

    • stub represents app and mocks responses from it instead

  • Storing state, ie as files

  • priming stub

  • using stub

  • Wiremock

mockito

  • Mock

  • Verify

  • When...ThenReturn...

  • Dummy

  • Argument captors

    • https://www.baeldung.com/mockito-argumentcaptor

Junit

  • asserting

    • boolean

    • exception thrown

    • expected == actual

    • collection matchers

  • Before

  • After

  • Ignore

  • Setups

    • in tests

    • outside of test

  • @Note

  • Fuzzy assertions

AssertJ

  • Readbility and intent via fluent api

  • Extracting

  • Soft Assertions

    • https://joel-costigliola.github.io/assertj/core/api/org/assertj/core/api/SoftAssertions.html

yatspec

  • Acceptance Tests

  • BDD language

  • Captured inputs and outputs

  • combining with Wiremock

  • linking note

    • link test to classes or other tests

  • Interesting givens

    • any important domain information set in the givens

      • givens state

  • test state

  • state extractors

  • given builders

  • action under test

  • Sequence diagrams

  • Dictionary

  • Linking notes

  • html output

  • parametised/table tests

Writing Tests

  • Arrange - Act - Assert - Teardown

    • Order in test method

    • Arrange can be put in a before block or start of test

    • Teardown can be put in an after block

    • Arrange include

      • Mocking other dependecies in class and/or return values if dependency's methods are called

        • Use Mockito

      • Starting up other parts of the app ie database or new up classes

    • Asserting on output of code

      • verifying methods of dependencies have been called

        • mockito

      • Asserting on output

        • Junit and AssertJ

  • https://mixandgo.com/learn/feature-tests-vs-integration-tests-vs-unit-tests-in-ruby-and-rails

  • https://testing.googleblog.com/2008/12/static-methods-are-death-to-testability.html

  • http://misko.hevery.com/code-reviewers-guide/

  • https://blog.sebastian-daschner.com/entries/thoughts-on-efficient-testing

  • Google testing blog: https://testing.googleblog.com/search/label/TotT

  • automation test (UI) https://ultimateqa.com/automation-patterns-antipatterns

  • https://www.slideshare.net/VictorRentea/the-tests-are-trying-to-tell-you-somethingvoxxedbucharestpptx

  • Wiremock

  • Yatspec

  • AssertJ

    • https://dzone.com/articles/fluent-assertions-with-assertj

    • http://joel-costigliola.github.io/assertj/index.html

  • Junit

  • Mockito

Last updated

Was this helpful?