增加 后端,后台管理系统,uniapp会员端的自动化测试。
This commit is contained in:
+246
@@ -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());
|
||||
}
|
||||
}
|
||||
+138
@@ -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; }
|
||||
}
|
||||
}
|
||||
+85
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user