Upload
others
View
13
Download
0
Embed Size (px)
Citation preview
T.D.HellmannT.D.HellmannSENG 301
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
Overview – If There’s Time� Refactoring
� Refactoring Techniques� Refactoring Example
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
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
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
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!
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
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
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!
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.
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
TDD Example – Getting Started� To start, we should have a class called TNode with
nothing in it
TDD Example – Write Tests� Next, we should code tests that define success
TDD Example – Run Tests� Now we’ll run this test to make sure that it fails
TDD Example – Code� Now we can get down to coding the feature
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
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!
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
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
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
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!
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
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?
Mocking Example - 2� The Subject Class:
Mocking Example - 3� The Observer interface:
Mocking Example - 4� So how can we test notifyObservers?
Mocking Example - 5� We can test
this feature using jMock – even – even without having a concrete Observer defined!
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
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
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.
Refactoring Techniques – 3
Refactoring Techniques - 4
Refactoring Techniques - 5
Refactoring Techniques - 6
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.
Refactoring Techniques - 8
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!