Since the early days of eXtreme Programming, tests have been touted as "executable documentation".
In practice, however, many tests fail to live up to that ideal and worse become a time sink when trying to decipher test failures.
Using examples and focusing specifically on unit or developer tests, this talk will examine the characteristics of readability in tests and offer advice on making your tests more readable.
14. it "adds items to top" do
stack = Stack.new
stack.add "thing"
assert_equal "thing", stack.top
stack = nil
end
#
#
#
#
Setup
Exercise
Verify
Teardown
15. it "adds items to top" do
stack = Stack.new
stack.add "thing"
assert_equal "thing", stack.top
end
# Setup
# Exercise
# Verify
16. it "adds items to top" do
stack = Stack.new
stack.add "thing"
assert_equal "thing", stack.top
end
# Arrange
# Act
# Assert
17. it "adds items to top" do
stack = Stack.new
stack.add "thing"
assert_equal "thing", stack.top
end
# Given
# When
# Then
18. before do
@stack = Stack.new
end
# Given
it "adds items to top" do
@stack.add "thing"
assert_equal "thing", @stack.top
end
# When
# Then
19. before do
@stack = Stack.new
end
# Given
it "is initially empty" do
assert_empty @stack # Then
end
20.
21. it "returns the total timesheet hours worked of the timesheet" do
timesheet = Timesheet.new
task_items = [stub(start_time: 1359291600, end_time: 1359295200),
stub(start_time: 1359295200, end_time: 1359298900)]
timesheet.stub(:task_items) { task_items }
timesheet.calculate_total_hours.should == 2.0
end
http://www.superpumpup.com/sub-ms-test
22. it "returns the total timesheet hours worked of the timesheet" do
timesheet = Timesheet.new
task_items = [stub(start_time: 1359291600, end_time: 1359295200),
stub(start_time: 1359295200, end_time: 1359298900)]
timesheet.stub(:task_items) { task_items }
timesheet.calculate_total_hours.should == 2.0
end
23. Extract Given
You have a long, confusing or poorly named test
setup.
Turn the fragment into a method or member variable
whose name describes the “given”.
26. def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
task_items = [stub(start_time: 1359291600, end_time: 1359295200),
stub(start_time: 1359295200, end_time: 1359298900)]
timesheet.stub(:task_items) { task_items }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
27. def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
task_items = [stub(start_time: 1359291600, end_time: 1359295200),
stub(start_time: 1359295200, end_time: 1359298900)]
timesheet.stub(:task_items) { task_items }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
28. Extract Collaborator
An object your test depends on is confusing or
poorly named.
Extract the collaborating object into a method or
member variable whose name describes its purpose.
29. def one_hour_task
stub(start_time: 1359291600, end_time: 1359295200)
end
def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
task_items = [one_hour_task, one_hour_task]
timesheet.stub(:task_items) { task_items }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
30. def one_hour_task
stub(start_time: 1359291600, end_time: 1359295200)
end
def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
task_items = [one_hour_task, one_hour_task]
timesheet.stub(:task_items) { task_items }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
31. Inline Temp
You have a temp that is assigned to once with a
simple expression, and the temp is getting in the way
of other refactorings.
Replace all references to that temp with the
expression.
http://www.refactoring.com/catalog/inlineTemp.html
32. def one_hour_task
stub(start_time: 1359291600, end_time: 1359295200)
end
def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
timesheet.stub(:task_items) { [one_hour_task, one_hour_task] }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
33. def one_hour_task
stub(start_time: 1359291600, end_time: 1359295200)
end
def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
timesheet.stub(:task_items) { [one_hour_task, one_hour_task] }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
34. Use meaningful data
Your test uses arbitrary literal values as test data.
Replace arbitrary values with values that have
meaning in the domain of the test.
35. ONE_HOUR_IN_SECONDS = (60*60*1)
def one_hour_task
stub(start_time: 0, end_time: ONE_HOUR_IN_SECONDS)
end
def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
timesheet.stub(:task_items) { [one_hour_task, one_hour_task] }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
36. ONE_HOUR_IN_SECONDS = (60*60*1)
def one_hour_task
stub(start_time: 0, end_time: ONE_HOUR_IN_SECONDS)
end
def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
timesheet.stub(:task_items) { [one_hour_task, one_hour_task] }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
37. Extract Test
Your test method is checking multiple behaviours, or
checking behaviour of a collaborating object.
Extract a new test method for each behaviour.
38. describe Task do
it "knows duration in hours" do
task = Task.new(start_time: 0, end_time: 3600)
task.duration.should == 1.0
end
end
39. def one_hour_task
stub(duration: 1)
end
def timesheet_with_two_one_hour_tasks
timesheet = Timesheet.new
timesheet.stub(:task_items) { [one_hour_task, one_hour_task] }
timesheet
end
it "returns the total timesheet hours worked of the timesheet" do
timesheet_with_two_one_hour_tasks.calculate_total_hours.should == 2.0
end
40. it "returns the total timesheet hours worked of the timesheet" do
one_hour_task = stub(duration: 1)
timesheet = Timesheet.new
timesheet.stub(:task_items) { [one_hour_task, one_hour_task] }
timesheet.calculate_total_hours.should == 2.0
end
41. it "returns the total timesheet hours worked of the timesheet" do
timesheet = Timesheet.new
task_items = [stub(start_time: 1359291600, end_time: 1359295200),
stub(start_time: 1359295200, end_time: 1359298900)]
timesheet.stub(:task_items) { task_items }
timesheet.calculate_total_hours.should == 2.0
end
http://www.superpumpup.com/sub-ms-test
42. it "returns the total timesheet hours worked of the timesheet" do
one_hour_task = stub(duration: 1)
timesheet = Timesheet.new
timesheet.stub(:task_items) { [one_hour_task, one_hour_task] }
timesheet.calculate_total_hours.should == 2.0
end
46. “
clarity trumps brevity
in test names
https://www.facebook.com/notes/kent-beck/shorts-not-always-sweet-the-case-for-long-test-names/
564493423583526
47. Rename Method
The name of a method does not reveal its purpose.
Change the name of the method.
http://www.refactoring.com/catalog/renameMethod.html
52. Replace Assertion
Your test method uses a basic assertion to check for
a specific condition.
Use a more specific assertion provided by your test
framework, or create a custom assert method.