Подтвердить что ты не робот

Как написать тест 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 ();
        }
    }

}
4b9b3361

Ответ 1

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

Ответ 2

Stubbing

Вам понадобится тестовый двойной (заглушка), чтобы обеспечить изолированное, легкое, модульное тестирование. Следующее не тестируется, но демонстрирует идею. Использование Dependency Injection позволит вам вводить во время тестирования тестовую версию вашего 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 */ }

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

Ответ 3

Приближение вещей с немного другого угла...

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

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

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

String url = constructURL (event);

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

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

String responseMessage = responseParser.getResponseMessage (connection);

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

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

или что-то в этом роде.

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

Ответ 4

В качестве альтернативы полезному ответу Finglas в отношении насмешек рассмотрим острый подход, когда мы переопределяем функциональность 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);
    }
 }

Ответ 5

Создайте абстрактный класс 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 в некотором роде, подходящем для тестирования.

Затем в вашем unit test сделайте что-то вроде этого:

@Test
public void testPushEvent() {
   MyClass mc = new MyClass (new WebsiteCallerStub());
   mc.pushEvent (new Event(...));
}