Как использовать mockito для тестирования службы REST?

Я очень новичок в модульном тестировании Java, и я слышал, что среда Mockito действительно хороша для целей тестирования.

Я разработал REST-сервер (методы CRUD) и теперь хочу его протестировать, но не знаю как?

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

Вот что я пробовал до сих пор, но я почти уверен, что это неправильный путь.

    @Test
    public void testInitialize() {
        RESTfulGeneric rest = mock(RESTfulGeneric.class);

        ResponseBuilder builder = Response.status(Response.Status.OK);

        builder = Response.status(Response.Status.OK).entity(
                "Your schema was succesfully created!");

        when(rest.initialize(DatabaseSchema)).thenReturn(builder.build());

        String result = rest.initialize(DatabaseSchema).getEntity().toString();

        System.out.println("Here: " + result);

        assertEquals("Your schema was succesfully created!", result);

    }

Вот код для метода initialize.

    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/initialize")
    public Response initialize(String DatabaseSchema) {

        /** Set the LogLevel to Info, severe, warning and info will be written */
        LOGGER.setLevel(Level.INFO);

        ResponseBuilder builder = Response.status(Response.Status.OK);

        LOGGER.info("POST/initialize - Initialize the " + user.getUserEmail()
                + " namespace with a database schema.");

        /** Get a handle on the datastore itself */
        DatastoreService datastore = DatastoreServiceFactory
                .getDatastoreService();


        datastore.put(dbSchema);

        builder = Response.status(Response.Status.OK).entity(
                "Your schema was succesfully created!");
        /** Send response */
        return builder.build();
    }

В этом тестовом примере я хочу отправить строку Json на сервер (POST). Если все прошло хорошо, сервер должен ответить: «Ваша схема успешно создана!».

Кто-нибудь может мне помочь?


person Ion Morozan    schedule 27.05.2012    source источник
comment
Где в приведенном выше коде вы отправляете строку JSON на сервер? Зачем вообще нужен Mockito для таких тестов?   -  person JB Nizet    schedule 27.05.2012
comment
Я не знаю, нужен ли мне Mockito для таких тестов, как я уже сказал, я пытаюсь понять, как тестировать службу REST. Файл JSON отправляется, когда я вызываю этот метод: rest.initialize(DatabaseSchema), DatabaseSchema — это файл JSON.   -  person Ion Morozan    schedule 27.05.2012
comment
Нет, это не так. rest — мошенник. Это объект, который отвечает на то, что вы ему говорите. Это не ваш сервер отдыха. Вы тестируете Mockito вместо того, чтобы тестировать свой сервер. Вам не нужен Mockito для таких тестов.   -  person JB Nizet    schedule 27.05.2012
comment
Благодарю вас! Не могли бы вы направить меня, как мне быть? Я хочу проверить свои методы CRUD. Мне нужны модульные тесты, чтобы проверить каждый метод остатка на моем сервере.   -  person Ion Morozan    schedule 27.05.2012
comment
Либо вы выполняете интеграционные тесты и фактически отправляете JSON на свой сервер и анализируете полученные ответы, чтобы убедиться, что они подходят, либо вы выполняете модульные тесты для проверки методов ваших классов, используемых на стороне сервера. Mockito может быть полезен для последнего. Но не видя вашего кода, невозможно сказать вам, как его протестировать, кроме как указать вам на документацию Mockito.   -  person JB Nizet    schedule 27.05.2012
comment
Когда вы сказали провести интеграционные тесты и отправить JSON на сервер, вы имели в виду использовать HttpClient и фактически отправить запрос на URL-адрес API сервера? Но такие тесты не являются юнит-тестами, потому что я тестирую отдельные методы! Я думаю, что я путаю или это не ясно для меня.   -  person Ion Morozan    schedule 27.05.2012
comment
Да, это то, что я имел в виду под интеграционными тестами. Это также то, о чем вы просили: В этом тестовом примере я хочу отправить строку Json на сервер (POST). Если вы хотите протестировать отдельный метод объекта, то ладно, но это не то, о чем вы просили. И не зная, что этот метод должен делать, каково его имя, аргументы и зависимости, помочь невозможно.   -  person JB Nizet    schedule 27.05.2012
comment
Прежде всего большое спасибо за помощь мне. В этом случае метод initialize на стороне сервера получает файл JSON, например: {"kind":"Note", "property":["id","name","note","date"]}, а затем после разбора JSON и извлечения свойств создает базу данных с этой схемой, где kind — имя БД, а properties — поля (столбцы). Я обновил вопрос с кодом этого метода. Затем после инициализации я хочу протестировать метод Add, Update, Delete, Get.   -  person Ion Morozan    schedule 27.05.2012
comment
Я хочу предложить WireMock. Это библиотека для заглушек и имитации веб-сервисов. wiremock.org/index.html   -  person Lilylakshi    schedule 03.03.2016


