problem with overriding & testing

I have a class which initialize itself (constructor) with data in a file. I want to test it, with various datasets. So I put the code that pick the data from file in a method, and I override that method in my tests:

publicclass ClassToTest{

private String value;

public ClassToTest(){

this.value = initValue();

}

public String initValue(){

return"data from a file";

}

publicvoid execute(){

System.out.println("execute with ["+value+"]");

}

publicstatic ClassToTest makeSoftMock(final String mockValue){

returnnew ClassToTest(){

public String initValue(){

return mockValue;

}};

}

publicstatic ClassToTest makeHardMock(){

returnnew ClassToTest(){

public String initValue(){

return"hard mocked data";

}};

}

publicstaticvoid main(String[] args){

new ClassToTest().execute();

makeHardMock().execute();

makeSoftMock("soft mocked data").execute();

}

}

the execution gives:

execute with [data from file]

execute with [hard mocked data]

execute with [null]

the "makeSoftMock()" don't work. Why ? When I print the result of a

makeSoftMock("soft mocked data").initValue()

, it works ! what's the difference ?

thanks

[2936 byte] By [tusca] at [2007-10-2 7:16:36]
# 1
Why aren't you using JUnit?%
duffymoa at 2007-7-16 20:51:26 > top of Java-index,Other Topics,Patterns & OO Design...
# 2

With JUNIT:

C:\>more CommandTest.java

import junit.framework.TestCase;

class Command {

private String data;

public Command() {

this.data = initData();

}

public String initData() {

return "data from file";

}

public String execute() {

return "execute with ["+data+"]";

}

}

public class CommandTest extends TestCase {

// mocks for Command.initData()

public Command makeMockDataWithParam(final String param) {

return new Command() {

public String initData() {

return param;

}};

}

public static Command makeHardCodedMockData() {

return new Command() {

public String initData() {

return "hard coded mock data";

}};

}

// tests for Command.initData()

public void testInitData() {

assertEquals("data from file",new Command().initData());

}

public void testInitData_hardCodedMockData() {

assertEquals("hard coded mock data",

makeHardCodedMockData().initData());

}

public void testInitData_mockDataAsParam() {

final String param = "mock data as param";

assertEquals(param,makeMockDataWithParam(param).initData());

}

// tests for Command.execute()

public void testExecute() {

assertEquals("execute with [data from file]",

new Command().execute());

}

public void testExcute_hardCodedMockData() {

assertEquals("execute with [hard coded mock data]",

makeHardCodedMockData().execute());

}

public void testExecute_mockDataAsParam() {

final String param = "mock data as param";

assertEquals("execute with ["+param+"]",

makeMockDataWithParam(param).execute());

}

}

C:\>javac -classpath junit.jar CommandTest.java

C:\>java -cp .;junit.jar junit.textui.TestRunner CommandTest

......F

Time: 0

There was 1 failure:

1) testExecute_mockDataAsParam(CommandTest)junit.framework.ComparisonFailure: expected:<...mock data as param...> but was:<...null...>

at CommandTest.testExecute_mockDataAsParam(CommandTest.java:53)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

FAILURES!!!

Tests run: 6, Failures: 1, Errors: 0

I don't understand why my mocked initData() returns null in test testExecute_mockDataAsParam() ? It works well with testInitData_mockDataAsParam().

tusca at 2007-7-16 20:51:26 > top of Java-index,Other Topics,Patterns & OO Design...
# 3

> > execute with [data from file]

> execute with [hard mocked data]

> execute with [null]

>

I ran the code with the folloing results:

execute with [data from a file]

execute with [hard mocked data]

execute with [soft mocked data]

Ken_Sa at 2007-7-16 20:51:26 > top of Java-index,Other Topics,Patterns & OO Design...
# 4

Ken, I have this:

C:\>java -version

java version "1.4.2_08"

Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_08-b03)

Java HotSpot(TM) Client VM (build 1.4.2_08-b03, mixed mode)

C:\>java -cp . ClassToTest

execute with [data from a file]

execute with [hard mocked data]

execute with [null]

What is your version of Java ?

tusca at 2007-7-16 20:51:26 > top of Java-index,Other Topics,Patterns & OO Design...
# 5

Sorry, that's not what I meant. That's not what I would call mocking and testing.

I guess I don't understand what you're trying to do. I'm betting you don't, either.

Leave the code out of it for now. What are you trying to accomplish?

I don't see EasyMock. Are you trying to write your own mocking framework? Somebody else did it first and better. Go use it.

