[JAVA] [Spring Boot] Je suis tombé sur un test du nombre d'appels de méthode (framework Spock)

introduction

La première année des nouveaux diplômés est presque terminée. En utilisant le cadre de Spock, je suis tombé sur diverses façons de se moquer de Mockito et de Spock, alors j'ai fait un mémorandum et l'ai partagé pour des personnes similaires. Il serait préférable de créer une instance dans la situation réelle sans se moquer du code décrit ici, mais ce n'est qu'un exemple ...

environnement

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	// https://mvnrepository.com/artifact/org.spockframework/spock-core
	testCompile group: 'org.spockframework', name: 'spock-core', version: '1.3-groovy-2.5'
	// https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-all
	compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.5.6', ext: 'pom'
}

Code source

Il est publié sur GitHub.

tester

Vérifiez le nombre d'appels de méthode normaux

Mockito Utilisez when --then Return pour définir la valeur renvoyée par le simulacre. Puisque spockComponent1Factory est la cible de l'injection, ajoutez l'annotation ʻinitMocks et ʻInjectMocks dans setup. Il semble que vous puissiez vérifier le nombre de fois en écrivant true dans la partie verify, mais cela semble très étrange ...

SpockComponent1FactoryTest.groovy


    @InjectMocks
    SpockComponent1Factory spockComponent1Factory

    @Mock
    SpockComponent2Factory spockComponent2Factory

    @Mock
    SpockComponent2 spockComponent2

    @Mock
    SpockComponent3 spockComponent3

    @Mock
    SpockComponent4 spockComponent4

    def setup() {
        initMocks(this)
    }

    def "GetValueFromSpockComponent4 -Vérifiez le nombre d'appels"() {
        given:
        when(spockComponent2Factory.create(any(Integer.class))).thenReturn(spockComponent2)
        when(spockComponent2.getSpockComponent3()).thenReturn(spockComponent3)
        when(spockComponent3.getSpockComponent4()).thenReturn(spockComponent4)

        when:
        spockComponent1Factory.getValueFromSpockComponent4(0)

        then:
        verify(spockComponent2Factory, times(1)).create(any(Integer.class)) || true
    }

Utilisez RETURN_DEEP_STUBS pour simuler des instances imbriquées. En réécrivant le test ci-dessus, il peut être écrit comme suit.

SpockComponent1FactoryTestAlt.groovy


    @InjectMocks
    SpockComponent1Factory spockComponent1Factory

    @Mock
    SpockComponent2Factory spockComponent2Factory

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    SpockComponent2 spockComponent2

    @Mock
    SpockComponent4 spockComponent4

    def setup() {
        initMocks(this)
    }

    def "getValueFromSpockComponent -Pause de vérification du nombre d'appels"() {
        given:
        when(spockComponent2Factory.create(any(Integer.class))).thenReturn(spockComponent2)
        when(spockComponent2.getSpockComponent3().getSpockComponent4()).thenReturn(spockComponent4)

        when:
        spockComponent1Factory.getValueFromSpockComponent4(0)

        then:
        verify(spockComponent2Factory, times(1)).create(any(Integer.class)) || true
    }

Spock Un test similaire écrit en Spock ressemble à ceci: Si vous voulez vérifier le nombre d'exécutions, il semble que le test ne passera pas à moins que vous ne vous moquiez et ne stub en même temps dans la clause then. Au fait, dans Spock, si vous voulez remplacer le comportement défini dans setup par des cas de test individuels, il semble que vous deviez le faire dans la clause then. (Référence: Stackoverflow-Official Document)

SpockComponent1FactoryTestSpock.groovy


    SpockComponent1Factory spockComponent1Factory

    SpockComponent2Factory spockComponent2Factory

    SpockComponent2 spockComponent2

    SpockComponent3 spockComponent3

    SpockComponent4 spockComponent4

    def setup() {
        spockComponent2Factory = Mock(SpockComponent2Factory.class)
        spockComponent2 = Mock(SpockComponent2.class)
        spockComponent3 = Mock(SpockComponent3.class)
        spockComponent4 = Mock(SpockComponent4.class)
    }

    def "GetValueFromSpockComponent4 -La vérification du nombre d'appels a échoué"() {
        given:
        spockComponent3.getSpockComponent4() >> spockComponent4
        spockComponent2.getSpockComponent3() >> spockComponent3
        spockComponent2Factory.create(_ as Integer) >> spockComponent2

        when:
        spockComponent1Factory = new SpockComponent1Factory(spockComponent2Factory)
        spockComponent1Factory.getValueFromSpockComponent4(0)

        then:
        1 * spockComponent2Factory.create(_ as Integer)
    }

    def "GetValueFromSpockComponent4 -Vérification du nombre d'appels réussie"() {
        given:
        spockComponent3.getSpockComponent4() >> spockComponent4
        spockComponent2.getSpockComponent3() >> spockComponent3

        when:
        spockComponent1Factory = new SpockComponent1Factory(spockComponent2Factory)
        spockComponent1Factory.getValueFromSpockComponent4(0)

        then:
        1 * spockComponent2Factory.create(_ as Integer) >> spockComponent2
    }

