Как записать, что jUnit тестирует на класс, который использует сетевое соединение

Я хотел бы знать то, что является лучшим подходом для тестирования метода "pushEvent ()" в следующем классе с тестом jUnit. Моя проблема, что закрытый метод "callWebsite ()" всегда требует соединения с сетью. Как я могу избежать этого требования или осуществить рефакторинг мой класс, что я могу протестировать его без соединения с сетью?

class MyClass {

    public String pushEvent (Event event) {
        //do something here
        String url = constructURL (event); //construct the website url
        String response = callWebsite (url);

        return response;
    }

    private String callWebsite (String url) {
        try {
            URL requestURL = new URL (url);
            HttpURLConnection connection = null;
            connection = (HttpURLConnection) requestURL.openConnection ();


            String responseMessage = responseParser.getResponseMessage (connection);
            return responseMessage;
        } catch (MalformedURLException e) {
            e.printStackTrace ();
            return e.getMessage ();
        } catch (IOException e) {
            e.printStackTrace ();
            return e.getMessage ();
        }
    }

}
28
задан Raedwald 20 September 2013 в 19:23
поделиться

5 ответов

Возможное решение: Вы можете расширить этот класс, переопределить callWebsite (для этого его нужно сделать защищенным) - и в переопределенном методе написать реализацию метода-заглушки.

10
ответ дан 28 November 2019 в 03:13
поделиться

В качестве альтернативы полезному ответу Фингласа относительно насмешек рассмотрим заглушенный подход, при котором мы переопределяем функциональность callWebsite (). Это работает довольно хорошо в случае, когда нас не так интересует логика callWebsite, как логика другой логики, вызываемой в pushEvent (). Важно проверить, что callWebsite вызывается с правильным URL-адресом. Итак, первое изменение заключается в том, чтобы сигнатура метода callWebsite () стала:

protected String callWebsite(String url){...}

Теперь мы создаем заглушенный класс следующим образом:

class MyClassStub extends MyClass {
    private String callWebsiteUrl;
    public static final String RESPONSE = "Response from callWebsite()";

    protected String callWebsite(String url) {
        //don't actually call the website, just hold onto the url it was going to use
        callWebsiteUrl = url;
        return RESPONSE;
    }
    public String getCallWebsiteUrl() { 
        return callWebsiteUrl; 
    }
}

И, наконец, в нашем тесте JUnit:

public class MyClassTest extends TestCase {
    private MyClass classUnderTest;
    protected void setUp() {
        classUnderTest = new MyClassStub();
    }
    public void testPushEvent() { //could do with a more descriptive name
        //create some Event object 'event' here
        String response = classUnderTest.pushEvent(event);
        //possibly have other assertions here
        assertEquals("http://some.url", 
                     (MyClassStub)classUnderTest.getCallWebsiteUrl());
        //finally, check that the response from the callWebsite() hasn't been 
        //modified before being returned back from pushEvent()
        assertEquals(MyClassStub.RESPONSE, response);
    }
 }
4
ответ дан 28 November 2019 в 03:13
поделиться

Создайте абстрактный класс WebsiteCaller который будет родительским для ConcreteWebsiteCaller и WebsiteCallerStub .

Этот класс должен иметь один метод callWebsite (String url) . Переместите свой метод callWebsite из MyClass в ConcreteWebsiteCaller . И MyClass будет выглядеть так:

class MyClass {

    private WebsiteCaller caller;

    public MyClass (WebsiteCaller caller) {
        this.caller = caller;
    }

    public String pushEvent (Event event) {
        //do something here
        String url = constructURL (event); //construct the website url
        String response = caller.callWebsite (url);

        return response;
    }
}

и реализовать метод callWebsite в вашем WebsiteCallerStub некоторым образом подходящим для тестирования.

Затем в модульном тесте сделайте что-нибудь вроде этого:

@Test
public void testPushEvent() {
   MyClass mc = new MyClass (new WebsiteCallerStub());
   mc.pushEvent (new Event(...));
}
2
ответ дан 28 November 2019 в 03:13
поделиться

Заглушка

Вам понадобится тестовый двойной (заглушка), позволяющий изолированное, простое, модульное тестирование. Следующее не проверено, но демонстрирует идею. Использование внедрения зависимостей позволит вам внедрить во время тестирования тестовую версию вашего HttpURLConnection.

public class MyClass() 
{
   private IHttpURLConnection httpUrlConnection;   

   public MyClass(IHttpURLConnection httpUrlConnection)
   {
       this.httpUrlConnection = httpUrlConnection;
   }

   public String pushEvent(Event event) 
   {
       String url = constructURL(event);
       String response = callWebsite(url);
       return response;
  }
}

Затем вы создаете заглушку (иногда называемую имитацией объекта), которая будет заменять конкретный экземпляр.

class TestHttpURLConnection : IHttpURLConnection { /* Methods */ }

Вы также создадите конкретную версию для использования в производственном коде.

class MyHttpURLConnection : IHttpURLConnection { /* Methods */ }

Используя свой тестовый класс (адаптер ), вы можете указать, что должно произойти во время вашего теста. Фреймворк для имитации позволит вам сделать это с меньшим количеством кода, или вы можете подключить его вручную. Конечным результатом этого для вашего теста является то, что вы установите свои ожидания для своего теста, например, в этом случае вы можете настроить OpenConnection для возврата истинного логического значения (это, кстати, всего лишь пример). Затем ваш тест подтвердит, что, когда это значение истинно, возвращаемое значение вашего метода PushEvent соответствует некоторому ожидаемому результату. Я какое-то время не касался Java должным образом, но вот несколько рекомендуемых фреймворков для имитации , как указано членами StackOverflow.

28
ответ дан 28 November 2019 в 03:13
поделиться

Подходя к вещам под немного другим углом ...

Я бы меньше беспокоился о тестировании этого конкретный класс. Код в нем чрезвычайно прост, и хотя функциональный тест, чтобы убедиться, что он работает с подключением, был бы полезен, тест на уровне модулей «может» не понадобиться.

Вместо этого я бы сосредоточился на тестировании вызываемых им методов, которые действительно что-то делают. В частности ...

Я бы протестировал метод constructURL из этой строки:

String url = constructURL (event);

, убедившись, что он может правильно построить URL-адрес из разных событий и выдает исключения, когда это необходимо (возможно, при недопустимом событии или нулевом значении).

И я бы протестировал метод из следующей строки:

String responseMessage = responseParser.getResponseMessage (connection);

Возможно, вытащил любую логику «получить информацию из соединения» в одну процедуру и оставил только «проанализировать указанную информацию» в исходной:

String responseMessage = responseParser.getResponseMessage(responseParser.getResponseFromConnection(connection));

Или что-то вдоль этих линий.

Идея состоит в том, чтобы поместить любой код «должен иметь дело с внешними источниками данных» в один метод, а любую логику кода - в отдельные методы, которые можно легко протестировать.

7
ответ дан 28 November 2019 в 03:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: