I've been using Test Driven Development in a Seaside app I've been playing with, and all of my data is stored as objects in the image (as opposed to a database).
So when I run my tests I've had to be careful to store away the real data before it gets trashed with test data, like this:
ToDoTest>>setUp
savedTasks := Task tasklist.
Task deleteAllTasks.
savedProjects := ToDoProject projectlist.
ToDoProject deleteAllProjects.
savedPeople := Person peoplelist.
Person deleteAllPeople.
And:
ToDoTest>>tearDown
Task tasklist: savedTasks.
ToDoProject projectlist: savedProjects.
Person peoplelist: savedPeople
The problem comes when my tests fail, which of course they do, this pops up the debugger, and I can then fix away, but the tearDown doesn't always get called and so I can lose my real data.
I do save the data out to files, so it's not a huge problem, but it is not as smooth and automated as I'd like it to be.
Anyway I can improve this?
I'm not sure if there is a scenario that will fix the problem completely. The real problem is that the model is global. That is convenient and nice but it fails easily within such a scenario. So I would consider changing the model from something global to a more localized variant so you can create your model solely for testing purpose without interfering with production data.
To fix it within your current setup you need to add an ensure: block somewhere. An ensure block "ensures" you that something is executed regardless if everything went ok or an error happened. The problem is that you need to do it before and after a test.
In this case I would overwrite TestCase>>#runCase in your own test class with something like
runCase
[ self saveRealModel.
super runCase ]
ensure: [ self restoreRealModel ]
Ah, that's a nice test smell. Norbert is right in pointing out that your tested model should probably not be global. Most tests should be on the interaction between individual objects. In StoryBoard we have users
DEUser subclass: #SBUser
instanceVariableNames: 'email initials projects invitations'
classVariableNames: ''
poolDictionaries: ''
category: 'StoryBoard-Data'
with class instancevar users as an entrypoint. Projects are only reachable through the users.
users
^users ifNil: [ users := OrderedCollection with: (SBAdministrator new
userid: 'admin';
password: 'admin';
yourself)
]
and a way to clear them
resetUsers
" SBUser resetUsers "
users := nil
Often we can pass in dependencies on creation for domain objects
Iteration>on: aProject
^self new
project: aProject;
yourself
This allows a testcase to pass in itself or a separate (mock) object
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With