Scenario 1 [Testing a User Interface]
Core Concept :
Component gui = createGui();
Approvals.approve(gui);
This creates a snap shot of the gui: gui.received.png
This passes if gui.received.png == gui.approved.png
If you like the result, simply rename gui.received.png -> gui.approved.png and the test will pass.
Could you explain that in a diagram?
yes.
Why is this awesome?
“a picture is worth a 1000 tests”. Ever find that your unit tests aren’t giving you enough security? Have you started making lots & lots of asserts? Ever find your tests test too many things and the maintenance required to change anything is actually reducing your agility?
Approval Testing gives you 100% lock down on your gui, yet changes simply require a click of a button.
Where are the approved images stored?
Approved images are stored in your testing folder and in your source control. This makes the test repeatable on different systems & you can actually browse a visual of how your gui’s have changed over time!
How does it work for a web app?
String html = createWebPage();
Approvals.approve(html);
Instead of an image, now a text file (page.received.html) is created.
Scenario 2 [Behavior Driven Development]
Core Concept :
Object myObject = createObject();
/*temp*/ Approvals.approve(myObject.toString());
doSomething1();
/*temp*/ Approvals.approve(myObject.toString());
doSomething2();
/*temp*/ Approvals.approve(myObject.toString());
doSomething3();
/*temp*/ Approvals.approve(myObject.toString());
doSomething4();
/*temp*/ Approvals.approve(myObject.toString());
doSomething5();
Approvals.approve(myObject.toString());
As the code is created, the approval will move down, so That the final code is
Object myObject = createObject();
doSomething1();
doSomething2();
doSomething3();
doSomething4();
doSomething5();
Approvals.approve(myObject.toString());
Why is this awesome?
Many times a unit test is scattered with spot testing to assure things are done correctly. This creates a barrier to readability, and can lead to unneeded code.
Approval tests reduce the need to spot check, making the test cleaner and simpler.
What if I don’t want to change my toString?
No problem, create a toOtherString(), or a ObjectWritter, or Inspect the object. Sometimes we even create a ObjectVisualizer when an image is a better way to show the state.
What if there’s a bug in my code?
The first thing to do is to try to walk through your code and get an idea of the state of the object, so you can see where it went wrong... if only you had a nice way of visualizing the state of your object.... oh wait! that’s the first thing you created! I guess it will be pretty easy to debug.
Scenario 3 [The lock down]
Core Concept :
Say I have a web page that displays a user profile. I want to refactor it, but I have no tests. However, I do have 300 users in my current database.
String allPages = getAllUserProfilePages();
Approvals.approve(allPages);
Why is this awesome?
2 lines of code to completely lock down a legacy process. That is the definition of awesome!
Won’t this test be kinda slow?
Yes. But after you are done safely refactoring, you can/should remove the tests.
Is this test data independent? Will it be repeatable tomorrow?
No. But after you are done safely refactoring, you can/should remove the tests.
Should I keep this test after the refactoring?
No!
Although it would be a little more complex to implement, it might be worth implementing a framework that allows you to take a state chart or SVG diagram as input for your approval test. You could then verify that your app is in the appropriate state at the appropriate time.
ReplyDelete