%

duffymoa at 2007-7-16 20:51:26 > top of Java-index,Other Topics,Patterns & OO Design...
# 6

Seems like a LOT of work that's unnecessary.

I'd write a constructor for that object that took a String, pass whatever value I got from the file to it, and test away. You seem to be making a simple thing unnecessarily difficult, IMO.

If this is a toy that illustrates a more complex case I might reconsider if I understood better. But your case is simple. Why are you making it so hard?

%

duffymoa at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 7

> Seems like a LOT of work that's unnecessary.

>

> I'd write a constructor for that object that took a

> String, pass whatever value I got from the file to

> it, and test away. You seem to be making a simple

> thing unnecessarily difficult, IMO.

>

> If this is a toy that illustrates a more complex case

> I might reconsider if I understood better. But your

> case is simple. Why are you making it so hard?

>

> %

ok, the "real" class is a PropertyHolder, the method "getPropsFromBundle()" just load the properties from a file in an internal map. It is called in the constructor.

class PropertyHolder {

private Map map;

PropertyHolder(String bundleName) {

this.map = getPropsFromBundle(bundleName);

}

public Map getPropsFromBundle(String bundleName) {

// get the props from the bundle and return them as a map

return map;

}

// various getters

public boolean getBoolean(String prop) {

return map.get(prop);

}

// ...

}

the propertyHolder has various getter methods, such as getBoolean, getInt, getWhatever... that must be tested. To test these, my goal was to override the getPropsFromBundle() method so that it returns The Map That I Want (with my test data). My first attempt was to hardcode the map in the method:

PropertyHolder test = new PropertyHolder(null) {

public Map getPropsFromBundle(String bundleName) {

HashMap map = new HashMap();

map.put("foo","true");

return map;

}

};

assertTrue(test.getBoolean("foo"));

It's works well, but I have a lot tests to do, so I want to make an utility method that take the data to test as params, and build the overriding class with these params:

public PropertyHolder makePropertyHolder(final String prop,final String value) {

return new PropertyHolder(null) {

public Map getPropsFromBundle(String bundleName) {

HashMap map = new HashMap();

map.put(prop,value);

return map;

}

};

}

assertTrue(makePropertyHolder("foo","true").getBoolean("foo"));

It doesn't work at all: when I debug, it seems that Java does a "map.put(null,null)" instead of "map.put('foo','true'). To be sure, I executed the following test:

assertTrue(makePropertyHolder("foo","bar").getPropsFromBundle(null).get("foo"));

and this one worked. In fact, I'd like to understand what's going on, because I do a lot of anonymous class instanciations in my code (not only in tests), with params passed as finals, and I want to be sure that this code won't break.

tusca at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 8

intresting!

j2sdk1.4.2_03:

execute with [data from a file]

execute with [hard mocked data]

execute with [null]

jdk1.5.0_04:

execute with [data from a file]

execute with [hard mocked data]

execute with [soft mocked data]

same code. Did java 5 alter the way that variables are used by anonymous inner classes?

Ken_Sa at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 9

> intresting!

>

> j2sdk1.4.2_03:

>

> execute with [data from a file]

> execute with [hard mocked data]

> execute with [null]

>

> jdk1.5.0_04:

>

> execute with [data from a file]

> execute with [hard mocked data]

> execute with [soft mocked data]

>

> same code. Did java 5 alter the way that variables

> are used by anonymous inner classes?

Thank you Ken. I was downloading JDK 1.5 but you were faster :).

My problem is, what are the exact conditions that makes Java 1.4

nullifing my data ? Is it the execution of the overrided method in constructor ? Is it the final param ? a combination of the 2 ? As I said, I make a big use of anonymous instanciation with final params in my code, and I don't want it to break.

tusca at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 10

Hello,

Your problem is that method initValue() is called from ClassToTest constructor.

The final variable mockValue is copied under the hood into an attribute named something like mockValue$0 of the anonymous subclass, in the anonymous subclass' constructor. Unitl this constructor executes, the value of mockValue$0 is null.

So here is the chaining of execution when you instantiate the anonymous subclass,:

- the superclass constructor executes

- it calls initValue (overridden version)...

- ...wich return the value of mockValue$0, which is null.

- Then the anonymous subclass constructor executes, and mockValue$0 receives the intended value, but it's too late :-)

I happened to have the exact same problem once, alos in the context of using a Fake subclass returning mocked content in an overridden method.

