t.me/atinfo_chat Telegram группа по автоматизации тестирования

Подводные камни при написании юнит-тестов?

Теги: #<Tag:0x00007f9af8f4cbb8> #<Tag:0x00007f9af8f4caf0> #<Tag:0x00007f9af8f4ca00> #<Tag:0x00007f9af8f4c910> #<Tag:0x00007f9af8f4c848>

Всем привет!

Подскажите пжста с юнит-тестированием.

При написании юнит-тестов частой проблемой является подмена функций, вызываемых внутри тестируемой функции (mocking/stubbing).

Очень интересует как это решается в таких языках программирования как Java и C/C++, где есть строгая типизация.

К примеру в python, ruby, js можно инжектить фейковые модули, классы, объекты, функции как аргументы функции или класса (dependency injection). Насколько я знаю, с Java понятие dependency injection также тесно связано. Интересно, как в java и плюсах подменяются импортированные классы и модули на фейковые при юнит-тестировании.

Dependency Injection как раз эту проблему и решает. Все зависимости приходят извне, соответственно в юнит тестах можно просто подсунуть моки в сеттеры или конструкторы.

С точки зрения строгой типизации, то тут тоже ничего военного. Мок должен быть того же типа, что и ожидаемый объект (наследоваться от того же класса и имплементить те же интерфейсы). С технической точки зрения реализуется либо dynamic proxy, либо генерацией класса-наследеника оригинального ожидаемого типа.

В джавке вообще есть библиотеки типа Mockito, которые очень упрощают написание моков/стаббов.

В чем именно проблема подмены? Если речь идет о поиске конкретно инструмента, то для джавы это будет Mockito

@olyv Проблема может быть в том, что функция (класс, модуль) может быть недоступна снаружи (в тесте) для подмены из-за ограничений области видимости. Н-р в случае c javascript:

// file.js

var Server = require("some-module").Server;

module.exports.func = () => {
    var server = new Server({ host: "localhost", port: 8888 });
    server.start();
};

// test.js

var func = require("./file").func;

describe("my tests", () => {
    it("should start server", () => {
        func();  // here I can't observe server, because it's internal
    });
});

Но если поменять слегка код, то можно сделать инжекцию фейкового класса.

// file.js

var Server = require("some-module").Server;

module.exports.func = opts => {
    opts = opts || {};
    var S = opts.Server || Server;
    var server = new S({ host: "localhost", port: 8888 });
    server.start();
};

// test.js

var expect = require("chai").expect;
var sinon = require("sinon");

var func = require("./file").func;

describe("my tests", () => {
    var sOpts, start, Server;

    before(() => {
        Server = function (opts) {
            sOpts = opts;
            this.start = start = sinon.spy();
        };
    })

    it("should start server", () => {
        func({ Server: Server });
        expect(sOpts.host).to.be.equal("localhost");
        expect(sOpts.port).to.be.equal(8888);
        expect(start.calledOnce).to.be.true
    });
});

Или реальный пример из проекта гугла:

Интересуют подобные примеры, как инжекция классов или модулей делается в Java и C++.

Я затрудняюсь перевести это на джаву поскольку слабоват в js.

@olyv,

А не найдется пара ссылок на хорошие примеры юниттестов на java c mockito?

@Sergei_Chipiga мне вот этот пример кажется очень удачным https://dzone.com/articles/a-guide-to-mocking-with-mockito

1 Симпатия

благодарю :slightly_smiling_face:

1 Симпатия