[소프트웨어 테스팅] Junit 단위 테스팅

머리말

이번 학기에 소프트웨어 테스팅 수업이 있고, 일부 테스팅 도구에 대한 실험 보고서를 작성하는 것이 교과 과정입니다. 이것은 정말 좋은 기회입니다. 결국 풀 스택 엔지니어로서 어떻게 테스팅에 대해 모를 수 있습니까? (웃음과 눈물).

이 기사에서는 단위 테스트 및 Java의 단일 테스트 도구인 junit 및 mokito 프레임워크의 간단한 사용에 대해 간략하게 설명합니다. 또한 이 시리즈의 다른 테스트 관련 기사도 포함할 것입니다.

1. 단위 테스트

1. 단위 테스트란 무엇입니까?

바이두 백과사전의 말을 빌리자면,

단위 테스트는 테스트 가능한 가장 작은 소프트웨어 단위의 검사 및 검증을 의미합니다. 단위 테스트에서 단위의 의미는 일반적으로 실제 상황에 따라 구체적인 의미를 정할 필요가 있는데, 예를 들어 C 언어에서 단위는 함수, 자바에서 단위는 클래스, 그래픽 소프트웨어는 창이나 메뉴를 참조할 수 있습니다. 일반적으로 단위는 테스트 중인 사람이 정의한 가장 작은 기능 모듈입니다. 단위 테스트는 소프트웨어 개발 중에 수행되는 가장 낮은 수준의 테스트 활동으로, 개별 소프트웨어 단위가 프로그램의 나머지 부분과 분리되어 테스트됩니다.

다음은 몇 가지 핵심 사항입니다.

  • 유닛은 인공
  • 단위 테스트는 다른 부분과 분리된 독립적인 단위입니다.

2. 단위 테스트가 필요한 이유는 무엇입니까?

여기까지 제 느낌을 말씀드리자면 저는 단위 테스트를 하는 버릇이 없습니다.. 단위 테스트에 시간이 많이 걸리는 것 같아요. 동시에 시간을 들일 가치가 없다고 생각합니다. 모두 단순한 조잡한 프로젝트입니다.
그러나 내가 개발하는 프로젝트가 커지고 요구 사항이 복잡해지면서 점차적으로 내가 하는 프로젝트의 품질이 점점 더 불안정해지고 있고 종종 이상한 버그가 있음을 알게 됩니다. 버그가 발생하면 종종 문제를 찾아야 합니다. 예를 들어 예전에 일반 IoT 플랫폼을 만든 적이 있는데, 시연 영상을 녹화할 때 보정 공식(4개 연산 지원)을 입력했는데, 당시 다른 기기에서는 정상적으로 동작했는데 그 때 이상이 있었습니다. 다행히 전체 프로젝트는 제가 직접 만들었고, 구현 내용을 어느정도 알고 있었습니다. 잠시 디버깅을 해보니 알고리즘 문제라는 걸 알게 되었어요. 그때 갑자기 생각나서 작성하다가 알고리즘이 구현은 음수를 지원하지 않습니다.음수 연산은 "(0-x)" 형식이 되며 해당 장치에서 업로드한 데이터가 음수가 되어 문제가 있습니다.

다행히 풀스택 개발이라 모든 것이 나 혼자 하는 프로젝트인데, 이 프로젝트가 팀으로 개발된다면 버그를 찾는 데 걸리는 시간이 기하급수적으로 늘어날 것으로 예상한다.

통합 테스트와 같은 대규모 테스트에서는 버그를 찾는 데 시간이 너무 오래 걸리기 때문에 각 작은 모듈의 정확성을 보장하기 위해 단위 테스트가 필요합니다. 시간이 더 걸리겠지만 버그 및 버그 수정에 소요된 시간에 비해 시간 가치가 있습니다.

프로젝트를 개발하는 과정에서 이전 버그의 유산을 해결하는 경우가 많습니다.
여기에 이미지 설명 삽입

인터넷에서 관련 요약을 보았고 공유할 수 있도록 잘 작성되었습니다 . 단위 테스트가 정확히 무엇입니까? 무엇을 해야 합니까?

  • 단위 테스트는 제품 품질에 매우 중요합니다.
  • 단위 테스팅은 모든 테스트 중 가장 낮은 테스팅 유형으로 첫 번째이자 가장 중요한 부분이며 100% 코드 커버리지를 보장하는 유일한 테스트이며 전체 소프트웨어 테스팅 프로세스의 기초이자 전제입니다. 단위 테스트는 너무 많은 버그로 인해 늦은 개발이 통제 불능 상태가 되는 것을 방지하고 단위 테스트의 비용 성능이 가장 좋습니다.
  • 통계에 따르면 오류의 약 80%는 소프트웨어 설계 단계에서 도입되며, 소프트웨어 수명 주기가 진행됨에 따라 소프트웨어 오류를 수정하는 비용이 증가합니다. 버그가 늦게 발견될수록 수정 비용이 더 많이 들고 기하급수적으로 증가합니다.
  • 그는 코더이자 단위 테스트의 주역으로서 결함 없는 프로그램을 만들 수 있는 유일한 사람입니다.그 누구도 할 수 없는 코드 사양, 최적화 및 테스트 가능성.
  • 자신 있게 리팩토링
  • 3,000번 실행 자동화

