Как вы тестируете юнит для методов, которые занимают много времени? - PullRequest
3 голосов
/ 12 мая 2011

Я новичок в модульном тестировании.Есть метод, который выглядит следующим образом:

public Image getImage(String url) {
    Document pageSource = fecthSource(url);
    Image myImage = parseHtmlToImage(pageSource);
    return Image;
}

И я пишу модульный тест:

@test
public void getRightPicture() {
    Image img = imageFetcher.getImage("http://www.123.com");
    assertEquals(img.sourceUrl, "http://www.123.com/456.png");
    Image img = imageFetcher.getImage("http://www.abc.com");
    assertEquals(img.sourceUrl, "http://www.abc.com/def.png");
}

Но если для доступа в Интернет требуется много времени.Я обычно хочу использовать локальный HTML-файл для тестирования этого метода, но иногда проверяю веб-версию.

Есть предложения?

Ответы [ 4 ]

3 голосов
/ 12 мая 2011

Короткий ответ: инверсия зависимости.

Несколько более длинный ответ:

Проблема, вероятно, связана с тем, что делает fetchSource;не видя подробностей, этот ответ должен быть немного расплывчатым.Я предполагаю, что fetchSource вызывает что-то, чтобы фактически выполнить поиск, и что детали того, как это делается, не совсем то, что вы заинтересованы в тестировании.Я также предполагаю, что вы хотели писать «fetch» ​​везде, где пишете «fecht», но это не особенно важно для ответа.

Вы хотите абстрагироваться от конкретных кусочков fetchSource, чтобы вы моглисосредоточиться на тестировании, что важно для вас (parseHtmlToImage?).Это можно сделать несколькими способами:

  • Создайте свой imageFetcher с реализацией заглушки интерфейса, через который вы фактически получаете URL, передавая его через конструктор (инвертируя зависимость);Ваша заглушка играет роль «Интернета» и возвращает стандартный ответ.
  • Если вы не можете создать реализацию интерфейса заглушки, представьте интерфейс адаптера, где у вас есть производственная реализация, использующая текущий интерфейс.который вы используете для извлечения, и реализацию заглушки, которую вы используете для своих тестов.

Это довольно стандартная методика написания тестируемого кода (см. принцип инверсии зависимости).

3 голосов
/ 12 мая 2011

Ваш модульный тест не должен проверять, работает ли интернет. Вы должны предоставить некоторые фиктивные HTML-файлы и указать их для теста. Поскольку вы имеете дело с URL-адресом, у вас должна быть возможность указать путь, например "file:///path/to/test.html", вместо веб-URL.

1 голос
/ 16 мая 2011

Чтобы развернуть ответ «Внедрение зависимости» выше:

Итак, у вас есть:

class ImageFetcher {
    public Image getImage(String url) {
       Document pageSource = fetchSource(url);
       Image myImage = parseHtmlToImage(pageSource);
       return Image;
    }
    private Document fetchSource(String url) {
        // ... Network IO etc
    }
    private Image parseHtmlToImage(Document pageSource) {
        // Parsing logic
        // Network IO
        return image;
    }
}

Первый вопрос: «Действительно ли все это принадлежит« ImageFetcher ». IMO fetchSource неЭто (для меня) звучит как то, что сделал бы другой класс "Web Utils". Я бы также перенес получение изображения из ImageFetcher в класс utils.

class ImageFetcher {
    private WebUtils webUtils; 
    public ImageFetcher(webUtils webUtils) {
      this.webUtils = webUtils;
    }


    public Image getImage(String url) {
       Document pageSource = webUtils.fetchSource(url);
       Image myImage = parseHtmlToImage(pageSource);
       return Image;
    }

    private Image parseHtmlToImage(Document pageSource) {
        imageURL = // Parsing logic
        image = webUtils.fetchImage(imageURL);
        return image;
    }
}

Теперь Image Fetcher является первичнымпроблема заключается в том, чтобы определить, какое изображение использовать. Ему больше не нужно заботиться о том, как получить это изображение из сети. WebUtils можно смоделировать для модульных тестов.

@test
public void getImage_ReturnsBannerImage() {
     Image expected = // ...


     String html = "<html><img src="fred"/>...</html>"; // Keep this short and simple.
     mockedWebUtil.fetchSource(Any.String).returns(html);
     mockedWebUtil.fetchImage("fred").returns(expected);

     var subject = new ImageFetcher(mockedWebUtil);
     var actual = subject.getImage("blah");

     Assert.AreEqual(expected, actual);
}
1 голос
/ 12 мая 2011

Здесь есть два варианта -

  1. Локальный файл, безусловно, является более быстрым способом выполнения действий;кроме того, вы можете создать фиктивный объект (см. Mockito или Easymock для облегчения имитации), чтобы он мог имитировать метод fetchURL в классе, который вы пытаетесь протестировать;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...