The lesson is: beware of virtual calls in constructors!

Some say it's bad design - it's arguable, but at the very least it is fragile, as you can see... (well in my case it WAS bad design :o)

jdupreza at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 11

<snip>

> The lesson is: beware of virtual calls in

> constructors!

That's it.

> Some say it's bad design - it's arguable, but at the

> very least it is fragile, as you can see...

> (well in my case it WAS bad design :o)

Thank you for the explanation... This kinda magic around finals/overriding/anonymous mecanism has always been a mistery to me.

This story keeps me thinking constructors/initializers in Java are Bad Places, better to put the initialization code in an init() method or make lazy init. I'm just bored with coding IllegaStateExceptions and others synchronized blocks all over the place.

tusca at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 12

> Thank you for the explanation... This kinda magic

> around finals/overriding/anonymous mecanism has

> always been a mistery to me.

> This story keeps me thinking

> constructors/initializers in Java are Bad Places,

> better to put the initialization code in an

> init() method or make lazy init. I'm just

> bored with coding IllegaStateExceptions and

> others synchronized blocks all over the place.

So long as you don't make the mistake of calling overridable methods from a constructor you should be fine. The supertype's constructor will be called before the subtype's constructor and if the overriden method is dependent upon initialization in the subtype's constructor you run into problems. I was not personally aware that the initialization of an anonymous class's members occurred in the constructor of the subtype but apparently it does. Seeing as it works in 1.5 maybe it was a bug in 1.4? Either that or it's just dumb luck that it works in 1.5.

kablaira at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 13

> So long as you don't make the mistake of calling

> overridable methods from a constructor you should be

> fine.

Yes. I add this one in my personal rules bag: "thou must only use private methods in constructors".

> The supertype's constructor will be called

> before the subtype's constructor and if the overriden

> method is dependent upon initialization in the

> subtype's constructor you run into problems. I was

> not personally aware that the initialization of an

> anonymous class's members occurred in the constructor

> of the subtype but apparently it does.

As I said, I just feel pain that I can't clearly override the init process of an object (good for tests) in the constructor (good for encapsulation). The "Good Way" to overriding the init process seems to be making lazy initialization (or with an explicit init() method). It is, IMO, a pain as it introduce verbose code such as state checking of the objet, synchronization matters...

Or is there another solution ?

> Seeing as it

> works in 1.5 maybe it was a bug in 1.4? Either that

> or it's just dumb luck that it works in 1.5.

I'd be curious to know the exact explaination of this change in the specs.

tusca at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 14

>I was

> not personally aware that the initialization of an

> anonymous class's members occurred in the constructor

> of the subtype but apparently it does.

This is not specific to anonymous classes. That's why calling overridable methods from a constructor is indeed warned against, regardless of whether it is used with inner or anonymous classes.

What is specific to anonymous classes is the fact that the final variable passed to it is copied into an attribute of the class.

> Seeing as it

> works in 1.5 maybe it was a bug in 1.4? Either that

> or it's just dumb luck that it works in 1.5.

Yes, that's the point I forgot in my earlier reply : I noticed this problem with JDK1.3 and JDK1.4. I had not envisioned that it would be any different in JDK1.5. I haven't had the chance to work with this one yet, but I don't remember reading anything about 1.5 changing the way variables are passed to inner/anonyous classes.

Anyone has info about this? Admittedly, I don't know the 3rd edition of the JLS thoroughly, by now it has become quite a demanding reading :-(

jdupreza at 2007-7-16 20:51:27 > top of Java-index,Other Topics,Patterns & OO Design...
# 15

> As I said, I just feel pain that I can't clearly

> override the init process of an object (good for

> tests) in the constructor (good for encapsulation).

> The "Good Way" to overriding the init process seems

> to be making lazy initialization (or with an explicit

> init() method). It is, IMO, a pain as it

> introduce verbose code such as state checking of the

> objet, synchronization matters...

> Or is there another solution.

That depends on your definition overridding the initialization process. In most situations that I've encountered and indeed this one as well it is a matter of changes to the initial values of members. If this can't be done through mutator methods after the supertype's constructor has executed then there should probably be a constructor with a parameter to set it. If you allow for the presumption that a class should prohibit inheritance unless it is designed and documented for it than this situation should not be a problem.

This does not say anything about the possibility of needing to change the supertype's initialization in some way that isn't appropriate for either of those two situations. I can't think of an example and my inclination is to think it would be an artifact of a flaw in the design or requirements.

