Как много из насмешки - это издевательство над Мокито? - PullRequest
0 голосов
/ 06 октября 2018

Это серьезный вопрос, обещаю.Я провел последние 2 часа, читая столько определений Mock, сколько смог найти, и ни одно из них не объясняет мне это.

У меня есть класс, который я хочу протестировать, и этот класс требует класса mapper как частиего основного конструктора:

open class PoiListViewModel @Inject constructor(
        private val mapper: PoiMapper
) : ViewModel() {

В моем модульном тесте у меня есть следующий код:

//Mock objects needed to instantiate the class under test
@Mock lateinit var mapper: PoiMapper

// Class being tested
lateinit var poiListViewModel: PoiListViewModel

@Before
fun setup() {
    MockitoAnnotations.initMocks(this)
    poiListViewModel = PoiListViewModel(mapper)
}

Мой вопрос к вам, все умные разработчики, что такое макет?И, в частности, сколько из моего исходного класса он повторяет?

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

По крайней мере, это мой невежественный взгляд на издевательства.Но я, очевидно, обижен, потому что в моем модульном тесте мой «макетный» класс мапперов выглядит как фактический класс мапперов.Если я отлаживаю свой модульный тест, я вижу, как он проходит через весь код моего класса mapper.Я вижу, что он возвращает преобразованные данные.

Вот код класса картографа (если он имеет значение):

open class PoiMapper @Inject constructor() {

    fun mockTest(num: Int): Int{
        return num *23
    }

    fun mapToPresentation(domainModel: Poi_Domain): Poi_Presentation {

        var test = 3
        var results = mockTest(test)

        return Poi_Presentation(domainModel.id,domainModel.name,domainModel.description,
                domainModel.img_url,domainModel.latitude,domainModel.longitude,domainModel.imgFocalpointX,
                domainModel.imgFocalpointY,domainModel.collection,domainModel.collectionPosition,
                domainModel.release,domainModel.stampText)
    }
}

Может ли кто-нибудь объяснить мне, какая часть издевательства является издевательством над Мокито?Я инстанцировал издевательства неправильно?Может кто-нибудь дать мне лучший способ думать о насмешках, чтобы я мог все это обернуть?

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Ваше понимание насмешек верно.Вы сталкиваетесь с последним по умолчанию поведением Kotlin, как деталь реализации Mockito.

Mockito mocks (в отличие от шпионов Mockito) определены вместо вашего экземпляра класса.Если вы не указали их, чтобы они возвращали иначе, они записывают все взаимодействия и возвращают фиктивные значения по умолчанию (ноль, пустая строка, пустой список, ноль и т. Д.).Вы можете подтвердить, что они правильно работали с тестируемой системой, указав возвращаемые значения (when / thenReturn), указав специфическое поведение для методов (when / thenAnswer), или проверив, что определенные методы быливызывается (verify) и извлекает конкретные экземпляры, с которыми они были вызваны (ArgumentCaptor).

Под капотом Mockito достигает этого, генерируя подкласс класса, над которым вы издеваетесь,и переопределение всех методов для делегирования внутреннему обработчику Mockito.Это то, что дает Mockito возможность переопределять поведение в режиме без вывода сообщений: ваша тестируемая система думает, что она вызывает вашу зависимость, но ваш код использует диспетчеризацию виртуальных методов Java для переопределения поведения вашей зависимости.

Вот хитрость:Методы Java являются виртуальными по умолчанию, если вы не отметите их final.В Kotlin функции закрыты по умолчанию, если вы не отметите их open.(Я буду продолжать называть их final, потому что это определение используется в JVM или Android Dexer, который читает байт-код, который все равно генерирует Kotlin.) Когда виртуальная машина уверена в типе ссылки на основе статическогонабрав, и вы вызываете метод final, компилятору разрешено встроить код этой реализации (JLS 8.4.3.3) , потому что вы утверждали, что метод не может быть переопределен и любой код, который пытаетсяпереопределить его не удастся при компиляции. Сгенерированный код Mockito не скомпилирован таким образом, и Mockito не может обнаружить этот случай.

В вашем случае mapToPresentation не является open, поэтому компилятор видит его какfinal и не поддерживает диспетчеризацию виртуальных методов, которая позволила бы Mockito переопределить поведение.Ваше определение mocking правильно, и ваш код будет правильным, за исключением того, что Kotlin закрыт по умолчанию, а Java открыт по умолчанию.Кроме того, вы действительно полагаетесь на то, что функция open, потому что вы хотите, чтобы она вызывалась с реализацией, отличной от той, что в функции.

Тривиально, вы можете просто сделать все функции open что вы намереваетесь переопределить или использовать встроенную функцию Mockito 2.1 + для насмешки над финальными методами .Однако, как правило, вы хотите, чтобы объект вел себя как PoiMapper, даже если он не соответствует вашей конкретной реализации PoiMapper.Это может быть хорошей причиной для разделения интерфейс / импл, так что ваш класс PoiListViewModel принимает интерфейс PoiMapper.В производственном процессе вы можете предоставить PoiMapperImpl, как у вас, но Mockito может генерировать произвольную реализацию интерфейса PoiMapper, не заботясь о том, открыты или закрыты PoiMapperImpl и его функции.

0 голосов
/ 17 октября 2018

Вы добавили аннотацию

@RunWith(MockitoJUnitRunner.class)

в свой тестовый класс?

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