增加 后端,后台管理系统,uniapp会员端的自动化测试。

This commit is contained in:
2026-07-21 18:09:29 +08:00
parent df0e68469b
commit 6b60b3b4da
64 changed files with 13095 additions and 376 deletions
@@ -0,0 +1,246 @@
package cn.novalon.gym.manage.member.handler;
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.enums.CardEvent;
import cn.novalon.gym.manage.member.enums.MemberCardRecordStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.junit.jupiter.api.Assertions.*;
/**
* 会员卡状态机单元测试
*/
class MemberCardStateMachineTest {
private MemberCardStateMachine stateMachine;
@BeforeEach
void setUp() {
stateMachine = new MemberCardStateMachine();
}
// ======================= ACTIVE 状态的转换 =======================
@Test
@DisplayName("ACTIVE + USE = ACTIVE(继续使用)")
void testActiveUse() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.ACTIVE, CardEvent.USE);
StepVerifier.create(result)
.expectNext(MemberCardRecordStatus.ACTIVE)
.verifyComplete();
}
@Test
@DisplayName("ACTIVE + RENEW = ACTIVE(续费保持有效)")
void testActiveRenew() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.ACTIVE, CardEvent.RENEW);
StepVerifier.create(result)
.expectNext(MemberCardRecordStatus.ACTIVE)
.verifyComplete();
}
@Test
@DisplayName("ACTIVE + EXPIRE = EXPIRED(过期)")
void testActiveExpire() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.ACTIVE, CardEvent.EXPIRE);
StepVerifier.create(result)
.expectNext(MemberCardRecordStatus.EXPIRED)
.verifyComplete();
}
@Test
@DisplayName("ACTIVE + REFUND = REFUNDED(退款)")
void testActiveRefund() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.ACTIVE, CardEvent.REFUND);
StepVerifier.create(result)
.expectNext(MemberCardRecordStatus.REFUNDED)
.verifyComplete();
}
// ======================= USED_UP 状态的转换 =======================
@Test
@DisplayName("USED_UP + RENEW = ACTIVE(用完后续费恢复)")
void testUsedUpRenew() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.USED_UP, CardEvent.RENEW);
StepVerifier.create(result)
.expectNext(MemberCardRecordStatus.ACTIVE)
.verifyComplete();
}
@Test
@DisplayName("USED_UP + REFUND = REFUNDED(用完后退款)")
void testUsedUpRefund() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.USED_UP, CardEvent.REFUND);
StepVerifier.create(result)
.expectNext(MemberCardRecordStatus.REFUNDED)
.verifyComplete();
}
@Test
@DisplayName("USED_UP + USE = 非法(用完不能再使用)")
void testUsedUpUseInvalid() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.USED_UP, CardEvent.USE);
StepVerifier.create(result)
.expectError(IllegalStateException.class)
.verify();
}
// ======================= EXPIRED 状态的转换 =======================
@Test
@DisplayName("EXPIRED + RENEW = ACTIVE(过期后续费恢复)")
void testExpiredRenew() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.EXPIRED, CardEvent.RENEW);
StepVerifier.create(result)
.expectNext(MemberCardRecordStatus.ACTIVE)
.verifyComplete();
}
@Test
@DisplayName("EXPIRED + USE = 非法(过期不能使用)")
void testExpiredUseInvalid() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.EXPIRED, CardEvent.USE);
StepVerifier.create(result)
.expectError(IllegalStateException.class)
.verify();
}
// ======================= REFUNDED 状态(终态)=======================
@Test
@DisplayName("REFUNDED + RENEW = 非法(已退款终态)")
void testRefundedRenewInvalid() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.REFUNDED, CardEvent.RENEW);
StepVerifier.create(result)
.expectError(IllegalStateException.class)
.verify();
}
@Test
@DisplayName("REFUNDED + USE = 非法(已退款终态)")
void testRefundedUseInvalid() {
Mono<MemberCardRecordStatus> result = stateMachine.transition(
MemberCardRecordStatus.REFUNDED, CardEvent.USE);
StepVerifier.create(result)
.expectError(IllegalStateException.class)
.verify();
}
// ======================= canTransition 检查 =======================
@Test
@DisplayName("canTransition - ACTIVE可以RENEW")
void testCanTransitionActiveRenew() {
Mono<Boolean> result = stateMachine.canTransition(
MemberCardRecordStatus.ACTIVE, CardEvent.RENEW);
StepVerifier.create(result)
.expectNext(true)
.verifyComplete();
}
@Test
@DisplayName("canTransition - USED_UP不能USE")
void testCanTransitionUsedUpUse() {
Mono<Boolean> result = stateMachine.canTransition(
MemberCardRecordStatus.USED_UP, CardEvent.USE);
StepVerifier.create(result)
.expectNext(false)
.verifyComplete();
}
@Test
@DisplayName("canTransition - REFUNDED不能任何操作")
void testCanTransitionRefundedAny() {
Mono<Boolean> result = stateMachine.canTransition(
MemberCardRecordStatus.REFUNDED, CardEvent.RENEW);
StepVerifier.create(result)
.expectNext(false)
.verifyComplete();
}
// ======================= validateTransition 检查 =======================
@Test
@DisplayName("validateTransition - ACTIVE状态卡片使用通过")
void testValidateTransitionValid() {
MemberCardRecord card = MemberCardRecord.builder()
.memberCardRecordId(1L)
.status(MemberCardRecordStatus.ACTIVE)
.build();
Mono<Void> result = stateMachine.validateTransition(card, CardEvent.USE);
StepVerifier.create(result)
.verifyComplete();
}
@Test
@DisplayName("validateTransition - REFUNDED状态卡片拒绝所有操作")
void testValidateTransitionRefunded() {
MemberCardRecord card = MemberCardRecord.builder()
.memberCardRecordId(2L)
.status(MemberCardRecordStatus.REFUNDED)
.build();
Mono<Void> result = stateMachine.validateTransition(card, CardEvent.RENEW);
StepVerifier.create(result)
.expectError(IllegalStateException.class)
.verify();
}
// ======================= 枚举值验证 =======================
@Test
@DisplayName("MemberCardRecordStatus 枚举值完整")
void testStatusEnumValues() {
MemberCardRecordStatus[] values = MemberCardRecordStatus.values();
assertEquals(4, values.length);
assertEquals("有效", MemberCardRecordStatus.ACTIVE.getDesc());
assertEquals("用完", MemberCardRecordStatus.USED_UP.getDesc());
assertEquals("过期", MemberCardRecordStatus.EXPIRED.getDesc());
assertEquals("已退款", MemberCardRecordStatus.REFUNDED.getDesc());
}
@Test
@DisplayName("CardEvent 枚举值完整")
void testEventEnumValues() {
CardEvent[] values = CardEvent.values();
assertEquals(6, values.length);
assertEquals("激活卡片", CardEvent.ACTIVATE.getDesc());
assertEquals("使用卡片", CardEvent.USE.getDesc());
assertEquals("续费", CardEvent.RENEW.getDesc());
assertEquals("过期", CardEvent.EXPIRE.getDesc());
assertEquals("退款", CardEvent.REFUND.getDesc());
assertEquals("禁用", CardEvent.DISABLE.getDesc());
}
}
@@ -0,0 +1,138 @@
package cn.novalon.gym.manage.member.util;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* Bean转换工具类单元测试
*/
class BeanConvertUtilTest {
@Test
@DisplayName("toBean - 正常转换")
void testToBeanNormal() {
Source source = new Source();
source.setName("张三");
source.setAge(25);
source.setEmail("zhangsan@test.com");
Target target = BeanConvertUtil.toBean(source, Target.class);
assertNotNull(target);
assertEquals("张三", target.getName());
assertEquals(25, target.getAge());
assertEquals("zhangsan@test.com", target.getEmail());
}
@Test
@DisplayName("toBean - 部分字段转换")
void testToBeanPartial() {
Source source = new Source();
source.setName("李四");
source.setAge(30);
// email 不设置
Target target = BeanConvertUtil.toBean(source, Target.class);
assertNotNull(target);
assertEquals("李四", target.getName());
assertEquals(30, target.getAge());
assertNull(target.getEmail());
}
@Test
@DisplayName("toBean - null输入返回null")
void testToBeanNull() {
Target target = BeanConvertUtil.toBean(null, Target.class);
assertNull(target);
}
@Test
@DisplayName("toBeanList - 正常批量转换")
void testToBeanListNormal() {
Source s1 = new Source();
s1.setName("A"); s1.setAge(20);
Source s2 = new Source();
s2.setName("B"); s2.setAge(25);
Source s3 = new Source();
s3.setName("C"); s3.setAge(30);
List<Target> targets = BeanConvertUtil.toBeanList(
Arrays.asList(s1, s2, s3), Target.class);
assertEquals(3, targets.size());
assertEquals("A", targets.get(0).getName());
assertEquals(20, targets.get(0).getAge());
assertEquals("B", targets.get(1).getName());
assertEquals("C", targets.get(2).getName());
}
@Test
@DisplayName("toBeanList - null输入返回空列表")
void testToBeanListNull() {
List<Target> targets = BeanConvertUtil.toBeanList(null, Target.class);
assertNotNull(targets);
assertTrue(targets.isEmpty());
}
@Test
@DisplayName("toBeanList - 空列表输入返回空列表")
void testToBeanListEmpty() {
List<Target> targets = BeanConvertUtil.toBeanList(
Collections.emptyList(), Target.class);
assertNotNull(targets);
assertTrue(targets.isEmpty());
}
@Test
@DisplayName("toBeanList - 单元素列表")
void testToBeanListSingle() {
Source source = new Source();
source.setName("王五");
source.setAge(35);
List<Target> targets = BeanConvertUtil.toBeanList(
Collections.singletonList(source), Target.class);
assertEquals(1, targets.size());
assertEquals("王五", targets.get(0).getName());
assertEquals(35, targets.get(0).getAge());
}
// === 内部测试类 ===
public static class Source {
private String name;
private int age;
private String email;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
public static class Target {
private String name;
private int age;
private String email;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
}
@@ -0,0 +1,85 @@
package cn.novalon.gym.manage.member.util;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* 会员号生成器单元测试
*/
class MemberNoGeneratorTest {
@Test
@DisplayName("生成会员号 - 格式正确 GYM + 8位字符")
void testGenerateFormat() {
String memberNo = MemberNoGenerator.generate();
assertNotNull(memberNo);
assertEquals(11, memberNo.length());
assertTrue(memberNo.startsWith("GYM"));
// 后8位只包含合法字符
String suffix = memberNo.substring(3);
for (char c : suffix.toCharArray()) {
assertTrue("23456789ABCDEFGHJKLMNPQRSTUVWXYZ".indexOf(c) >= 0,
"非法字符: " + c);
}
}
@Test
@DisplayName("生成会员号 - 不包含易混淆字符")
void testGenerateNoAmbiguousChars() {
for (int i = 0; i < 50; i++) {
String memberNo = MemberNoGenerator.generate();
assertFalse(memberNo.contains("0"));
assertFalse(memberNo.contains("O"));
assertFalse(memberNo.contains("1"));
assertFalse(memberNo.contains("I"));
assertFalse(memberNo.contains("l"));
}
}
@RepeatedTest(10)
@DisplayName("生成会员号 - 高概率不重复")
void testGenerateUniqueness() {
String no1 = MemberNoGenerator.generate();
String no2 = MemberNoGenerator.generate();
// 极低概率重复(字符集30个字符取8位,总共 30^8 种组合)
assertNotEquals(no1, no2);
}
@Test
@DisplayName("批量生成 - 生成指定数量")
void testGenerateBatch() {
int count = 5;
String[] memberNos = MemberNoGenerator.generateBatch(count);
assertEquals(count, memberNos.length);
for (String no : memberNos) {
assertNotNull(no);
assertEquals(11, no.length());
assertTrue(no.startsWith("GYM"));
}
}
@Test
@DisplayName("批量生成 - 全部不重复")
void testGenerateBatchUnique() {
String[] memberNos = MemberNoGenerator.generateBatch(20);
for (int i = 0; i < memberNos.length; i++) {
for (int j = i + 1; j < memberNos.length; j++) {
assertNotEquals(memberNos[i], memberNos[j],
"重复会员号: " + memberNos[i]);
}
}
}
@Test
@DisplayName("批量生成 - count=0 返回空数组")
void testGenerateBatchZero() {
String[] memberNos = MemberNoGenerator.generateBatch(0);
assertEquals(0, memberNos.length);
}
}