2. 주닛

1. 주니트란?

JUnit은 Java 언어의 단위 테스트 프레임워크입니다. Kent Beck과 Erich Gamma가 설립한 이 회사는 Kent Beck에서 파생된 xUnit 제품군의 가장 성공적인 회사로 성장했습니다. JUnit에는 자체 JUnit 확장 에코시스템이 있습니다.
현재 junit은 junit4에 비해 많은 변화가 있는 junit5로 발전했습니다. JUnit5는 세 가지 다른 하위 프로젝트의 여러 가지 모듈로 구성됩니다.
JUnit 5=JUnit 플랫폼+JUnit Jupiter+JUnit 빈티지

자세한 내용 은 공식 웹 사이트 를 참조하십시오

참고: junit은 기본적으로 Java 단위 테스트의 주류이며 오늘날 대부분의 Java 프로젝트에는 junit이 있습니다.

2.Junit 개념 - 주장

단위 테스트에 노출된 학생은 junit을 배울 때 assert 메서드가 무엇을 의미하고 assertion이 무엇인지 확실히 궁금해할 것입니다. 처음 접했을 때는 이런 주장이 무엇인지 궁금했다.

사실, assertion은 실제로 테스트 중인 메서드가 예상대로 작동하는지 여부를 결정하는 데 사용되는 일부 도우미 기능입니다. 일반적으로 이러한 도우미 기능을 assertion이라고 합니다.

3. Junit의 간단한 사용

다음 데모는 maven 프로젝트입니다.

① 의존성 가져오기

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.8.1</version>
        <scope>test</scope>
    </dependency>
    <!-- ... -->
</dependencies>
<build>
    <plugins>
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.2</version>
        </plugin>
        <plugin>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.22.2</version>
        </plugin>
    </plugins>
</build>

② 테스트 케이스 작성

다음은 공식 웹 사이트의 예입니다.

import static org.junit.jupiter.api.Assertions.assertEquals;

import example.util.Calculator;

import org.junit.jupiter.api.Test;

class MyFirstJUnitJupiterTests {
    
    

    private final Calculator calculator = new Calculator();

    @Test
    void addition() {
    
    
        assertEquals(2, calculator.add(1, 1));
    }

}

위의 예는 계산기.add(1, 1)의 반환 값이 2와 같을 것이라고 주장합니다.

아이디어에서 테스트를 실행하는 것이 매우 편리할 것입니다. 실행 아이콘을 클릭하기만
여기에 이미지 설명 삽입
하면 됩니다.아이디어에 없으면 mian 함수를 추가하여 실행하면 됩니다.

실행 중인 주장이 정확하면 프로그램은 다음과 같습니다.
여기에 이미지 설명 삽입
주장이 잘못된 경우 junit은 AssertionFailedError 예외를 발생시키고 무엇이 잘못되었는지 알려줍니다.
여기에 이미지 설명 삽입

4. SpringBoot 환경에서 junit 사용

물론 SpringBoot 환경과 같은 실제 개발에서는 Spring 컨테이너에 많은 비즈니스 코드 클래스가 주입되고, 주입된 다른 클래스들 사이에 종속성이 존재하기 때문에 이전과 같이 테스트 객체를 생성하는 것은 분명히 비현실적이다. . 그렇다면 이 문제의 해결책은 무엇일까요?
SpringBoot+SSM 프로젝트에서 junit을 사용하는 방법을 소개하겠습니다.

① 의존성 가져오기

SpringBoot에 종속성을 통합하며, 관련 종속성을 테스트해야 하는 경우 해당 테스트 모듈만 도입하면 됩니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

② 테스트 케이스 작성

테스트 객체 클래스

package com.example.demo.service;

import org.springframework.stereotype.Component;

@Component
public class Junit5Test {
    
    
    public int add(int i,int j){
    
    
        System.out.println("-----------add被执行了---------------");
        return i+j;
    }
    public int doAdd(int i,int j){
    
    
        System.out.println("------------doAdd被执行了--------------");
        //被mock的函数会先执行,且只会执行一次
        System.out.println(add(i,j));
        return add(i,j);
    }
}

테스트 케이스

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

//初始化一个spring的上下文,使其可以使用一些注入(junit5)。junit4会用runwith
@SpringBootTest
class Junit5TestTest {
    
    
    @Autowired
    Junit5Test junit5Test;
    //会初始化一次
    @BeforeAll
    static void init(){
    
    
        System.out.println("init");
    }
    //所有测试方法前都会执行一遍
    @BeforeEach
    void each(){
    
    
        System.out.println("each");
    }

    @Test
    void getDeviceStatistic() {
    
    
        Assertions.assertEquals(2,spyJunit5Test.doAdd(1,1));
    }
}

SpringBoot 컨텍스트가 필요한 경우 @SpringBootTest 주석만 추가하면 됩니다.물론 이전 프로젝트에서는 @RunWith(SpringRunner.class)를 볼 수 있습니다. 전자는 junit5의 쓰기 방식이고 후자는 junit4의 쓰기 방식입니다.

스프링 컨테이너에 테스트 개체가 필요할 때 정상적으로 주입하면 됩니다.

@Autowired
Junit5Test junit5Test;

3. Mock 데이터 - Mockito 프레임워크 사용

1.모의

실제 개발 및 단일 테스트에서는 테스트 객체가 네트워크 데이터를 요청하거나 데이터베이스를 변경해야 할 수 있지만 변경을 원하지 않을 수 있습니다.

소위 모의(mock)는 우리가 작성하는 코드가 일부 객체에 의존하고 이러한 객체가 수동으로 생성하기 어려운 경우(예: HttpRequest 및 기타 객체와 같은 초기화 방법 등을 모르는 경우) 가상 테스트 대상. 클래스 파일을 전달하기 때문에 정적 코드 블록은 계속 실행되지만 생성자 및 인스턴스 코드 블록은 실행되지 않습니다 .

2. 스텁 쌓기

테스트에 필요한 테스트 데이터를 제공하기 위해 소위 stubbing Stub을 사용하는데, Mock 객체이기 때문에 일부 메소드는 반환값을 모를 수 있으므로 반환값을 가정해야 한다. 다양한 상호 작용, 즉 when(…).thenReturn(…) 및 doReturn(…).when(…)을 사용하여 메서드 호출의 반환 값을 설정하기 위해 해당 응답을 설정할 수 있습니다.

예를 들어:


//You can mock concrete classes, not only interfaces
 LinkedList mockedList = mock(LinkedList.class);
 
 //stubbing
 when(mockedList.get(0)).thenReturn("first");
 when(mockedList.get(1)).thenThrow(new RuntimeException());
  • doReturn().when()에는 부작용이 없습니다. 쌓이는 동안 메소드가 실행되지 않습니다.
  • when().thenReturn()에는 부작용이 있습니다. 즉, 누적되는 동안 메서드가 먼저 실행되어 특정 부작용이 발생할 수 있습니다.

3. @MockBean 및 @SpyBean

물론 SpringBoot 환경에서는 @SpyBean과 @MockBean 어노테이션을 직접 사용하여 @Autowired의 주입된 객체를 대체하여 가상 객체가 존재하도록 할 수도 있다.

@MockBean @MockBean
만 사용하는 경우 수정된 객체가 조롱되어 Junit5Test의 add() 메소드가 더 이상 특정 세부사항을 실행하지 않지만 MockBean은 대상 객체의 모든 메소드를 조롱하므로 테스트를 수행할 수 없습니다. 실제로 실행됩니다. , 테스트할 수 없습니다.

@SpyBean
및 어떤 경우에는 실제 메소드를 실행해야 하며 일부 메소드만 조롱하고 싶은 경우 @SpyBean을 사용할 수 있습니다.
@SpyBean으로 장식된 spyJunit5Test는 실제 객체이며(spyJunit5Test.add(1,1)).thenReturn(2);, add 메소드가 스텁되고 다른 메소드가 계속 호출될 때만 가능합니다.

다음은 예입니다.

package com.example.demo.service;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

//初始化一个spring的上下文,使其可以使用一些注入(junit5)。junit4会用runwith
@SpringBootTest
class Junit5TestTest {
    
    
//    @Autowired
//    Junit5Test junit5Test;
    //介于@Autowired和@MockBean之间的注解,当配置了when时使用mock,没有进行打桩则走正常方法
    @SpyBean
    Junit5Test spyJunit5Test;
    //完全使用mock方法,方法都不会去真正执行。当调用mockBean修饰的方法时,不会去真正执行该方法,只会返回打桩后的值,如果没有打桩时会返回默认值,比如int就返回0。
//    @MockBean
//    Junit5Test mockJunit5Test;
    //会初始化一次
    @BeforeAll
    static void init(){
    
    
        System.out.println("init");
    }
    //所有测试方法前都会执行一遍
    @BeforeEach
    void each(){
    
    
        System.out.println("each");
    }

    @Test
    void getDeviceStatistic() {
    
    
        Assertions.assertEquals(1,1);
    }
    @Test
    void testDeviceStatisticByMock() {
    
    

        //配置mock
        when(spyJunit5Test.add(1,1)).thenReturn(2);
        Assertions.assertEquals(2,spyJunit5Test.doAdd(1,1));
    }
}

마지막으로 모두 행복한 프로그래머의 날이 되길 바랍니다!

추천

출처blog.csdn.net/qq_46101869/article/details/120942569