Dominando o MockK, Parte 2: Coroutines e Extensions

Ao finalizar esta aula, nós:

  • Saberemos o que é a correspondência de argumentos (argument matching).
  • Entenderemos como podemos utilizar os diferentes argument matchers fornecidos pelo MockK 1.14.

Esta aula assume que:

Uma Introdução Muito Rápida à Correspondência de Argumentos (Argument Matching)

A correspondência de argumentos (argument matching) é uma técnica que nos permite verificar se o argumento real passado para a função invocada do nosso dublê de teste (test double) cumpre a condição especificada. Podemos usar a correspondência de argumentos quando fazemos o stub de funções do nosso dublê de teste (incluindo funções regulares, coroutines com coEvery ou extension functions) ou quando verificamos as interações que aconteceram entre o sistema sob teste e o nosso dublê de teste (com verify ou coVerify).

A seguir, daremos uma olhada no sistema sob teste.

Introdução ao Sistema Sob Teste

Durante esta aula, usaremos argument matchers ao fazer o stub das funções de um repositório que manipula objetos Task. Para manter tudo o mais simples possível, as declarações dos componentes do modelo de dados foram adicionadas a um único arquivo-fonte chamado Task.kt. Este arquivo tem a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
data class Creator(val id: Long)

enum class TaskResolution {
    DUPLICATE,
    DONE,
    WONT_DO
}

enum class TaskStatus {
    CLOSED,
    IN_PROGRESS,
    OPEN
}

data class Task(
    val id: Long,
    val creator: Creator,
    val title: String,
    val status: TaskStatus,
    val resolution: TaskResolution
)

A interface TaskRepository possui três funções:

  • A função findById() retorna as informações de uma tarefa utilizando o seu id como critério de busca. Se nenhuma tarefa for encontrada no banco de dados, esta função retorna null.
  • A função findByCreatorAndStatus() retorna todas as tarefas que foram criadas pela pessoa especificada e que possuem o status fornecido. Se nenhuma tarefa for encontrada no banco de dados, esta função retorna uma lista vazia.
  • A função create() insere uma nova tarefa no banco de dados e retorna as informações da tarefa criada.

O código-fonte da interface TaskRepository tem a seguinte aparência:

1
2
3
4
5
6
7
8
interface TaskRepository {

    fun findById(id: Long): Task?

    fun findByCreatorAndStatus(creatorId: Long, status: TaskStatus): List<Task>

    fun create(task: Task): Task
}

Vamos avançar e descobrir como podemos utilizar os argument matchers fornecidos pelo MockK.

Utilizando os Argument Matchers Fornecidos pelo MockK

Agora analisaremos doze exemplos que demonstram como podemos usar os argument matchers fornecidos pelo MockK 1.14 em conjunto com JUnit Jupiter 6.0.3 e AssertJ 3.27.7. Vamos começar.

Exemplo 1: O Matcher Padrão (Default Matcher)

