Использование JMockit для возврата фактического экземпляра из макета конструктора - PullRequest
3 голосов
/ 05 декабря 2011

Я посмотрел на следующий вопрос, и он не совпадает с моим:

jMockit: как ожидать вызова конструктора для Mocked объектов?

Этот вопрос похож, но ответ мне не поможет:

Как смоделировать конструктор по умолчанию класса Date с помощью JMockit?

То, что я пытаюсь сделать, это смоделировать вызов конструктора для java.util.zip.ZipFile, в частности, для аргумента java.io.File. Я хотел бы, чтобы конструктор возвращал экземпляр другого ZipFile, экземпляр которого я создам с помощью конструктора, который принимает только аргумент String.

Этот вызов конструктора происходит внутри тестируемого метода, поэтому я не могу вставить ZipFile, который я хочу в качестве параметра.

Например, код выглядит примерно так:

public void whatever() {
   //some code
   //some more code
   foo();
   //yet more unrelated code
}

private Blah foo() {
    ZipFile zf;
    //a bunch of code we don't care about

    zf = new ZipFile(someFile);// I want to give it a known zipfile! mock this!


    // some more code we don't care about

    Enumeration<?> entries = zf.entries();
    ZipEntry entry = (ZipEntry) entries.nextElement();
    InputStream is = zf.getInputStream(entry)
    //maybe some other calls to the ZipFile

    // do something else
}

Моей первой мыслью было сделать следующее при статическом частичном насмешке:

final ZipFile test = new ZipFile("path/to/actual.zip");
new NonStrictExpectations() {
    @Mocked("(java.io.File)")
    ZipFile zf;
    {
        new ZipFile((File) any); result = test;
    }
};

Но это не будет работать, как указано в этой строке в учебнике: constructors have void return type, so it makes no sense to record return values for them

Моя вторая мысль была попытаться сделать следующее:

new NonStrictExpectations() {
    {
        newInstance("java.util.zip.ZipFile", new File("path/to/actual.zip"));
    }
};

Но при попытке инициализации файла выдается следующее:

java.util.zip.ZipException: error in opening zip file
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(Unknown Source)
at java.util.zip.ZipFile.<init>(Unknown Source)

Моя третья мысль заключалась в использовании @MockClass, как показано ниже:

@Before
public void setUp() throws Exception {
    Mockit.setUpMocks(MockedZipFile.class);
}
@After
public void tearDown() {
    Mockit.tearDownMocks();
}

@MockClass(realClass=ZipFile.class)
public static class MockedZipFile {
    public ZipFile it;
    @Mock
    public void $init(File f) throws ZipException, IOException {
        it = new ZipFile("path/to/actual.zip");//this is what would be called
    }
}

Но это шланги, которые у меня есть, которые загружают файл конфигурации для другой части моего тестового класса. Не говоря уже о том, что мне нужны разные zip-файлы для разных тестов.

Полагаю, я мог бы высмеивать все, что мог бы ZipFile, но это быстро стало бы гигантской болью, так как его называли множеством мест, его выходной требовалось бы высмеивать, и т. Д., И т. Д. Рефакторинг, чтобы попытаться сделать это доступным, был бы неловким, поскольку код, который использует ZipFile, является внутренним по отношению к коду, и публичные методы на самом деле не заботятся об этом.

У меня есть ощущение, что у JMockit есть способ разрешить это (давая конкретный экземпляр объекта при вызове конструктора), но я не могу понять это. У кого-нибудь есть идеи?

РЕДАКТИРОВАТЬ: я попробовал метод, предложенный @Rogerio, но у меня новая ошибка. Вот мои настройки:

final ZipFile test = new ZipFile("path/to/actual.zip");
new NonStrictExpectations() {
    ZipFile zf;
    {
        zf.entries();
        result = test.entries();
        zf.getInputStream((ZipEntry) any);
        result = new Delegate() {
            InputStream getInputStream(ZipEntry entry) throws IOException {                 
                return test.getInputStream(entry);
            }
        };
    }
};

но я получаю следующую трассировку стека:

java.lang.InternalError
at path.to.test.ExtractDataTest$1.<init>(ExtractDataTest.java:61)
at path.to.test.ExtractDataTest.setUp(ExtractDataTest.java:61)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

, где строка 61 - это строка new NonStrictExpectations() {.

Я действительно хочу сказать "вместо того, чтобы издеваться над этим объектом, замените этот другой объект того же типа". Может быть, я выразил это плохо.

EDIT2: я решил, что я должен включить номера версий: Использование Eclipse 3.6.1 Java 1.6.0_26 JMockit 0.999.10

Ответы [ 2 ]

1 голос
/ 06 декабря 2011

JMockit может издеваться над классом ZipFile, но это мешает загрузке классов, поскольку подкласс JarFile используется JVM все время (всякий раз, когда он загружает класс из файла jar в classpath).В настоящее время не существует простого способа избежать этого вмешательства (есть план «исправить» это, но это займет время).

Однако этот конкретный тестовый пример не очень подходит для инструмента-насмешникатем не мение.Вместо этого я бы порекомендовал настроить тест так, чтобы он предоставлял фактический zip-файл с нужным содержимым в нужном месте.

(другое редактирование) Я только что применил изменение к JMockit (для выпуска 0.999.12)который позволяет пройти следующий тест, при условии, что в рабочем каталоге есть файл test.zip, и он содержит текстовый файл с первой строкой «test»:

@Test
public void mockZipFile() throws Exception
{
    final ZipFile testZip = new ZipFile("test.zip");

    new NonStrictExpectations() {
        @Capturing @Injectable ZipFile mock;

        {
            mock.entries(); result = testZip.entries();

            mock.getInputStream((ZipEntry) any);
            result = new Delegate() {
                InputStream delegate(ZipEntry e) throws IOException {
                    return testZip.getInputStream(e);
                }
            };
        }
    };

    ZipFile zf = new ZipFile("non-existing");
    ZipEntry firstEntry = zf.entries().nextElement();
    InputStream content = zf.getInputStream(firstEntry);
    String textContent = new BufferedReader(new InputStreamReader(content)).readLine();

    assertEquals("test", textContent);
}

Однако я все равно рекомендую не с использованием насмешливого API для подобных случаев.Вместо этого используйте настоящий файл.

0 голосов
/ 06 декабря 2011

Это, вероятно, вам не поможет, но если вы используете Mockito или EasyMock, вы можете добавить PowerMock, который позволяет вам высмеивать конструкцию новых объектов в тестируемом коде.

...