Java自动化测试案例

wen java案例 2

本文目录导读:

Java自动化测试案例

  1. 项目结构
  2. 依赖配置 (pom.xml)
  3. 业务代码
  4. 单元测试用例
  5. API 自动化测试
  6. UI 自动化测试(Selenium)
  7. 运行测试
  8. 测试报告示例

我来为您提供一个完整的Java自动化测试案例,涵盖单元测试、集成测试和UI自动化测试。

项目结构

src/
├── main/
│   └── java/
│       └── com/example/
│           ├── model/
│           │   └── User.java
│           ├── service/
│           │   ├── UserService.java
│           │   └── impl/
│           │       └── UserServiceImpl.java
│           └── controller/
│               └── UserController.java
└── test/
    └── java/
        └── com/example/
            ├── service/
            │   └── UserServiceTest.java
            ├── controller/
            │   └── UserControllerTest.java
            └── ui/
                └── UserLoginUITest.java

依赖配置 (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>automation-testing</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <junit.version>5.10.0</junit.version>
        <mockito.version>5.7.0</mockito.version>
        <selenium.version>4.15.0</selenium.version>
        <rest-assured.version>5.4.0</rest-assured.version>
        <spring-boot.version>2.7.17</spring-boot.version>
    </properties>
    <dependencies>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <!-- JUnit 5 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- Mockito -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- Selenium -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- REST Assured -->
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>${rest-assured.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- H2 Database for testing -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>2.2.224</version>
            <scope>test</scope>
        </dependency>
        <!-- AssertJ -->
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.24.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

业务代码

User.java

package com.example.model;
import java.time.LocalDateTime;
import java.util.Objects;
public class User {
    private Long id;
    private String username;
    private String email;
    private String password;
    private Integer age;
    private LocalDateTime createdAt;
    public User() {}
    public User(String username, String email, String password, Integer age) {
        this.username = username;
        this.email = email;
        this.password = password;
        this.age = age;
        this.createdAt = LocalDateTime.now();
    }
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) && 
               Objects.equals(username, user.username) && 
               Objects.equals(email, user.email);
    }
    @Override
    public int hashCode() {
        return Objects.hash(id, username, email);
    }
}

UserService.java

package com.example.service;
import com.example.model.User;
import java.util.List;
import java.util.Optional;
public interface UserService {
    User createUser(User user);
    Optional<User> getUserById(Long id);
    List<User> getAllUsers();
    User updateUser(Long id, User user);
    void deleteUser(Long id);
    boolean existsByEmail(String email);
}

UserServiceImpl.java