Ответы (5)


ХОРОШО. Итак, контракт метода следующий: анализировать входную строку как JSON и отправлять обратно BAD_REQUEST, если она недействительна. Если это допустимо, создайте объект в datastore с различными свойствами (вы их знаете) и отправьте обратно OK.

И вам нужно проверить, что этот контракт выполняется методом.

Где здесь может помочь Mockito? Что ж, если вы тестируете этот метод без Mockito, вам нужен настоящий DataStoreService, и вам нужно убедиться, что сущность была создана правильно в этом реальном DataStoreService. Здесь ваш тест больше не является модульным тестом, а также в этом случае его слишком сложно тестировать, он слишком длинный и слишком сложный для запуска, потому что ему нужна сложная среда.

Mockito может помочь, имитируя зависимость от DataStoreService: вы можете создать макет DataStoreService и убедиться, что этот макет действительно вызывается с соответствующим аргументом сущности, когда вы вызываете свой метод initialize() в своем тесте.

Для этого вам нужно иметь возможность внедрить DataStoreService в тестируемый объект. Это может быть так же просто, как рефакторинг вашего объекта следующим образом:

public class MyRestService {
    private DataStoreService dataStoreService;

    // constructor used on the server
    public MyRestService() {
        this.dataStoreService = DatastoreServiceFactory.getDatastoreService();
    }

    // constructor used by the unit tests
    public MyRestService(DataStoreService dataStoreService) {
        this.dataStoreService = dataStoreService;
    }

    public Response initialize(String DatabaseSchema) {
         ...
         // use this.dataStoreService instead of datastore
    }
}

И теперь в вашем тестовом методе вы можете сделать:

@Test
public void testInitializeWithGoodInput() {
    DataStoreService mockDataStoreService = mock(DataStoreService.class);
    MyRestService service = new MyRestService(mockDataStoreService);
    String goodInput = "...";
    Response response = service.initialize(goodInput);
    assertEquals(Response.Status.OK, response.getStatus());

    ArgumentCaptor<Entity> argument = ArgumentCaptor.forClass(Entity.class);
    verify(mock).put(argument.capture());
    assertEquals("the correct kind", argument.getValue().getKind());
    // ... other assertions
}
person JB Nizet    schedule 27.05.2012
comment
Благодарю вас! У меня вопрос: здесь должен был быть verify(mockDataStoreService).put(argument.capture()); ? Даже если это произошло после того, как я изменил на mockDataStoreService, я получаю сообщение об ошибке The method verify(DatastoreService) is undefined for the type MyRestService. - person Ion Morozan; 27.05.2012
comment
Метод тестирования должен находиться в классе модульного теста, а не в классе, который вы тестируете. И EasyMock обычно используется с import static org.mockito.Mockito.*. verify — это статический метод класса Mockito. - person JB Nizet; 27.05.2012

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

Если вы хотите выполнить модульное тестирование кода, который вы написали, Mockito, безусловно, будет полезным инструментом.

Я предлагаю вам больше узнать о насмешливом / модульном тестировании и о том, в каких обстоятельствах его следует использовать.

