테스트 유형
테스트란 무엇입니까? Wiki에서는 다음과 같이 말합니다. " 테스트 또는 테스트는 시스템을 다양한 상황에 배치하고 관찰 가능한 변경 사항을 추적하여 시스템의 기본 프로세스를 연구하는 방법입니다." 즉, 이는 특정 상황에서 시스템이 올바르게 작동하는지 테스트하는 것입니다. 그럼 어떤 유형의 테스트가 있는지 살펴보겠습니다.-
단위 테스트는 시스템의 각 모듈을 개별적으로 테스트하는 테스트입니다. 이는 모듈과 같이 시스템에서 최소한으로 분할 가능한 부분인 것이 바람직합니다.
-
시스템 테스트는 애플리케이션의 더 큰 부분이나 시스템 전체의 작동을 테스트하는 높은 수준의 테스트입니다.
-
회귀 테스트는 새로운 기능이나 버그 수정이 애플리케이션의 기존 기능에 영향을 미치는지, 오래된 버그가 다시 나타나는지 확인하는 데 사용되는 테스트입니다.
-
기능 테스트는 애플리케이션의 일부가 사양, 사용자 스토리 등에 명시된 요구 사항을 준수하는지 확인하는 것입니다.
기능 테스트 유형:
- 시스템의 내부 구현에 대한 지식을 바탕으로 애플리케이션의 일부가 요구 사항을 준수하는지 확인하는 "화이트 박스" 테스트
- 시스템의 내부 구현에 대한 지식 없이 애플리케이션의 일부가 요구 사항을 준수하는지 확인하는 "블랙 박스" 테스트입니다 .
- 성능 테스트는 특정 부하에서 시스템 또는 그 일부가 실행되는 속도를 결정하기 위해 작성된 테스트 유형입니다.
- 부하 테스트 - 표준 부하 하에서 시스템의 안정성을 확인하고 애플리케이션이 올바르게 작동하는 최대 피크를 찾기 위해 고안된 테스트입니다.
- 스트레스 테스트는 비표준 부하에서 응용 프로그램의 기능을 확인하고 시스템이 충돌하지 않는 최대 피크를 결정하기 위해 설계된 테스트 유형입니다.
- 보안 테스트 - 시스템 보안(해커, 바이러스 공격, 기밀 데이터에 대한 무단 액세스 및 기타 삶의 즐거움)을 확인하는 데 사용되는 테스트입니다.
- 현지화 테스트는 애플리케이션에 대한 현지화 테스트입니다.
- 사용성 테스트는 사용자의 사용성, 이해성, 매력, 학습성을 확인하는 것을 목표로 하는 테스트 유형입니다. 이 모든 것이 듣기에는 좋지만 실제로는 어떻게 작동합니까? 간단합니다. Mike Cohn의 테스트 피라미드가 사용됩니다. 이것은 피라미드의 단순화된 버전입니다. 이제 더 작은 부분으로 나뉩니다. 그러나 오늘 우리는 변태하지 않고 가장 간단한 옵션을 고려할 것입니다.
-
단위 - 응용 프로그램의 다양한 계층에서 사용되는 단위 테스트로, 응용 프로그램의 가장 작은 분할 가능한 논리(예: 클래스, 그러나 대부분 메서드)를 테스트합니다. 이러한 테스트는 일반적으로 외부 로직에서 최대한 격리하려고 합니다. 즉, 애플리케이션의 나머지 부분이 표준 모드에서 작동하는 것처럼 보이게 하기 위한 것입니다.
이러한 테스트는 작은 부분을 테스트하고 매우 가벼우며 많은 리소스를 소비하지 않기 때문에(다른 유형보다 더 많이) 항상 많은 테스트가 있어야 합니다(리소스란 RAM과 시간을 의미함).
-
통합 - 통합 테스트. 이는 시스템의 더 큰 부분, 즉 여러 논리 부분(여러 메서드 또는 클래스)의 조합인지 또는 외부 구성 요소 작업의 정확성인지 확인합니다. 일반적으로 이러한 테스트는 단위 테스트보다 더 무겁기 때문에 개수가 적습니다.
통합 테스트의 예로, 데이터베이스에 연결하고 데이터베이스와 함께 작동하는 메서드가 올바르게 실행되는지 확인하는 것을 고려할 수 있습니다 .
-
UI - 사용자 인터페이스의 작동을 확인하는 테스트입니다. 이는 애플리케이션의 모든 수준에서 논리에 영향을 미치므로 엔드투엔드(end-to-end)라고도 합니다. 일반적으로 해당 경로는 가장 무겁고 가장 필요한(사용되는) 경로를 확인해야 하기 때문에 그 수가 훨씬 적습니다.
위 그림에서 우리는 삼각형의 여러 부분의 면적 비율을 볼 수 있습니다. 실제 작업에서 이러한 테스트 횟수는 거의 동일한 비율로 유지됩니다.
오늘은 가장 많이 사용되는 테스트인 단위 테스트에 대해 자세히 살펴보겠습니다. 모든 자존심 있는 Java 개발자는 기본 수준에서 단위 테스트를 사용할 수 있어야 하기 때문입니다.
- 우리는 테스트를 작성하고 있습니다.
- 우리는 통과 여부에 관계없이 테스트를 실행합니다(모든 것이 빨간색임을 확인합니다. 놀라지 마세요. 이렇게 되어야 합니다).
- 이 테스트를 만족해야 하는 코드를 추가합니다(테스트 실행).
- 우리는 코드를 리팩토링합니다.
- 테스트할 데이터(픽스처) 지정.
- 테스트 중인 코드 사용(테스트 중인 메서드 호출)
- 결과를 확인하고 예상한 결과와 비교합니다.
assertEquals(Object expecteds, Object actuals)
— 전송된 객체가 동일한지 확인합니다.assertTrue(boolean flag)
— 전달된 값이 true를 반환하는지 확인합니다.assertFalse(boolean flag)
— 전달된 값이 false를 반환하는지 확인합니다.assertNull(Object object)
– 객체가 null인지 확인합니다.assertSame(Object firstObject, Object secondObject)
— 전달된 값이 동일한 객체를 참조하는지 확인합니다.assertThat(T t, Matcher<T> matcher)
— t가 매처에 지정된 조건을 만족하는지 확인합니다.
단위 테스트의 주요 개념
테스트 커버리지 (코드 커버리지)는 애플리케이션 테스트 품질에 대한 주요 평가 중 하나입니다. 테스트에 포함된 코드의 비율입니다(0-100%). 실제로 많은 사람들은 필요하지 않은 곳에 테스트를 추가하기 시작하기 때문에 동의하지 않는 이 비율을 쫓습니다. 예를 들어, 우리 서비스에는 추가 논리 없이 표준 CRUD(생성/가져오기/업데이트/삭제) 작업이 있습니다. 이러한 방법은 저장소와 함께 작동하는 레이어에 작업을 위임하는 순전히 중개자입니다. 이 상황에서 우리는 테스트할 것이 없습니다. 아마도 이 메소드가 Tao의 메소드를 호출하는지 여부일 수도 있지만 이는 심각하지 않습니다. 테스트 적용 범위를 평가하기 위해 일반적으로 JaCoCo, Cobertura, Clover, Emma 등의 추가 도구가 사용됩니다. 이 문제에 대한 더 자세한 연구를 위해 몇 가지 적절한 기사를 보관하십시오. TDD (테스트 중심 개발) - 테스트 중심 개발. 이 접근 방식에서는 먼저 특정 코드를 확인하는 테스트가 작성됩니다. 이는 블랙박스 테스트로 밝혀졌습니다. 우리는 입력에 무엇이 있는지 알고 출력에 어떤 일이 일어나야 하는지 알고 있습니다. 이렇게 하면 코드 중복이 방지됩니다. 테스트 중심 개발은 애플리케이션의 작은 기능 각각에 대한 테스트를 설계하고 개발하는 것부터 시작됩니다. TDD 접근 방식에서는 먼저 코드가 수행할 작업을 정의하고 확인하는 테스트가 개발됩니다. TDD의 주요 목표는 코드를 더 명확하고 단순하며 오류 없이 만드는 것입니다. 이 접근 방식은 다음 구성 요소로 구성됩니다.테스트 단계
테스트는 세 단계로 구성됩니다.테스트 환경
이제 사업을 시작하겠습니다. Java에 사용할 수 있는 여러 테스트 환경(프레임워크)이 있습니다. 그 중 가장 인기 있는 것은 JUnit과 TestNG입니다. 검토를 위해 다음을 사용합니다. JUnit 테스트는 테스트에만 사용되는 클래스에 포함된 메서드입니다. 클래스 이름은 일반적으로 끝에 +Test를 붙여 테스트 중인 클래스와 동일하게 지정됩니다. 예를 들어 CarService→ CarServiceTest입니다. Maven 빌드 시스템은 테스트 영역에 이러한 클래스를 자동으로 포함합니다. 실제로 이 클래스를 테스트 클래스라고 합니다. 기본 주석을 조금 살펴보겠습니다. @Test - 이 메서드를 테스트 메서드로 정의합니다(사실 이 주석으로 표시된 메서드는 단위 테스트입니다). @Before - 각 테스트 전에 실행될 메서드를 표시합니다. 예를 들어 클래스 테스트 데이터 채우기, 입력 데이터 읽기 등 @After - 각 테스트 후에 호출될 메서드 위에 배치됩니다(데이터 정리, 기본값 복원). @BeforeClass - 메소드 위에 위치하며 @Before와 유사합니다. 그러나 이 메서드는 특정 클래스에 대한 모든 테스트 전에 한 번만 호출되므로 정적이어야 합니다. 테스트 데이터베이스를 들어 올리는 등 보다 강력한 작업을 수행하는 데 사용됩니다. @AfterClass 는 @BeforeClass와 반대입니다. 특정 클래스에 대해 한 번 실행되지만 모든 테스트 후에 실행됩니다. 예를 들어 영구 리소스를 정리하거나 데이터베이스 연결을 끊는 데 사용됩니다. @Ignore - 아래 메서드는 비활성화되어 있으며 전체 테스트를 실행할 때 무시됩니다. 예를 들어 기본 방법이 변경되어 테스트를 다시 실행할 시간이 없는 경우와 같이 다양한 경우에 사용됩니다. 이러한 경우에는 설명(@Ignore("Some Description"))을 추가하는 것이 좋습니다. @Test (예상 = Exception.class) - 부정적인 테스트에 사용됩니다. 이는 오류가 발생한 경우 메서드가 어떻게 작동하는지 확인하는 테스트입니다. 즉, 테스트에서는 메서드가 일부 예외를 throw할 것으로 예상합니다. 이러한 메서드는 @Test 주석으로 표시되지만 오류가 발생합니다. @Test(timeout=100) - 메서드가 100밀리초 이내에 실행되는지 확인합니다. @Mock - 클래스는 주어진 객체를 모의 개체로 설정하기 위해 필드 위에 사용되며(이것은 Junit 라이브러리가 아니라 Mockito에서 가져온 것임) 필요한 경우 특정 상황에서 모의 개체의 동작을 설정합니다. , 테스트 방법에서 직접. @RunWith(MockitoJUnitRunner.class) - 메서드가 클래스 위에 배치됩니다. 테스트를 실행하기 위한 버튼입니다. 실행기는 다를 수 있습니다. 예를 들어 MockitoJUnitRunner, JUnitPlatform, SpringRunner 등이 있습니다. JUnit 5에서는 @RunWith 주석이 더 강력한 @ExtendWith 주석으로 대체되었습니다. 결과를 비교하는 몇 가지 방법을 살펴보겠습니다.assertThat(firstObject).isEqualTo(secondObject)
나머지는 위의 다른 변형이므로 여기서는 기본 방법에 대해 설명했습니다.
테스트 실습
이제 구체적인 예를 사용하여 위 자료를 살펴보겠습니다. 서비스 업데이트 방법을 테스트하겠습니다. dao 레이어는 기본값이므로 고려하지 않습니다. 테스트용 스타터를 추가해 보겠습니다.<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
따라서 서비스 클래스는 다음과 같습니다.
@Service
@RequiredArgsConstructor
public class RobotServiceImpl implements RobotService {
private final RobotDAO robotDAO;
@Override
public Robot update(Long id, Robot robot) {
Robot found = robotDAO.findById(id);
return robotDAO.update(Robot.builder()
.id(id)
.name(robot.getName() != null ? robot.getName() : found.getName())
.cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
.producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
.build());
}
}
8 - 데이터베이스에서 업데이트된 객체 가져오기 9-14 - 수신 객체에 필드가 있는 경우 빌더를 통해 객체 생성 - 필드가 없으면 설정 - 데이터베이스에 있는 내용 그대로 두고 테스트를 살펴보세요.
@RunWith(MockitoJUnitRunner.class)
public class RobotServiceImplTest {
@Mock
private RobotDAO robotDAO;
private RobotServiceImpl robotService;
private static Robot testRobot;
@BeforeClass
public static void prepareTestData() {
testRobot = Robot
.builder()
.id(123L)
.name("testRobotMolly")
.cpu("Intel Core i7-9700K")
.producer("China")
.build();
}
@Before
public void init() {
robotService = new RobotServiceImpl(robotDAO);
}
1 - Runner 4 - 모의 객체를 대체하여 dao 레이어에서 서비스를 분리합니다. 11 - 클래스에 대한 테스트 엔터티 설정(테스트 햄스터로 사용할 엔터티) 22 - 테스트할 서비스 객체 설정
@Test
public void updateTest() {
when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
Robot robotForUpdate = Robot
.builder()
.name("Vally")
.cpu("AMD Ryzen 7 2700X")
.build();
Robot resultRobot = robotService.update(123L, robotForUpdate);
assertNotNull(resultRobot);
assertSame(resultRobot.getId(),testRobot.getId());
assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
assertEquals(resultRobot.getProducer(),testRobot.getProducer());
}
여기에서는 테스트가 세 부분으로 명확하게 구분되어 있습니다. 3-9 - 고정 장치 설정 11 - 테스트된 부분 실행 13-17 - 결과 확인 자세한 내용: 3-4 - moka dao에 대한 동작 설정 5 - 설정 표준 위에 업데이트할 인스턴스 11 - 메서드를 사용하고 결과 인스턴스 가져오기 13 - 0이 아닌지 확인 14 - 결과 ID와 지정된 메서드 인수 확인 15 - 이름이 업데이트되었는지 확인 16 - CPU 17로 결과를 살펴봅니다. - 업데이트 인스턴스 필드에 이를 설정하지 않았으므로 동일하게 유지되어야 합니다. 확인해 보겠습니다. 시작합시다: 테스트는 녹색입니다. 숨을 내쉴 수 있습니다.) 요약하자면, 테스트는 코드 품질을 향상시키고 개발 프로세스를 더욱 유연하고 안정적으로 만듭니다. 수백 개의 클래스 파일로 소프트웨어를 재설계할 때 얼마나 많은 노력을 기울여야 하는지 상상해 보십시오. 이러한 모든 클래스에 대해 단위 테스트를 작성하면 자신감을 갖고 리팩토링할 수 있습니다. 그리고 가장 중요한 것은 개발 중에 오류를 쉽게 찾는 데 도움이 된다는 것입니다. 여러분, 오늘은 그게 전부입니다. 좋아요를 붓고 댓글을 작성하세요)))
GO TO FULL VERSION