本文目录导读:

我来为您提供一个完整的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自动化测试案例涵盖了:
- 单元测试:使用JUnit 5和AssertJ验证业务逻辑
- 集成测试:使用MockMvc测试REST API
- UI测试:使用Selenium WebDriver测试用户界面
- 测试报告:Allure或Surefire生成详细测试报告
您可以根据实际需求调整测试场景和用例,需要更多特定功能的示例吗?
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播