- write acceptance test first (name it
...Feature
in a packagefeature
)- identify side effects - what are we testing (e.g. text printed to console)
- identify the trigger of the side effect (e.g. print statement on account)
- drive implementation from test
- use
UnsupportedOperationException
for all methods... that are not yet implemented, to see where things are failing- change template of creating a new method in the IDE to create a
throw
statement in the body of the method
- change template of creating a new method in the IDE to create a
- acceptance testing means:
- run trigger for side effect
- assure expected side effect has happened
- as acceptance test is failing for the right reason, it's time to "park" the acceptance test and go to the inner loop --> unit testing
- META split IDE editor: test left, implementation right
- Outside-In TDD is doing design while writing the tests
- use
void
command methods (no return value) - do not store things in objects that you can calculate on the fly
- only if calculation takes too much time
- META create a shortcut
test
in the IDE that generates a test method stub (template) - start your tests with
<CLAAS>Should
- do not use / implement methods just for the purpose of testing
- when mocking a class / classes, use
@RunWith(MockitoJUnitRunner.class)
- in acceptance test use real repository, in unit test mock it
- in unit test, mock external world
- with introducing a repository, a logic is pushed further down into the system (from
Account
intoTransactionRepository
)
- Question to ask when doing outside-in TDD:
- is the responsibility of class under test to do all of that
- or is it the responsibility of someone else
- keep all the methods of a class on the same level of abstraction
- every time a collaborator of the class under test in the acceptance test is created, a method body of this collaborator will throw a
UnsupportedOperationException
- this way we will be guided on what needs to be implemented next...
- therefore the Acc tests runs the real class, not the mocks
- as part of the outside-in process, this means that we start with the class under test and are then guided to the missing points of implementation in the collaborator classes
- each collaborator then gets a unit test as the next step
- within the unit test, we can think of what the collaborator class should do
- collaborators of the class under test in the unit tests are mocked
- how to mock things like a date
- e.g. the transaction is saved in the database with a given date, which normally will be created "in time"
- rule: you can't test what you can't controll (e.g.
System.date()
) - you need to controll this randomness in your tests
- so you need the
System.date()
to come from something that you can control / mock, e.g. aClock
class
- META* check Idea shortcut for "recent runs" --> select recent runs of unit tests
- when you need to test some functionality that you can't control (e.g. a clock / date / time), put the call to this functionality into a
protected
method and override the class / this method in your test