Spock a également besoin d'ingéniosité pour simuler des instances imbriquées. Moins que.

SpockComponent1FactoryTestSpockAlt.groovy


    SpockComponent1Factory spockComponent1Factory

    SpockComponent2Factory spockComponent2Factory

    SpockComponent2 spockComponent2

    SpockComponent4 spockComponent4

    def setup() {
        spockComponent2Factory = Mock(SpockComponent2Factory.class)
        spockComponent2 = Mock(SpockComponent2.class)
        spockComponent4 = Mock(SpockComponent4.class)
    }

    def "GetValueFromSpockComponent4 -Pause de vérification du nombre d'appels"() {
        given:
        spockComponent2.getSpockComponent3() >> {
            Mock(SpockComponent3.class) {
                getSpockComponent4() >> spockComponent4
            }
        }

        when:
        spockComponent1Factory = new SpockComponent1Factory(spockComponent2Factory)
        spockComponent1Factory.getValueFromSpockComponent4(0)

        then:
        1 * spockComponent2Factory.create(_ as Integer) >> spockComponent2
    }

Vérifiez le nombre d'appels aux méthodes qui prennent null comme argument

Dans le code d'implémentation, null est directement placé dans la méthode qui prend l'argument de String.

SpockComponent1Factory.java


    public void nullArgumentMethodCall() {
        spockComponent2Factory.nullArgumentMethodCalled(null);
    }

Mockito J'ai utilisé nullable () parce que je ne pouvais pas le vérifier avec ʻany () `.

SpockComponent1FactoryTest.groovy


    @InjectMocks
    SpockComponent1Factory spockComponent1Factory

    @Mock
    SpockComponent2Factory spockComponent2Factory

    @Mock
    SpockComponent2 spockComponent2

    @Mock
    SpockComponent3 spockComponent3

    @Mock
    SpockComponent4 spockComponent4

    def setup() {
        initMocks(this)
    }

    def "NullArgumentMethodCall -La vérification du nombre d'appels a échoué"() {
        when:
        spockComponent1Factory.nullArgumentMethodCall()

        then:
        verify(spockComponent2Factory, times(1)).nullArgumentMethodCalled(any(String.class)) || true
    }

    def "NullArgumentMethodCall -Vérification du nombre d'appels réussie"() {
        when:
        spockComponent1Factory.nullArgumentMethodCall()

        then:
        verify(spockComponent2Factory, times(1)).nullArgumentMethodCalled(nullable(String.class)) || true
    }

Spock Le côté Spock a fonctionné sans aucun problème.

SpockComponent1FactoryTestSpock.groovy


    SpockComponent1Factory spockComponent1Factory

    SpockComponent2Factory spockComponent2Factory

    SpockComponent2 spockComponent2

    SpockComponent3 spockComponent3

    SpockComponent4 spockComponent4

    def setup() {
        spockComponent2Factory = Mock(SpockComponent2Factory.class)
        spockComponent2 = Mock(SpockComponent2.class)
        spockComponent3 = Mock(SpockComponent3.class)
        spockComponent4 = Mock(SpockComponent4.class)
    }

    def "NullArgumentMethodCall -Vérifiez le nombre d'appels"() {
        when:
        spockComponent1Factory = new SpockComponent1Factory(spockComponent2Factory)
        spockComponent1Factory.nullArgumentMethodCall()

        then:
        spockComponent2Factory.nullArgumentMethodCalled(_ as String)
    }
}

en conclusion

Comme je l'ai mentionné au début, il peut être possible de rendre le code de test plus lisible en créant réellement une instance qu'en se moquant de celle-ci, il semble donc préférable de l'écrire en réfléchissant (également comme un commandement pour vous-même).

Recommended Posts

[Spring Boot] Je suis tombé sur un test du nombre d'appels de méthode (framework Spock)
J'ai écrit un test avec Spring Boot + JUnit 5 maintenant
Je veux appeler une méthode et compter le nombre
Rendre System.out Mock avec Spock Test Framework
Je veux appeler une méthode d'une autre classe
Comment écrire un test unitaire pour Spring Boot 2
[Compatible JUnit 5] Ecrire un test en utilisant JUnit 5 avec Spring boot 2.2, 2.3
[JUnit 5] Ecrivez un test de validation avec Spring Boot! [Test de paramétrage]
[Débutant] Je suis tombé sur le lancement d'un projet avec Rails6
Ce que je suis tombé sur le test ActiveModel :: Serializer
Je n'ai pas compris Spring Boot depuis un mois
Présentation de Spring Boot2, un framework Java pour le développement Web (pour les débutants)
J'ai créé un système d'exemple MVC simple à l'aide de Spring Boot
Mémo de méthode de contrôleur de démarrage à ressort
J'ai essayé le guide d'introduction de Spring Boot [Création d'un service Web RESTful]
J'ai créé un formulaire de recherche simple avec Spring Boot + GitHub Search API.
Exemple de code pour le test unitaire d'un contrôleur Spring Boot avec MockMvc
Guide de démarrage de Spring Boot [Utilisation d'un service Web RESTful]
Écrivons un code de test pour la fonction de connexion avec Spring Boot