I'd be interested to hear how you approach testing EOs. I can understand the motivation to exclude the database so that the test run quickly and hence can be run frequently. However, I usually find that very few of the interesting methods of my EOs can be tested without a network of other EO objects.

Some examples:

MyEO has has a dozen relationships to other EOs, many of which are mandatory in the model. I want to test a method foo() that only interacts with two of those relationships. So all I'm doing:

public void testFoo() {
MyEO myEO = (MyEO)mockEditingContext().createSavedObject (_MyEO.ENTITY_NAME); myEO.addToAsRelationship(mockEditingContext().createSavedObject (_A.ENTITY_NAME)); myEO.addToAsRelationship(mockEditingContext().createSavedObject (_A.ENTITY_NAME));
  B b = (B)mockEditingContext().createSavedObject(_B.ENTITY_NAME);
  b.setSomethingNecessaryForFoo(...);
  myEO.setBRelationship(b);
  myEO.foo();
  assert...
}
I.e. I don't concern myself with setting up the whole object graph. All I'm doing is setting up the absolute minimum for testing foo().

Now let's say foo()'s implementation calls bar(), which traverses a huge portion of the object graph and then returns a number. Still, all I want to test is foo(). So what I usually do is taking control of bar() by creating an inner class inside the test case that inherits from MyEO and overwrites bar():

class TestMyEO extends MyEO {
  int bar;
  protected int bar() {
    return bar;
  }
}

public void testFoo() {
  TestMyEO myEO = new TestMyEO();
  mockEditingContext().insertSavedObject(myEO);
  myEO.bar = 0;
  ...
  // assert that foo does the right thing if bar returns 0

  myEO.bar = 30;
  // assert that foo does the right thing if bar returns 30
  ...
}

Now we still want to test bar() itself, and let's assume that the test would only be meaningful if lots of objects are involved, so then lets make it easy to setup those objects to keep the test method as short as possible:

void addNewCToMyEO(MyEO myEO, String name, int age) {
  C c = (C)mockEditingContext().createSavedObject(_C.ENTITY_NAME);
  c.setName(name);
  c.setAge(age);
  myEO.addToCsRelationship(c);
}

public void testBar() {
MyEO myEO = (MyEO)mockEditingContext().createSavedObject (_MyEO.ENTITY_NAME);
  addNewCToMyEO(myEO, "abc", 3);
  addNewCToMyEO(myEO, "def", 20);
  addNewCToMyEO(myEO, "ghi", 9);
  addNewCToMyEO(myEO, "abc", 1);
  addNewCToMyEO(myEO, "def", 2);
  addNewCToMyEO(myEO, "ghi", 5);
  assertEquals(40, myEO.bar());
}

I find that with techniques like these I can test all of the test- worthy stuff in my business logic. Sometimes it's getting more complicated and I need to subclass several EOs and create objects of many EOs to setup a unit test. This is often a symptom for badly designed code (I'm just speaking about my own). The more experienced I get at TDD, though, the simpler and easier my test cases get.


Some times it is reasonable to create these by hand, but often this can be hundreds or thousands of objects.

Do you really need all those objects to unit test individual methods? If none of my examples would help, maybe you could explain one of your complex scenarios and we'll take a crack at making it testable without a DB.


So, although it makes the tests slow, I find it easier to read them from a known state in the database. Do you have two sets of tests, one quick, programmatic only set and one functional, database using, slow set?

I have unit tests that test units of code as much isolated as possible (i.e. with the fewest number of other objects involved), and without accessing the database. And then I'm working with a QA team that creates acceptance tests against a controlled database and also does exploratory testing against a production-data like database.


Christian



Attachment: smime.p7s
Description: S/MIME cryptographic signature

 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list      ([email protected])
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to [email protected]

Reply via email to