Как написать тест jUnit для класса, который использует сетевое соединение - PullRequest
27 голосов
/ 27 апреля 2010

Я хотел бы знать, каков наилучший подход для тестирования метода "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 ();
        }
    }

}

Ответы [ 5 ]

28 голосов
/ 27 апреля 2010

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 */ }

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

10 голосов
/ 27 апреля 2010

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

7 голосов
/ 27 апреля 2010

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

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

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

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

String url = constructURL (event);

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

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

String responseMessage = responseParser.getResponseMessage (connection);

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

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

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

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

4 голосов
/ 27 апреля 2010

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

Создайте абстрактный класс 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(...));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...