Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test fixtures or equivalent for test data with Smalltalk Seaside?

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?

like image 454
Eric Clack Avatar asked Apr 27 '13 20:04

Eric Clack


2 Answers

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 ]
like image 82
Norbert Hartl Avatar answered Jan 01 '23 14:01

Norbert Hartl


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

like image 22
Stephan Eggermont Avatar answered Jan 01 '23 13:01

Stephan Eggermont