Advertisement
Guest User

Untitled

a guest
Jan 23rd, 2017
109
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.67 KB | None | 0 0
  1. # TDD is great. It helps me focus on, think through and flesh out the problem before I try and solve it. Without tests, I only have a vague understanding of the problem domain.
  2. # Well written tests can also serve as documentation for your code - documentation that should never go out of date.
  3.  
  4. # 3 rules for unit testing
  5.  
  6. # - unit tests should test behaviour
  7. # the systems we make only exist because they bring some useful behaviour to the end user, whether that is a person, a computer, or another part of the system. therefore we should only be interested in the interface, not implementation. badly designed tests can lead to us exposing the internals of the system solely for the purpose of testing.
  8. # - unit tests should be descriptive
  9. # describe the public interface of your code and make it clear to the reader what behaviour is being verified. this allows future you, other developers and non-technical people to understand what the code does.
  10. # - system under test should be isolated
  11. # when testing a class, a controller, a router, whatever, the dependencies and state of SUT should be controlled and mocked. only test one behaviour at a time.
  12.  
  13. # 4 tips for unit testing
  14. # - the 4 phase testing pattern
  15.  
  16. # SETUP –> EXERCISE –> VERIFY –> TEARDOWN
  17.  
  18. # - SETUP - we create a specific state for the system
  19. # - EXERCISE - we perform the action
  20. # - VERIFY - we verify the behaviour of the action against our expectations
  21. # - TEARDOWN - we reset the state of the system
  22.  
  23. # identifying the four phases in comments makes the intent of the test much easier to see.
  24.  
  25. # EXAMPLE
  26. describe User, '#save' do
  27. it 'encrypts the password' do
  28. user = User.new(password: 'password') #setup
  29. user.save #exercise
  30. user.encrypted_password.should_not be_nil #verify
  31. end
  32. end
  33.  
  34. # Where the same setup phase is repeated in multiple tests, it can be tempting to extract it out to keep your code DRY. But I think in tests, it is better to be as clear as possible as to what is going on, so we don't have to sacrifice clarity or go look elsewhere to find the context of the test.
  35.  
  36. # Frameworks often have the teardown phase built in. Database cleaning tools are available with customisable strategies that will allow you to migrate and seed your test db between each test.
  37.  
  38. # - be structured as possible - structure your tests by method, and by setup context
  39.  
  40. describe ExampleClass '#method1' do
  41. context 'default' do
  42. it 'does one thing' do
  43. # exercise and verify the behaviour of #method1 in the default context
  44. end
  45. end
  46.  
  47. context 'when some condition applies' do
  48. it 'does one thing' do
  49. # set up the conditions for this context
  50. # exercise and verify the behaviour when some conditions apply
  51. end
  52. end
  53.  
  54. context 'when some other different condition applies' do
  55. it 'does one thing' do
  56. # set up the different conditions for this context
  57. # exercise and verify the behaviour when some different conditions apply
  58. end
  59. end
  60. end
  61.  
  62. describe ExampleClass '#method2' do
  63. # ...
  64. end
  65.  
  66. # etc...
  67.  
  68. # - be descriptive as possible, and make your descriptions as clear as possible
  69. # giving your tests clear descriptions is like giving your variables, methods and classes good names. they should be as unambiguous as possible and provide as much clarity to the user as possible as to what they do. using the structure above will help.
  70.  
  71. describe Counter, '#increment' do
  72. context 'Default' do
  73. subject(:counter) { Counter.new }
  74. it 'increases the count by 1' do
  75. counter.increment
  76. expect(counter.count).to eq 1
  77. end
  78. end
  79.  
  80. context 'when the maximum count is reached' do
  81. let(:max_count) { 10 }
  82. subject(:counter) { Counter.new max_count }
  83. it 'does not increase the count' do
  84. max_count.times { counter.increment }
  85. counter.increment
  86. expect(counter.count).to eq max_count
  87. end
  88. end
  89. end
  90.  
  91. # "Counter #increment Default increases the count by 1"
  92. # "Counter #increment when the maximum count is reached does not increase the count"
  93.  
  94. # - Leave out unnecessary details
  95.  
  96. describe Person, '#grow_up' do
  97. context 'Default' do
  98. it 'increases the persons age by 1' do
  99. hobbies = [:painting, :music, :cycling]
  100. person = Person.new('Michael', 26, :green, hobbies)
  101. person.grow_up
  102. expect(person.age).to eq 27
  103. end
  104. end
  105. end
  106.  
  107. # Why do we need to know that the person is called Michael, likes the colour green, painting, music and cycling?
  108. # It obscures what we actually care about, which is the age
  109. # We can extract the Person making to a helper method that provides default values, and only pass in what care about - the age
  110.  
  111. describe Person, '#grow_up' do
  112. context 'Default' do
  113. it 'increases the persons age by 1' do
  114. create_person(age: 26)
  115. person.grow_up
  116. expect(person.age).to eq 27
  117. end
  118. end
  119.  
  120. def create_person arguments
  121. first_name = arguments[:first_name] || 'Michael'
  122. age = arguments[:age] || 21
  123. favourite_colour = arguments[:favourite_colour] || :green
  124. hobbies = arguments[:hobbies] || [:painting, :music, :cycling]
  125. Person.new first_name, age, favourite_colour, hobbies
  126. end
  127.  
  128. # Now the test is much more to-the-point
  129. # And by providing default values, if we change the implementation later and add in another initialization parameter to the Person class, we only have to change it in one place - the create_person helper method.
  130. # We are not taking the person initialization - the setup phase - away from the test, we are just changing how the setup is done, and making it clearer to the reader what is being tested
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement