Необъяснимое изменение значения поля - PullRequest
1 голос
/ 09 декабря 2010

Взгляните на эту странную проблему:

Debugging steps

  1. У меня есть точка останова при любом доступе к полю res1
  2. Res1 isприсвоено значение f, которое равно "bar"
  3. Res1 теперь равно "bar"
  4. На следующем перерыве res1 внезапно null

Почемуэто невозможно?

  • Из-за точки останова на res1 я вижу, что она вообще не должна была измениться
  • И не могла, потому что я явно неизмените его между присваиванием и assertEquals, и этот код выполняется в одном потоке JUnit.
  • Нет других полей или переменных с именем res1.

Что можетбыть на ногах?Я предполагаю, что это не ошибка в JVM, но кто знает.


Как сказал Джон Скит, проблема в том, что экземпляры разные.Это вызвано странной проблемой отражения:

public class ServiceObject {

    private static final Map<Class<?>, Map<String, Operation>> opsByClass =
        new ConcurrentHashMap<Class<?>, Map<String,Operation>>();

    private final Object target;
    private final Map<String, Operation> myClassOps;

    private class Operation {
        private final Method method;
        public Operation(Method met) {
            this.method = met;
            method.setAccessible(true);
        }
        public void execute(Map<String,?> args, Responder responder, Object context, boolean coerce) {
            ...
            method.invoke(target, mArgs);
        }
    }

    public ServiceObject(Object target) {
        Class<?> myClass = target.getClass();
        Map<String, Operation> op = opsByClass.get(myClass);
        if (op == null) {
            op = new HashMap<String, Operation>();
            for (Method meth : myClass.getMethods()) {
                ...
                op.put(opName, new Operation(meth));
            }
            opsByClass.put(myClass, op);
        }
        this.target = target;
        this.myClassOps = op;
    }

    public void execute(String opName) {
        Operation op = myClassOps.get(opName);
        ...
        op.execute(args, responder, context, coerce);
    }
}

// Foo is equivalent to the class that contains <res1> above
class Foo {

    @ServiceOperation
    public void bar() {
        // breakpoint here
    }

}

Что происходит при выполнении тестов:

a = new Foo();
b = new Foo();
svc = new ServiceObject(a);
svc.execute("bar", ...); // inside Foo.bar() <this> will be <a>
svc = new ServiceObject(b);
svc.execute("bar", ...); // inside Foo.bar() <this> will still be <a>, but should be <b>

Ответы [ 3 ]

4 голосов
/ 09 декабря 2010

На предположение: на этапе утверждения вы смотрите на экземпляр класса, отличный от того, который был установлен на "bar".

Трудно сказать, не видя остальную часть кода.

РЕДАКТИРОВАТЬ: Проблема в том, что ваш opsByClass кэш будет включать в себя операцию, с которой неявно связан ServiceObject. Поэтому, когда вы создаете первый ServiceObject с Foo, он будет кэшировать экземпляры Operation, связанные с этим первым экземпляром ServiceObject. Когда вы вызываете svc.execute для второго экземпляра ServiceObject, он найдет операцию на карте и найдет Operation, связанный с первым экземпляром ... что, вероятно, не что ты намеревался.

0 голосов
/ 09 декабря 2010

Я нашел это. Проблема в том, что, поскольку Operation не является static, оно относится к ServiceObject, которое было изначально создано, а не к тому, на котором выполняется второй вызов.

0 голосов
/ 09 декабря 2010

Полагаю, ваш код охватывает разные тесты.
Предлагаю выполнить настройку в методе onSetUp ()

...