38
T.D.Hellmann T.D.Hellmann SENG 301

T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

  • Upload
    others

  • View
    13

  • Download
    0

Embed Size (px)

Citation preview

Page 1: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

T.D.HellmannT.D.HellmannSENG 301

Page 2: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Overview - Definitely� Test-Driven Development (TDD)

� What is TDD?� The TDD Cycle� Features of Good Unit Tests� Features of Good Unit Tests� TDD vs the Traditional Scheme� TDD Example� Support for TDD

� Mocking as Support for TDD� Mock Objects in TDD� Mocking Example

Page 3: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Overview – If There’s Time� Refactoring

� Refactoring Techniques� Refactoring Example

Page 4: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

What is TDD?� An evolutionary approach to testing which uses test-

first design and refactoring as its core principles� Test-first design: write all the tests for a given feature

first, then code that featurefirst, then code that feature� This makes sure that you will know when you are finished!

� Refactoring: changing code without changing functionality� Some motivations for refactoring:

� To make use of abstraction� To break code into more logical pieces� To improve the names or locations of code

Page 5: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

The TDD Cycle - 11. Write some tests2. Run all tests and watch the new ones fail3. Write some code4. Run all tests and watch them all succeed4. Run all tests and watch them all succeed5. Refactor6. Repeat

Page 6: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

The TDD Cycle - 2� Add a test

� It will fail because the feature hasn’t been implemented!� Implies that the developer will need to understand the

requirements before writing any coderequirements before writing any code

� Run all tests and watch the new one fail� Makes sure that the new test isn’t erroneously passing� In a sense this is a test of the test

Page 7: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

The TDD Cycle - 3� Write some code

� The goal here is to make the code pass the new test� The code should only pass the new test – no extra code!

� Doesn’t matter if it’s not pretty at this point� Doesn’t matter if it’s not pretty at this point

� Run all tests and watch them all succeed� This makes sure that the new code meets the

requirements, as expressed in the new test� This also makes sure that the new code didn’t break

anything that was already working!

Page 8: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

The TDD Cycle - 4� Refactor

� The code can be cleaned up now that we know it works� Should also rerun tests after refactoring to make sure,

again, that nothing broke due to changesagain, that nothing broke due to changes

� Repeat� Each iteration increases the program’s functionality� Iterations can be of any size

� If the coding phase of an iteration takes a substantial amount of time, that likely means the feature needed to be more specific

Page 9: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Features of Good Unit Tests� Good unit tests will:

� Run quickly – short setups, runtimes, and breakdowns� Run independently – should be order-independent� Be easy to read an understand� Be easy to read an understand� Use real data where possible � Test a discrete feature

Page 10: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD vs the Traditional Scheme� Traditionally:

� A testing phase follows each coding phase� The team that writes the code is not the team that writes

the teststhe tests

� With TDD:� Testing is done early, so errors will be caught early� Code coverage will be high – theoretically 100%� Testing is done in a purposeful, methodical manner� Testing is done by those most familiar with the code!

Page 11: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD vs. the Traditional Scheme� The main difference is where in your project

uncertainty is going to be concentrated� In TDD, uncertainty is front-loaded, meaning that the

most chaotic part of the project is the beginning. This most chaotic part of the project is the beginning. This means that it may take longer for the project to start coming together, but towards the deadline things are calmer

� In contrast to this, traditionally testing is done close to the deadline, meaning it’s easy to miss deadlines because of errors cropping up at the last minute.

Page 12: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD Example - Requirements� Suppose we need to create a tree

� Trees are composed of TNodes� Each TNode has:

� The ability to set their data field or to return that data� The ability to set their data field or to return that data� The ability to set their left and right children or to return them

� For this example, let’s assume that we’re starting from scratch, so for the first iteration we’ll need to take care of the getData and setData features

Page 13: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD Example – Getting Started� To start, we should have a class called TNode with

nothing in it

Page 14: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD Example – Write Tests� Next, we should code tests that define success

Page 15: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD Example – Run Tests� Now we’ll run this test to make sure that it fails

Page 16: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD Example – Code� Now we can get down to coding the feature

Page 17: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD Example – Run Tests Again� Now that we have both our code and our tests, we can

run the tests again to make sure the code’s fine� This step may (of course) take a couple of tries

Page 18: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD Example – Refactoring� In this example, we don’t necessarily need to refactor

anything� However, imagine a project in which we’d have both

trees and lists, with TNodes and LNodes bothtrees and lists, with TNodes and LNodes both� In that case we might want to define a Node superclass

that defines a data field� This won’t change the functionality of the code at all, but

it will reduce duplication � Remember – run your tests after refactoring to make