person darrengorman    schedule 27.05.2012
comment
Я никак не могу согласиться с этим ответом. Я успешно использовал Mockito во множестве интеграционных тестов. Часто существуют интеграционные тесты, в которых целые подсистемы не имеют значения и могут быть разумно смоделированы. Mockito (или какой-либо другой макетный фреймворк) обычно полезен, когда вы хотите протестировать какую-то часть системы, без взаимодействия с другими частями, омрачающими то, что происходит в вашем тесте. Это может быть модульный тест, объемный тест или интеграционный тест. - person Dawood ibn Kareem; 28.05.2012
comment
@DavidWallace Согласен. Моя формулировка вводила в заблуждение, я имел в виду, что с тем, что описывал OP (развертывание своего приложения и запуск запросов REST), насмешка не будет иметь большого смысла. - person darrengorman; 28.05.2012

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

Чтобы протестировать внутренние части службы REST (например, конкретный метод службы), не затрагивая базу данных, вы должны имитировать подсистему БД, позволяя тестировать только внутренние компоненты службы, не задействуя БД. Это тестирование относится к сервисному модулю REST, а не к клиентской части.

Чтобы протестировать саму службу REST, вы должны использовать настоящую клиентскую библиотеку, создав полный интеграционный тест. Здесь можно использовать Mockito для имитации частей клиента, не связанных с потреблением службы REST.

person Dave Newton    schedule 27.05.2012
comment
Спасибо за ответ. На самом деле мне нужно протестировать REST-сервис, который я разработал, поэтому я хочу протестировать отдельные методы, но не знаю, как это сделать. Например, как я сказал в своем вопросе, я хочу отправить JSON на сервер и проверить ответ. Я не знаю, где я должен сделать запрос, на локальном хосте или где я развернул API. Я использую GAE в качестве бэкэнда. - person Ion Morozan; 27.05.2012
comment
@IonMorozan Не уверен, что понимаю вопрос - вы можете протестировать локально, вы можете протестировать развертывание, это зависит от того, что вы хотите протестировать. Запрос, скорее всего, будет сделан локально, независимо от конечной точки. - person Dave Newton; 27.05.2012

Лучший способ — использовать wiremock. Добавьте следующие зависимости: com.github.tomakehurst wiremock 2.4.1 org.igniterealtime.smack smack-core 4.0.6

Определите и используйте wiremock, как показано ниже.

@Rule
public WireMockRule wireMockRule = new WireMockRule(8089);

String response ="Hello world";
StubMapping responseValid = stubFor(get(urlEqualTo(url)).withHeader("Content-Type", equalTo("application/json"))
        .willReturn(aResponse().withStatus(200)
                .withHeader("Content-Type", "application/json").withBody(response)));
person Yallaling Goudar    schedule 01.12.2016

Я согласен, что это не модульное тестирование, а интеграционный тест, в любом случае вы бы предпочли взглянуть на трикотажные и встроенные тесты сервера гризли. Подводя итог, этот код запускает сервер гризли (который также может запускать базу данных) на локальном хосте: 8888, затем настраивает клиент трикотажа клиента и отправляет запрос POST, ответ которого следует проверить. Это интеграция, поскольку вы тестируете и сервер, и базу данных, однако вы можете использовать mockito для эмуляции базы данных, но это зависит от того, насколько связаны ваш сервер и база данных.

(тест с использованием трикотажа 1.11 и гризли 2.2)

    @BeforeClass
    public static void setUpClass() throws Exception {
        // starts grizzly
        Starter.start_grizzly(true);
        Thread.sleep(4000);
    }

    @Before
    public void setUp() throws Exception {
        client = new Client();
        webResource = client.resource("http://localhost:8888");
    }   

    @Test
    public void testPostSchemaDatabase() throws Exception {
        {
            String DatabaseSchema = "{ database_schema : {...}}";
            logger.info("REST client configured to send: "  + DatabaseSchema);
            ClientResponse response =  
                    webResource
                             .path("/initialize")
                             .type("application/json")
                             .post(ClientResponse.class, DatabaseSchema);
            //wait for the server to process
            Thread.sleep(2000);
            assertEquals(response.getStatus(), 204);    
            //test the response
        }       
    }

    @After
    public void after() throws JSONException
    {
            //probably you want to delete the schema at database and stop the server

}
person user311174    schedule 28.05.2012