Test Design & Testing Theory
Recap: Verification & Validation
- Verification - The system has been built right
- Validation - The right system has been built
- The nature of testing: your code tests your tests, and your tests test your code
- Writing code is the easy part
Why test-first?
- Start with the end
- Test to an interface, not to an implementation (Black Box Testing)
- The failing unit test:
- Karl Popper & philosophy of falsification
- Testing as a science
- Test-last justifies code written, whereas we want to falsify
Designing Tests
- Devise a test list
- What is being tested
- Given inputs
- Expected outputs
- Adopt a systematic approach to writing test lists
- Exhaust all possibilities at an abstract level
- Test all flows in the program - optimal, optional and exceptional
Designing Tests
- Normal design principles apply - DRY, KISS
- Abstract away common functionality
- Fixtures - @BeforeEach, @AfterEach, @BeforeAll, @AfterAll
- Helper Functions
- Helper Classes
- Avoid complexity in tests
- Be careful of loops, if statements
Designing Tests
- Using class-scoped variables as part of tests
- This is OK, but be careful
- Tests must be atomic - run in any order
What are we testing anyway?
- Behaviour of the system/class/function matches a specification
- Articulate the contents of a test in the test name
- How is behaviour produced?
- Return values
- Side effects
Designing Tests
- How can we meaningfully test objects returned?
- Getters and setters
- Intermediary communication objects (e.g. EntityInfoResponse)
- JSON
Types of Testing, First Glance
- Unit Testing - test the individual parts of the system in isolation
- Integration Testing - test interconnecting parts of the system working together
- System Testing - test the system working as a whole
Types of Testing
- Unit tests - white-box
- Impossible to write with Abstract Data Types
- E.g. message/send, channel/messages
- Integration tests - black-box
- Test dependencies - functions rely on other functions to work
Unit Testing Abstract Data Types
- Unit testing an ADT is impossible
- Make tests as 'unit-like' as possible
- Treat like a scientific experiment:
- Pick one thing to test (independent variable)
- Only make assertions around the behaviour of that function
- Reduce dependencies on other functions as much as possible (keep other variables controlled)
- Avoid sanity-checking other unrelated values
- Anything that is asserted in the test should be encapsulated in the test name
- Do this for each unit of functionality
- A failing test pinpoints the exact broken functionality
Integration Testing Abstract Data Types
- Weave a web of dependencies
- Test and sanity-check as much as possible
- Catch any bugs that may have been missed by unit tests when things come together
- Any bug caught by an integration test should be replicable in a corresponding unit tests
System Testing
- What do we mean by "testing the system as a whole?"
- Treat the system as one large black box
- Interact with the entire system via the interface
- E.g. BlackoutController methods, Project API
- System tests may be similarly structured to integration tests
Acceptance Testing & Debugging
- Checking that the system fulfils the requirements
- Occurs at the most abstract level
- Test code works on the frontend
- Bug finding
- Diagnose and reproduce the error on the frontend
- Write a failing unit test + add to test bank
- Write code to pass the test
- Check the bug is resolved
Assumptions
- Clear up an ambiguity in the specification / contract
- A good assumption articulates a behaviour rather than an implementation
- Assumptions are black-box
- An assumption is accompanied by a corresponding unit test
We can design tests, but how do we test design?
- Verification vs. Validation
- Verification tests design
- Validation tests requirements
- tbc
Randomised Testing
- TBA
Test Design & Testing Theory COMP2511
By npatrikeos
Test Design & Testing Theory COMP2511
- 363