sure everything still works!

Page 19: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

TDD Example – Repeat� Now two things are true:

� We have our first feature implemented, and� We have proof that it works via the tests

� Since there are still more features left in our program, � Since there are still more features left in our program, we’d start again at the first step with the next feature, giving TNodes the abililty to set and return their children

Page 20: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Support for TDD - Techniques� Developers will have to master a few additional skills:

� Refactoring needs to be done frequently due to the modular approach that TDD takes

� Developers need to be at least as good at testing as they � Developers need to be at least as good at testing as they are at programming

� Automated testing is essential for making sure that regression errors (from refactoring) get caught

� Again, due to TDD’s modular approach, emphasis will need to be placed on integration testing when different parts of the system start to come together

Page 21: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Support for TDD - Tools� There are hundreds of unit testing frameworks out

there – Wikipedia has a pretty comprehensive list� xUnit frameworks are a popular choice – for example:

� JUnit: www.junit.org – for Java� JUnit: www.junit.org – for Java� NUnit: www.nunit.org – for .NET languages

� Mock object frameworks have gained popularity recently, and include:� jMock: www.jmock.org – for mocking Java objects� NMock: www.nmock.org - for mocking .NET objects

Page 22: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Mock Objects in TDD - 1� Mock objects have the same interface as their real

(read: complicated) counterpart� This means they can be used interchangeably

� It’s useful to mock an object when: � It’s useful to mock an object when: � It has many (or difficult-to-reproduce) states� It is slow to create or use (e.g. an entire database)� It returns non-deterministic results� Would need to include functionality to enable testing

� This is especially an issue in user interface testing!

� It doesn’t exist yet!

Page 23: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Mock Objects in TDD - 2� Mock objects allow testing to be done independent of

underlying or collaborating classes� In other words, by using mock objects, you can insure

that you’re testing a system rather than its dependenciesthat you’re testing a system rather than its dependencies

� Mock objects can also speed up unit tests� However – when this is done, additional integration

testing will need to be done later on

Page 24: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Mocking Example - 1� Suppose we are coding a system that uses the

Subject/Observer software design pattern� We have our Subject class coded and tested except for

the notifyAll methodthe notifyAll method� We only have the Observer interface coded – we don’t

have a concrete observer coded because we want to make sure the Subject feature is working first

� How can we test Subject without a concrete Observer?

Page 25: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Mocking Example - 2� The Subject Class:

Page 26: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Mocking Example - 3� The Observer interface:

Page 27: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Mocking Example - 4� So how can we test notifyObservers?

Page 28: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Mocking Example - 5� We can test

this feature using jMock – even – even without having a concrete Observer defined!

Page 29: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Mocking Example - 6� By using a mock object, we’ve managed to test a class

that interacts with other classes before we even code those dependencies

Page 30: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques - 1� Refactoring usually falls into one of three categories:

� Refactoring to increase abstraction� Refactoring to make smaller, more logical pieces� Refactoring to improve navigability� Refactoring to improve navigability

� Check www.refactoring .com/catalog for a pretty comprehensive list of refactoring techniques

Page 31: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques - 2� Refactoring to increase abstraction could be something

along the lines of encapsulating a field or making use of inheritance

� Two examples will follow� Two examples will follow� In the first, we want to make sure that only one instance of a

given class will exist at a time. In this case, the solution will be to encapsulate the entire class.

� In the second, we realize that two classes share many common characteristics, and generalize those commonalities into a superclass.

Page 32: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques – 3

Page 33: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques - 4

Page 34: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques - 5

Page 35: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques - 6

Page 36: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques - 7� Refactoring to create smaller, more logical units of

code makes it easier to test, among other things.� One instance of this is when you realize that part of a

method is generally applicable. The solution is to pull method is generally applicable. The solution is to pull out that discrete subunit of code and make it into its own method.

� In the following example, we find that part of a method doesn’t exactly belong with the rest of that method and encapsulates a discrete function, so we extract it into its own method.

Page 37: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques - 8

Page 38: T.D.Hellmann SENG 301pages.cpsc.ucalgary.ca/~sillito/seng-301/lecture-notes/intro-to-tdd.pdf · TDD Example –Getting Started To start, we should have a class called TNode with nothing

Refactoring Techniques - 9� Refactoring to improve navigability turns out to be pretty

straightforward. Basically this category includes any highly superficial changes that make your code easier to read.

� This can include (but is definitely not limited to):� This can include (but is definitely not limited to):� Moving an attribute or function to a more related class� Moving a method to a superclass from a subclass or vice versa� Simply renaming a class so its name reflects its function!