kablaira at 2007-7-20 19:14:28 > top of Java-index,Other Topics,Patterns & OO Design...
# 16

> This is not specific to anonymous classes. That's why

> calling overridable methods from a constructor is

> indeed warned against, regardless of whether it is

> used with inner or anonymous classes.

> What is specific to anonymous classes is the fact

> that the final variable passed to it is copied into

> an attribute of the class.

I should have said I was not aware that anonymous classes had their own copy of a variable and that the copy was not created until after the supertype's constructor executed. I can see why it would, though it's not immediately obvious and seems counter-intuitive.

kablaira at 2007-7-20 19:14:28 > top of Java-index,Other Topics,Patterns & OO Design...
# 17

I used javap to look at the bytecode. Compiled in 1.4:

final class Test$1 extends AbstractClass{

Test$1(java.lang.Object);

Code:

0:aload_0

1:invokespecial#15; //Method AbstractClass."<init>":()V

4:aload_0

5:aload_1

6:putfield#17; //Field val$o:Ljava/lang/Object;

9:return

protected void initValue();

Code:

0:aload_0

1:aload_0

2:getfield#17; //Field val$o:Ljava/lang/Object;

5:putfield#25; //Field value:Ljava/lang/Object;

8:return

}

Compiled in 1.5:

final class Test$1 extends AbstractClass{

Test$1(java.lang.Object);

Code:

0:aload_0

1:aload_1

2:putfield#13; //Field val$o:Ljava/lang/Object;

5:aload_0

6:invokespecial#16; //Method AbstractClass."<init>":()V

9:return

protected void initValue();

Code:

0:aload_0

1:aload_0

2:getfield#13; //Field val$o:Ljava/lang/Object;

5:putfield#24; //Field value:Ljava/lang/Object;

8:return

}

The variable in question in this example is an Object. It is passed to the anonymous constructor in both cases as we expect, but in 1.5 it is assigned to val$o before initialization and in 1.4 it is assigned after initialization. This isn't unique to anonymous classes either, it's the same with inner classes and probably all nested classes that need to copy a variable.

I'm not sure why it suddenly changed either. It had never occurred to me that they were copied after initialization before to begin with. Probably because I've never written code that would cause it to become a problem.

kablaira at 2007-7-20 19:14:28 > top of Java-index,Other Topics,Patterns & OO Design...
# 18

Apparently this is a result of the change in Java's memory model in the third edition. I asked the question here http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=34&t=006895 and JSR-133 was mentioned. Looking at the third edition JLS you'll notice chapter 17 is quite a bit different than the second edition, notably 17.5 which now describes final field semantics. I suppose with the new memory model you could 'safely' do what you were doing.

kablaira at 2007-7-20 19:14:28 > top of Java-index,Other Topics,Patterns & OO Design...
# 19

im still learning java. i have a program that has a couple of errors. i think its to do wit contructor overrriding but i cant seem to fix it. can anyone help ? .... :

import java.awt.*;

import java.awt.event.*;

import java.io.*;

class Tester

{

String plate;

int hour;

int rate;

Tester (int h) {

hour=h;

rate=h/10;

}

void add() throws Exception

{

BufferedReader input=new BufferedReader (new InputStreamReader(System.in));

System.out.print("Please Enter Your Car Plate Number: ");

String plate=input.readLine();

System.out.print("Please Enter The Hours Parked: ");

hour=Integer.parseInt(input.readLine());

System.out.println("-");

System.out.println("Your Car Plate Number Is: " + plate);

System.out.println("Number Of Hours Parked: " + hour);

System.out.println("The Rate Per Hour Is: " + rate);

}

public static void main (String args[]) throws Exception

{

Tester prods;

prods=new Tester();

Tester s=new Tester();

s.add();

}

}

shak28a at 2007-7-20 19:14:28 > top of Java-index,Other Topics,Patterns & OO Design...
# 20

> im still learning java. i have a program that has a

> couple of errors. i think its to do wit contructor

> overrriding but i cant seem to fix it. can anyone

> help ? .... :

You will have better luck getting an answer if you start a new post instead of tagging on to one that's 6 months old, and also use the [code] tags when posting code. There's a code button when you post.

Finally, please post exactly what's wrong. You only say you have "a problem." How do you know you have a problem? Is there an error message? Does the error occur when running or compiling? If there is an error message, post the full exact message.

atmguya at 2007-7-20 19:14:28 > top of Java-index,Other Topics,Patterns & OO Design...