package com.example.service.impl;
import com.example.model.User;
import com.example.service.UserService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Service
public class UserServiceImpl implements UserService {
    private final ConcurrentHashMap<Long, User> userStore = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(1);
    @Override
    public User createUser(User user) {
        // 业务逻辑验证
        if (user.getUsername() == null || user.getUsername().trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        if (user.getEmail() == null || !user.getEmail().contains("@")) {
            throw new IllegalArgumentException("邮箱格式不正确");
        }
        if (user.getPassword() == null || user.getPassword().length() < 6) {
            throw new IllegalArgumentException("密码长度不能少于6位");
        }
        // 检查邮箱是否已存在
        if (existsByEmail(user.getEmail())) {
            throw new IllegalArgumentException("邮箱已被注册");
        }
        // 生成ID并存储
        user.setId(idGenerator.getAndIncrement());
        userStore.put(user.getId(), user);
        return user;
    }
    @Override
    public Optional<User> getUserById(Long id) {
        return Optional.ofNullable(userStore.get(id));
    }
    @Override
    public List<User> getAllUsers() {
        return new ArrayList<>(userStore.values());
    }
    @Override
    public User updateUser(Long id, User user) {
        User existingUser = userStore.get(id);
        if (existingUser == null) {
            throw new IllegalArgumentException("用户不存在");
        }
        user.setId(id);
        userStore.put(id, user);
        return user;
    }
    @Override
    public void deleteUser(Long id) {
        userStore.remove(id);
    }
    @Override
    public boolean existsByEmail(String email) {
        return userStore.values().stream()
                .anyMatch(user -> user.getEmail().equals(email));
    }
}

单元测试用例

UserServiceTest.java

package com.example.service;
import com.example.model.User;
import com.example.service.impl.UserServiceImpl;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.assertj.core.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
@DisplayName("用户服务单元测试")
class UserServiceTest {
    private UserService userService;
    @BeforeEach
    void setUp() {
        userService = new UserServiceImpl();
    }
    @AfterEach
    void tearDown() {
        userService = null;
    }
    @Nested
    @DisplayName("创建用户测试")
    class CreateUserTest {
        @Test
        @DisplayName("创建有效用户")
        void createValidUser() {
            // Arrange
            User user = new User("john_doe", "john@example.com", "password123", 25);
            // Act
            User createdUser = userService.createUser(user);
            // Assert
            assertAll("验证用户创建成功",
                () -> assertNotNull(createdUser.getId(), "用户ID不应为空"),
                () -> assertEquals("john_doe", createdUser.getUsername(), "用户名不匹配"),
                () -> assertEquals("john@example.com", createdUser.getEmail(), "邮箱不匹配"),
                () -> assertNotNull(createdUser.getCreatedAt(), "创建时间不应为空")
            );
        }
        @Test
        @DisplayName("创建用户时用户名为空")
        void createUserWithEmptyUsername() {
            // Arrange
            User user = new User("", "john@example.com", "password123", 25);
            // Act & Assert
            IllegalArgumentException exception = assertThrows(
                IllegalArgumentException.class,
                () -> userService.createUser(user),
                "应该抛出异常"
            );
            assertEquals("用户名不能为空", exception.getMessage());
        }
        @Test
        @DisplayName("创建用户时邮箱格式不正确")
        void createUserWithInvalidEmail() {
            // Arrange
            User user = new User("john", "invalid-email", "password123", 25);
            // Act & Assert
            assertThatThrownBy(() -> userService.createUser(user))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("邮箱格式不正确");
        }
        @Test
        @DisplayName("创建用户时密码太短")
        void createUserWithShortPassword() {
            // Arrange
            User user = new User("john", "john@example.com", "123", 25);
            // Act & Assert
            IllegalArgumentException exception = assertThrows(
                IllegalArgumentException.class,
                () -> userService.createUser(user)
            );
            assertEquals("密码长度不能少于6位", exception.getMessage());
        }
        @Test
        @DisplayName("创建重复邮箱用户")
        void createDuplicateEmail() {
            // Arrange
            User user1 = new User("john", "john@example.com", "password123", 25);
            User user2 = new User("jane", "john@example.com", "password456", 30);
            userService.createUser(user1);
            // Act & Assert
            assertThatThrownBy(() -> userService.createUser(user2))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("邮箱已被注册");
        }
    }
    @Nested
    @DisplayName("查询用户测试")
    class GetUserTest {
        @Test
        @DisplayName("按ID查询存在的用户")
        void getUserById() {
            // Arrange
            User user = new User("john", "john@example.com", "password123", 25);
            User created = userService.createUser(user);
            // Act
            User found = userService.getUserById(created.getId()).orElse(null);
            // Assert
            assertNotNull(found);
            assertEquals(created.getId(), found.getId());
            assertEquals("john", found.getUsername());
        }
        @Test
        @DisplayName("查询不存在的用户")
        void getUserByIdNotFound() {
            // Act
            var result = userService.getUserById(999L);
            // Assert
            assertTrue(result.isEmpty(), "应该返回空Optional");
        }
        @Test
        @DisplayName("获取所有用户")
        void getAllUsers() {
            // Arrange
            User user1 = new User("john", "john@example.com", "password123", 25);
            User user2 = new User("jane", "jane@example.com", "password456", 30);
            userService.createUser(user1);
            userService.createUser(user2);
            // Act
            var users = userService.getAllUsers();
            // Assert
            assertEquals(2, users.size(), "应该有2个用户");
            assertThat(users)
                .extracting(User::getUsername)
                .containsExactlyInAnyOrder("john", "jane");
        }
    }
    @Nested
    @DisplayName("更新和删除用户测试")
    class UpdateAndDeleteUserTest {
        @Test
        @DisplayName("更新存在的用户")
        void updateUser() {
            // Arrange
            User user = new User("john", "john@example.com", "password123", 25);
            User created = userService.createUser(user);
            User updatedData = new User("john_updated", "john_updated@example.com", "newpassword123", 30);
            // Act
            User updated = userService.updateUser(created.getId(), updatedData);
            // Assert
            assertAll(
                () -> assertEquals(created.getId(), updated.getId(), "ID应该保持不变"),
                () -> assertEquals("john_updated", updated.getUsername(), "用户名应该已更新"),
                () -> assertEquals("john_updated@example.com", updated.getEmail(), "邮箱应该已更新")
            );
        }
        @Test
        @DisplayName("更新不存在的用户")
        void updateNonExistentUser() {
            // Arrange
            User user = new User("john", "john@example.com", "password123", 25);
            // Act & Assert
            assertThatThrownBy(() -> userService.updateUser(999L, user))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("用户不存在");
        }
        @Test
        @DisplayName("删除用户")
        void deleteUser() {
            // Arrange
            User user = new User("john", "john@example.com", "password123", 25);
            User created = userService.createUser(user);
            // Act
            userService.deleteUser(created.getId());
            // Assert
            assertTrue(userService.getUserById(created.getId()).isEmpty(), "删除后用户应该不存在");
        }
    }
}

API 自动化测试

UserControllerTest.java

package com.example.controller;
import com.example.model.User;
import com.example.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.*;
@SpringBootTest
@AutoConfigureMockMvc
@DisplayName("用户控制器API测试")
class UserControllerTest {
    @Autowired
    private MockMvc mockMvc;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private UserService userService;
    @Test
    @DisplayName("POST /api/users - 创建用户")
    void createUser() throws Exception {
        // Arrange
        User user = new User("testuser", "test@example.com", "password123", 25);
        // Act & Assert
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(user)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.username", is("testuser")))
            .andExpect(jsonPath("$.email", is("test@example.com")))
            .andExpect(jsonPath("$.id", notNullValue()));
    }
    @Test
    @DisplayName("GET /api/users/{id} - 获取用户")
    void getUserById() throws Exception {
        // Arrange
        User user = new User("testuser", "test@example.com", "password123", 25);
        User created = userService.createUser(user);
        // Act & Assert
        mockMvc.perform(get("/api/users/{id}", created.getId()))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.username", is("testuser")))
            .andExpect(jsonPath("$.email", is("test@example.com")));
    }
    @Test
    @DisplayName("GET /api/users/{id} - 用户不存在")
    void getUserByIdNotFound() throws Exception {
        mockMvc.perform(get("/api/users/{id}", 999L))
            .andExpect(status().isNotFound());
    }
    @Test
    @DisplayName("DELETE /api/users/{id} - 删除用户")
    void deleteUser() throws Exception {
        // Arrange
        User user = new User("testuser", "test@example.com", "password123", 25);
        User created = userService.createUser(user);
        // Act & Assert
        mockMvc.perform(delete("/api/users/{id}", created.getId()))
            .andExpect(status().isNoContent());
    }
}

UI 自动化测试(Selenium)

UserLoginUITest.java

package com.example.ui;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
import java.util.List;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@DisplayName("用户登录界面自动化测试")
class UserLoginUITest {
    private WebDriver driver;
    private WebDriverWait wait;
    @BeforeAll
    void setUpAll() {
        // 使用WebDriverManager自动管理驱动
        WebDriverManager.chromedriver().setup();
    }
    @BeforeEach
    void setUp() {
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless"); // 无头模式
        options.addArguments("--disable-gpu");
        options.addArguments("--no-sandbox");
        driver = new ChromeDriver(options);
        wait = new WebDriverWait(driver, Duration.ofSeconds(10));
        // 设置浏览器大小
        driver.manage().window().maximize();
    }
    @AfterEach
    void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
    @Test
    @DisplayName("用户登录成功")
    void testSuccessfulLogin() {
        // 导航到登录页面
        driver.get("http://localhost:8080/login");
        // 填写登录表单
        WebElement usernameInput = wait.until(
            ExpectedConditions.presenceOfElementLocated(By.id("username"))
        );
        usernameInput.sendKeys("testuser");
        WebElement passwordInput = driver.findElement(By.id("password"));
        passwordInput.sendKeys("password123");
        // 提交表单
        WebElement submitButton = driver.findElement(By.id("login-button"));
        submitButton.click();
        // 等待跳转并验证成功消息
        WebElement successMessage = wait.until(
            ExpectedConditions.presenceOfElementLocated(By.className("success-message"))
        );
        Assertions.assertTrue(
            successMessage.getText().contains("登录成功"),
            "应该显示登录成功消息"
        );
    }
    @Test
    @DisplayName("用户登录失败-密码错误")
    void testLoginFailureWithWrongPassword() {
        // 导航到登录页面
        driver.get("http://localhost:8080/login");
        // 填写表单
        WebElement usernameInput = driver.findElement(By.id("username"));
        usernameInput.sendKeys("testuser");
        WebElement passwordInput = driver.findElement(By.id("password"));
        passwordInput.sendKeys("wrongpassword");
        // 提交
        driver.findElement(By.id("login-button")).click();
        // 验证错误消息
        WebElement errorMessage = wait.until(
            ExpectedConditions.presenceOfElementLocated(By.className("error-message"))
        );
        Assertions.assertTrue(
            errorMessage.isDisplayed(),
            "应该显示错误消息"
        );
        List<WebElement> errorMessages = driver.findElements(By.className("error-message"));
        Assertions.assertEquals(1, errorMessages.size(), "应该只有一个错误消息");
    }
    @Test
    @DisplayName("登录页面表单验证")
    void testLoginFormValidation() {
        driver.get("http://localhost:8080/login");
        // 不填写用户名和密码,直接点击登录
        driver.findElement(By.id("login-button")).click();
        // 等待并在表单验证错误
        List<WebElement> validationErrors = wait.until(
            ExpectedConditions.presenceOfAllElementsLocatedBy(
                By.cssSelector(".validation-error")
            )
        );
        Assertions.assertTrue(
            validationErrors.size() >= 2,
            "应该至少有两个验证错误(用户名和密码)"
        );
        // 验证具体错误消息
        WebElement usernameError = driver.findElement(By.id("username-error"));
        Assertions.assertEquals(
            "请输入用户名",
            usernameError.getText(),
            "应该显示用户名验证错误"
        );
        WebElement passwordError = driver.findElement(By.id("password-error"));
        Assertions.assertEquals(
            "请输入密码",
            passwordError.getText(),
            "应该显示密码验证错误"
        );
    }
    @Test
    @DisplayName("登录页面UI元素验证")
    void testLoginPageElements() {
        driver.get("http://localhost:8080/login");
        // 验证页面标题
        Assertions.assertEquals(
            "系统登录",
            driver.getTitle(),
            "页面标题应该正确"
        );
        // 验证表单元素
        WebElement form = driver.findElement(By.id("login-form"));
        Assertions.assertTrue(form.isDisplayed(), "登录表单应该可见");
        // 验证表单字段
        WebElement usernameInput = driver.findElement(By.id("username"));
        Assertions.assertTrue(usernameInput.isEnabled(), "用户名输入框应该可用");
        Assertions.assertEquals("text", usernameInput.getAttribute("type"));
        Assertions.assertTrue(usernameInput.getAttribute("required") != null);
        WebElement passwordInput = driver.findElement(By.id("password"));
        Assertions.assertEquals("password", passwordInput.getAttribute("type"));
        Assertions.assertTrue(passwordInput.getAttribute("required") != null);
        // 验证登录按钮样式
        WebElement loginButton = driver.findElement(By.id("login-button"));
        Assertions.assertEquals("submit", loginButton.getAttribute("type"));
        Assertions.assertTrue(loginButton.getAttribute("class").contains("primary"));
    }
    @Test
    @DisplayName("快速输入密码显示/隐藏测试")
    void testPasswordVisibilityToggle() {
        driver.get("http://localhost:8080/login");
        // 输入密码
        WebElement passwordInput = driver.findElement(By.id("password"));
        passwordInput.sendKeys("secretpassword");
        // 验证密码字段是密码类型
        Assertions.assertEquals(
            "password",
            passwordInput.getAttribute("type"),
            "初始应该是密码类型"
        );
        // 点击显示/隐藏按钮
        WebElement toggleButton = driver.findElement(By.id("toggle-password"));
        toggleButton.click();
        // 验证密码字段变为文本类型
        String typeAfterToggle = passwordInput.getAttribute("type");
        Assertions.assertEquals(
            "text",
            typeAfterToggle,
            "点击后应该是文本类型"
        );
        // 验证值保持不变
        Assertions.assertEquals(
            "secretpassword",
            passwordInput.getAttribute("value"),
            "密码值应该保持不变"
        );
    }
}

运行测试

Maven 命令

# 运行所有测试
mvn test
# 运行指定测试类
mvn test -Dtest=UserServiceTest
# 运行指定测试方法
mvn test -Dtest=UserServiceTest#createValidUser
# 运行特定包的测试
mvn test -Dtest="com.example.service.*"
# 跳过测试
mvn clean install -DskipTests
# 生成测试报告
mvn surefire-report:report

测试配置 (test/resources/application.yml)

spring:
  datasource:
    url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
    driver-class-name: org.h2.Driver
    username: sa
    password: 
  h2:
    console:
      enabled: true
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: create-drop
server:
  port: 0  # 随机端口
logging:
  level:
    org.springframework: INFO
    com.example: DEBUG

测试报告示例

JUnit 测试报告

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.service.UserServiceTest
[INFO] Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123s
[INFO] Running com.example.controller.UserControllerTest
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.456s
[INFO] Running com.example.ui.UserLoginUITest
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 15.234s
[INFO] -------------------------------------------------------
[INFO] Results:
[INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0
[INFO] -------------------------------------------------------
[INFO] BUILD SUCCESS

这个完整的Java自动化测试案例涵盖了:

  1. 单元测试:使用JUnit 5和AssertJ验证业务逻辑
  2. 集成测试:使用MockMvc测试REST API
  3. UI测试:使用Selenium WebDriver测试用户界面
  4. 测试报告:Allure或Surefire生成详细测试报告

您可以根据实际需求调整测试场景和用例,需要更多特定功能的示例吗?

上一篇REST Assured案例

下一篇JaCoCo案例

抱歉,评论功能暂时关闭!