Se não especificarmos o argument matcher utilizado, o MockK usa o matcher retornado pela função eq(). Este matcher garante que o argumento real seja igual ao valor ou objeto especificado.

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o valor do argumento id for igual a 1L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use the default matcher")
    fun shouldMatchSimpleFunctionArgumentWithDefaultMatcher() {
        every { repository.findById(ID) } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Nota: Também quero destacar que, embora o manual do usuário do MockK mencione que o matcher padrão pode ser usado para valores simples, ao testar esse comportamento com objetos complexos como Task, constata-se que o MockK também utiliza o matcher retornado pela função eq() nesse caso.

Exemplo 2: O Argumento É Igual ao Valor Especificado

Se quisermos garantir explicitamente que o argumento real seja igual ao valor ou objeto especificado, devemos usar o matcher retornado pela função eq().

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o valor do argumento id for igual a 1L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use eq()")
    fun shouldMatchFunctionArgumentWithEq() {
        every { repository.findById(eq(ID)) } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Exemplo 3: O Argumento Não É Igual ao Valor Fornecido

Se quisermos garantir que o argumento real não seja igual ao valor ou objeto especificado, devemos usar o matcher retornado pela função neq().

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o valor do argumento id não for igual a -1L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use neq()")
    fun shouldMatchFunctionArgumentWithNeq() {
        every { repository.findById(neq(-1L)) } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Nota: Antes de continuarmos, vale ressaltar que as funções eq() e neq() utilizam a função deepEquals(). Se você quiser comparar igualdade referencial (mesma referência em memória), deverá utilizar as funções refEq() e nrefEq(). Por outro lado, se quiser comparar a igualdade de dois valores ou objetos utilizando a função compareTo(), você deverá criar o seu argument matcher utilizando a função cmpEq().

Exemplo 4: O Argumento É Menor Que o Valor Especificado

Se quisermos garantir que o argumento real seja menor que o valor especificado, devemos usar o matcher retornado pela função less().

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o valor do argumento id for menor que 2L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use less()")
    fun shouldMatchFunctionArgumentWithLess() {
        every { repository.findById(less(ID + 1)) } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Exemplo 5: O Argumento É Menor ou Igual ao Valor Fornecido

Se quisermos garantir que o argumento real seja menor ou igual ao valor especificado, devemos usar o matcher retornado pela função less(). Ao invocar esta função, precisamos configurar o valor “esperado” e garantir que o valor do argumento andEquals seja true.

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o valor do argumento id for menor ou igual a 1L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("should use less() when andEquals is true")
    fun shouldMatchFunctionArgumentWithLessAndEqual() {
        every {
            repository.findById(
                less(
                    value = ID,
                    andEquals = true
                )
            )
        } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Exemplo 6: O Argumento É Maior Que o Valor Especificado

Se quisermos garantir que o argumento real seja maior que o valor especificado, devemos usar o matcher retornado pela função more().

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o valor do argumento id for maior que 0L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use more()")
    fun shouldMatchFunctionArgumentWithMore() {
        every { repository.findById(more(ID - 1)) } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Exemplo 7: O Argumento É Maior ou Igual ao Valor Especificado

Se quisermos garantir que o argumento real seja maior ou igual ao valor especificado, devemos usar o matcher retornado pela função more(). Ao invocar esta função, precisamos configurar o valor “esperado” e garantir que o valor do argumento andEquals seja true.

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o valor do argumento id for maior ou igual a 1L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use more() when andEquals is true")
    fun shouldMatchFunctionArgumentWithMoreAndEqual() {
        every {
            repository.findById(
                more(
                    value = ID,
                    andEquals = true
                )
            )
        } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Nota: Antes de continuarmos, vale destacar que os quatro argument matchers anteriores utilizam a função compareTo().

Exemplo 8: O Argumento É Qualquer Objeto

Se quisermos garantir que o argumento real seja qualquer objeto, devemos usar o matcher retornado pela função any().

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o argumento real id for qualquer objeto, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use any()")
    fun shouldMatchFunctionArgumentWithAny() {
        every { repository.findById(any()) } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Exemplo 9: O Argumento É um Objeto do Tipo Long

Se quisermos garantir que o tipo do argumento real esteja correto, devemos usar o matcher retornado pela função ofType(). Ao invocar esta função, devemos especificar o tipo esperado.

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o argumento real id for um objeto do tipo Long, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use ofType()")
    fun shouldMatchFunctionArgumentWithOfType() {
        every { repository.findById(ofType(Long::class)) } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Exemplo 10: O Argumento Cumpre o Predicado Fornecido

Se quisermos garantir que o argumento real cumpra o predicado especificado, devemos usar o matcher retornado pela função match(). Ao invocar esta função, devemos especificar o predicado que deve ser avaliado como true.

Por exemplo, se quisermos garantir que a função findById() retorne a tarefa encontrada quando for invocada e o argumento real id for igual a 1L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByIdStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use match()")
    fun shouldMatchFunctionArgumentWithMatch() {
        every { repository.findById(match { it == ID }) } returns TASK

        val returned = repository.findById(ID)
        assertThat(returned?.id).isEqualByComparingTo(ID)
    }
}

Exemplo 11: Um Argumento Complexo Cumpre o Predicado Fornecido

Se quisermos garantir que o argumento real possua uma propriedade igual ao valor especificado, precisamos verificar se o argumento real cumpre um predicado. Em outras palavras, podemos usar o matcher retornado pela função match().

Por exemplo, se quisermos garantir que a função create() retorne a tarefa criada quando for invocada e o id da tarefa criada for igual a 1L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one complex function parameter")
class CreateStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Should use match()")
    fun shouldMatchFunctionArgumentWithMatch() {
        every { repository.create(match { it.id == ID }) } returns TASK

        val returned = repository.create(TASK)
        assertThat(returned).isSameAs(TASK)
    }
}

Exemplo 12: Combinar Valores de Argumentos com Argument Matchers

Se a função invocada tiver múltiplos parâmetros, também podemos combinar valores diretos de argumentos com argument matchers (uma grande vantagem do MockK em relação a bibliotecas legadas). Por exemplo, se quisermos garantir que a função findByCreatorAndStatus() retorne as tarefas encontradas quando for invocada e o chamador desta função quiser obter tarefas fechadas (CLOSED) que foram criadas por um usuário cujo ID é 5L, o código-fonte da nossa classe de teste terá a seguinte aparência:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import io.mockk.every
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

@Tag("unitTest")
@DisplayName("Stub function which has one simple function parameter")
class FindByCreatorAndStatusStubExampleTest {

    companion object {

        private const val ID = 1L
        private const val CREATOR_ID = 5L
        private const val TITLE = "Write stub example"
        private val STATUS = TaskStatus.CLOSED
        private val RESOLUTION = TaskResolution.DONE

        private val TASK = Task(
            id = ID,
            creator = Creator(id = CREATOR_ID),
            title = TITLE,
            status = STATUS,
            resolution = RESOLUTION
        )
    }

    private lateinit var repository: TaskRepository

    @BeforeEach
    fun createRepositoryStub() {
        repository = mockk<TaskRepository>()
    }

    @Test
    @DisplayName("Mix regular arguments and argument matchers")
    fun shouldMatchArgumentsWithDefaultMatcherAndProvidedMatcher() {
        every {
            repository.findByCreatorAndStatus(
                CREATOR_ID,
                eq(STATUS)
            )
        } returns listOf(TASK)

        val returned = repository.findByCreatorAndStatus(CREATOR_ID, STATUS)
        assertThat(returned).hasSize(1)
    }
}

Leitura Adicional:

Agora já sabemos como utilizar os argument matchers fornecidos pelo MockK. Vamos resumir o que aprendemos nesta aula.

Resumo

Esta aula nos ensinou oito pontos fundamentais:

  • Se não especificarmos o argument matcher utilizado, o MockK usa o matcher retornado pela função eq().
  • Se quisermos usar um argument matcher que utiliza a função deepEquals(), devemos criar nosso matcher usando as funções eq() ou neq().
  • Se quisermos comparar a igualdade referencial de dois objetos, devemos criar nosso argument matcher usando as funções refEq() ou nrefEq().
  • Se quisermos usar um argument matcher que utiliza a função compareTo(), devemos criar nosso matcher usando as funções cmpEq(), less() ou more().
  • Se quisermos garantir que o valor real do argumento seja qualquer objeto, devemos criar nosso argument matcher usando a função any().
  • Se quisermos garantir que o tipo do argumento real esteja correto, devemos criar nosso argument matcher usando a função ofType().
  • Se quisermos verificar se o argumento real cumpre um predicado, devemos criar nosso argument matcher usando a função match().
  • Se a função invocada tiver múltiplos parâmetros, podemos misturar valores diretos de argumentos com argument matchers.