×
¥
查看详情
🔥 会员专享 文生代码 其它

单元测试生成

👁️ 575 次查看
📅 Nov 24, 2025
💡 核心价值: 一键生成全面单元测试,覆盖所有场景,代码更健壮,测试更高效!

🎯 可自定义参数(5个)

测试框架
用于编写单元测试的测试框架
编程语言
编写单元测试的编程语言
函数/类定义
需要生成单元测试的目标函数或类定义代码
正常路径场景描述
描述函数或类在正常情况下的核心功能流程
边界与异常场景描述
描述函数或类在边界条件及可能异常情况下的测试场景

🎨 效果示例

请将以下导入语句中的模块名替换为实际代码所在模块

例如:from my_module import calculate_order_total

from decimal import Decimal import pytest

from target_module import calculate_order_total # 修改为你的模块名

def test_typical_order_percent_discount_with_cap_and_tax_returns_decimal_and_correct_total(): items = [ {'price': '30', 'qty': 2}, # subtotal = 60 ] discount = {'type': 'percent', 'value': '0.10', 'cap': Decimal('5')} # 10% capped at 5 -> discount = 5 tax_rate = Decimal('0.07') # 7%

total = calculate_order_total(items, discount=discount, tax_rate=tax_rate)

# taxed_base = 60 - 5 = 55; tax = 55 * 0.07 = 3.85; total = 58.85 -> Decimal rounded to 2 places
assert isinstance(total, Decimal)
assert total == Decimal('58.85')

def test_empty_cart_returns_zero_decimal_two_places(): total = calculate_order_total([], discount=None, tax_rate=Decimal('0')) assert isinstance(total, Decimal) assert total == Decimal('0.00')

def test_tax_rate_boundary_zero_no_tax(): items = [{'price': '10', 'qty': 2}] # subtotal = 20 total = calculate_order_total(items, tax_rate=Decimal('0')) assert total == Decimal('20.00')

def test_tax_rate_boundary_one_full_tax_on_discounted_base(): items = [{'price': 10, 'qty': 1}] # subtotal = 10 discount = {'type': 'fixed', 'value': 2} # taxed_base = 8 total = calculate_order_total(items, discount=discount, tax_rate=Decimal('1.0')) # total = 8 + 8 = 16 assert total == Decimal('16.00')

def test_fixed_discount_exceeds_subtotal_caps_to_zero_total_even_with_tax(): items = [{'price': '12', 'qty': 1}] # subtotal = 12 discount = {'type': 'fixed', 'value': '100'} # discount capped to subtotal -> taxed_base = 0 total = calculate_order_total(items, discount=discount, tax_rate=Decimal('0.07')) assert total == Decimal('0.00')

def test_negative_price_raises_value_error(): items = [{'price': -1, 'qty': 1}] with pytest.raises(ValueError, match='价格与数量需非负'): calculate_order_total(items)

def test_negative_quantity_raises_value_error(): items = [{'price': '1.00', 'qty': -2}] with pytest.raises(ValueError, match='价格与数量需非负'): calculate_order_total(items)

def test_tax_rate_below_zero_raises_value_error(): items = [{'price': '1', 'qty': 1}] with pytest.raises(ValueError, match='税率需在0到1之间'): calculate_order_total(items, tax_rate=Decimal('-0.01'))

def test_tax_rate_above_one_raises_value_error(): items = [{'price': '1', 'qty': 1}] with pytest.raises(ValueError, match='税率需在0到1之间'): calculate_order_total(items, tax_rate=Decimal('1.0001'))

def test_unknown_discount_type_raises_value_error(): items = [{'price': '10', 'qty': 1}] discount = {'type': 'bogus', 'value': '0.1'} with pytest.raises(ValueError, match='未知折扣类型'): calculate_order_total(items, discount=discount)

def test_prices_and_discount_accept_float_and_string_safely_converted_to_decimal(): items = [ {'price': 19.9, 'qty': 1}, # float price {'price': '10.10', 'qty': 2}, # string price ] discount = {'type': 'percent', 'value': '0.1'} # string percent tax_rate = 0.075 # float tax rate

total = calculate_order_total(items, discount=discount, tax_rate=tax_rate)
# subtotal = 19.9 + 20.2 = 40.1
# discount = 40.1 * 0.1 = 4.01
# taxed_base = 36.09
# tax = 36.09 * 0.075 = 2.70675
# total = 38.79675 -> ROUND_HALF_UP to 38.80
assert total == Decimal('38.80')

def test_discount_cap_negative_raises_value_error(): items = [{'price': '10', 'qty': 1}] discount = {'type': 'percent', 'value': '0.1', 'cap': '-1'} with pytest.raises(ValueError, match='折扣封顶需非负'): calculate_order_total(items, discount=discount)

def test_rounding_half_up_to_two_decimal_places(): items = [{'price': '0.01', 'qty': 1}] # subtotal = 0.01 # tax = 0.01 * 0.5 = 0.005; total = 0.015 -> ROUND_HALF_UP -> 0.02 total = calculate_order_total(items, tax_rate=Decimal('0.5')) assert total == Decimal('0.02')

@pytest.mark.parametrize("bad_item", [ {'qty': 1}, # missing price {'price': '1.00'}, # missing qty ]) def test_item_missing_price_or_qty_raises_value_error(bad_item): with pytest.raises(ValueError, match='item缺少price或qty'): calculate_order_total([bad_item])

@pytest.mark.parametrize("bad_percent", ['-0.1', Decimal('1.1')]) def test_percent_discount_out_of_range_raises_value_error(bad_percent): items = [{'price': '10', 'qty': 1}] discount = {'type': 'percent', 'value': bad_percent} with pytest.raises(ValueError, match='百分比折扣需在0到1之间'): calculate_order_total(items, discount=discount)

def test_fixed_discount_negative_raises_value_error(): items = [{'price': '10', 'qty': 1}] discount = {'type': 'fixed', 'value': '-5'} with pytest.raises(ValueError, match='固定折扣需非负'): calculate_order_total(items, discount=discount)

下面给出使用 JUnit 5 为 RateLimiter 编写的单元测试代码。测试覆盖了正常路径、边界与异常场景,并使用可控时间供应器以精确模拟窗口到期与重置。

文件:testgen/RateLimiterTest.java

package testgen;

import org.junit.jupiter.api.Test; import java.util.function.Supplier;

import static org.junit.jupiter.api.Assertions.*;

public class RateLimiterTest {

// 可控时间供应器
static class MutableClock implements Supplier<Long> {
    private long now;
    MutableClock(long start) { this.now = start; }
    @Override public Long get() { return now; }
    void advance(long deltaMillis) { this.now += deltaMillis; }
    void set(long newNow) { this.now = newNow; }
}

@Test
void normalPath_capacity3_window1000_allowThree_thenReject_untilBoundary_thenAllow_remainingResetsAndDecrements() {
    MutableClock clock = new MutableClock(0L);
    RateLimiter rl = new RateLimiter(3, 1000, clock);

    assertEquals(3, rl.remaining(), "Initial remaining should equal capacity");

    assertTrue(rl.allow(), "1st allow should pass");
    assertEquals(2, rl.remaining(), "Remaining should decrement after first allow");

    assertTrue(rl.allow(), "2nd allow should pass");
    assertEquals(1, rl.remaining(), "Remaining should decrement after second allow");

    assertTrue(rl.allow(), "3rd allow should pass");
    assertEquals(0, rl.remaining(), "Remaining should be zero after third allow");

    assertFalse(rl.allow(), "4th allow should be rejected within same window");
    assertEquals(0, rl.remaining(), "Remaining stays zero when capacity exceeded");

    // 推进时间到窗口边界(恰等于窗口长度),应当重置
    clock.advance(1000L);
    long expectedNewWindowStart = clock.get();
    assertTrue(rl.allow(), "Allow should pass right at window boundary (reset occurs)");
    assertEquals(2, rl.remaining(), "Remaining resets to capacity-1 after boundary allow");
    assertEquals(expectedNewWindowStart, rl.getWindowStart(), "Window start should update to current time on reset");
}

@Test
void remainingResetsWhenWindowExpiredBeforeAnyAllow() {
    MutableClock clock = new MutableClock(100L);
    RateLimiter rl = new RateLimiter(3, 200, clock);

    assertEquals(3, rl.remaining(), "Initial remaining");

    // 时间恰等于窗口长度触发重置(通过remaining触发)
    clock.advance(200L);
    long nowAtBoundary = clock.get();

    assertEquals(3, rl.remaining(), "Remaining should reset to full capacity at boundary");
    assertEquals(nowAtBoundary, rl.getWindowStart(), "Window start should update on reset triggered by remaining()");
}

@Test
void capacityOne_boundaryExactWindowReset() {
    MutableClock clock = new MutableClock(0L);
    RateLimiter rl = new RateLimiter(1, 100, clock);

    assertTrue(rl.allow(), "First allow should pass with capacity 1");
    assertFalse(rl.allow(), "Second allow in same window should be rejected");

    // 恰到窗口边界后应允许
    clock.advance(100L);
    assertTrue(rl.allow(), "Allow should pass exactly at window boundary after reset");
    assertEquals(0, rl.remaining(), "Remaining should reflect one usage in new window");
}

@Test
void constructorValidation_invalidArguments_throwIllegalArgumentException() {
    assertThrows(IllegalArgumentException.class, () -> new RateLimiter(0, 1000));
    assertThrows(IllegalArgumentException.class, () -> new RateLimiter(-1, 1000));
    assertThrows(IllegalArgumentException.class, () -> new RateLimiter(1, 0));
    assertThrows(IllegalArgumentException.class, () -> new RateLimiter(1, -1));
    assertThrows(IllegalArgumentException.class, () -> new RateLimiter(1, 1000, null));
}

@Test
void largeCapacity_stateConsistent_andResetsOnExpiry() {
    MutableClock clock = new MutableClock(10_000L);
    int largeCap = 1_000_000;
    RateLimiter rl = new RateLimiter(largeCap, 500, clock);

    // 使用少量次数验证剩余与递减
    for (int i = 0; i < 10; i++) {
        assertTrue(rl.allow(), "Allow should pass while under large capacity");
    }
    assertEquals(largeCap - 10, rl.remaining(), "Remaining should be capacity minus used");

    // 到期后重置
    clock.advance(500L);
    assertEquals(largeCap, rl.remaining(), "Remaining should reset to full capacity after window expiry");
    assertTrue(rl.allow(), "Allow should pass in new window");
    assertEquals(largeCap - 1, rl.remaining(), "Remaining should decrement correctly after new window allow");
}

@Test
void windowStartUpdatesOnResetViaAllow() {
    MutableClock clock = new MutableClock(1_000L);
    RateLimiter rl = new RateLimiter(2, 300, clock);

    assertTrue(rl.allow());
    assertTrue(rl.allow());
    assertFalse(rl.allow(), "Third allow should be rejected in the same window with capacity 2");

    long oldStart = rl.getWindowStart();
    clock.advance(300L);
    long now = clock.get();

    assertTrue(rl.allow(), "Allow at exact expiry should succeed and reset window");
    assertEquals(now, rl.getWindowStart(), "Window start should update to current time after reset");
    assertEquals(1, rl.remaining(), "Remaining should reflect one usage in the new window");
    assertTrue(oldStart != rl.getWindowStart(), "Window start must have changed after reset");
}

}

// Adjust the require path to where your module is located const assert = require('assert'); const { parseUserConfig } = require('./parseUserConfig');

describe('parseUserConfig', () => { describe('正常路径场景', () => { it('JSON字符串覆盖重试、超时、端点与特性;未知字段进入extras', () => { const input = JSON.stringify({ retries: 5, timeoutMs: 8000, endpoint: 'http://api.example.com', features: { cache: false, logging: true }, theme: 'dark', extraFlag: true }); const result = parseUserConfig(input);

  assert.strictEqual(result.retries, 5);
  assert.strictEqual(result.timeoutMs, 8000);
  assert.strictEqual(result.endpoint, 'http://api.example.com');
  assert.deepStrictEqual(result.features, { cache: false, logging: true });
  assert.deepStrictEqual(result.extras, { theme: 'dark', extraFlag: true });
});

it('对象输入并部分覆盖,未提供的特性按默认补齐', () => {
  const input = {
    retries: 2,
    features: { cache: false }, // logging 未提供,按默认 false
    custom: { a: 1 }
  };
  const result = parseUserConfig(input);

  assert.strictEqual(result.retries, 2);
  assert.strictEqual(result.timeoutMs, 5000);
  assert.strictEqual(result.endpoint, 'https://api.example.test');
  assert.deepStrictEqual(result.features, { cache: false, logging: false });
  assert.deepStrictEqual(result.extras, { custom: { a: 1 } });
  // 确保已知键未进入 extras
  assert.ok(!('retries' in result.extras));
  assert.ok(!('timeoutMs' in result.extras));
  assert.ok(!('endpoint' in result.extras));
  assert.ok(!('features' in result.extras));
});

it('features值做布尔归一化(类型校验并归一化)', () => {
  const input = {
    features: { cache: 'yes', logging: 1 }
  };
  const result = parseUserConfig(input);
  assert.deepStrictEqual(result.features, { cache: true, logging: true });
});

});

describe('边界与异常场景', () => { it('缺失所有字段时填默认,extras为空', () => { const result = parseUserConfig({}); assert.strictEqual(result.retries, 3); assert.strictEqual(result.timeoutMs, 5000); assert.strictEqual(result.endpoint, 'https://api.example.test'); assert.deepStrictEqual(result.features, { cache: true, logging: false }); assert.deepStrictEqual(result.extras, {}); });

it('retries在边界0与10时合法', () => {
  const r0 = parseUserConfig({ retries: 0 });
  assert.strictEqual(r0.retries, 0);
  const r10 = parseUserConfig({ retries: 10 });
  assert.strictEqual(r10.retries, 10);
});

it('retries越界或非整数抛错', () => {
  assert.throws(() => parseUserConfig({ retries: -1 }), /retries must be integer in \[0,10\]/);
  assert.throws(() => parseUserConfig({ retries: 11 }), /retries must be integer in \[0,10\]/);
  assert.throws(() => parseUserConfig({ retries: 3.5 }), /retries must be integer in \[0,10\]/);
  assert.throws(() => parseUserConfig({ retries: '3' }), /retries must be integer in \[0,10\]/);
});

it('timeoutMs非正或非整数抛错', () => {
  assert.throws(() => parseUserConfig({ timeoutMs: 0 }), /timeoutMs must be a positive integer/);
  assert.throws(() => parseUserConfig({ timeoutMs: -100 }), /timeoutMs must be a positive integer/);
  assert.throws(() => parseUserConfig({ timeoutMs: 1000.5 }), /timeoutMs must be a positive integer/);
  assert.throws(() => parseUserConfig({ timeoutMs: '5000' }), /timeoutMs must be a positive integer/);
});

it('endpoint未以http://或https://开头或类型错误抛错', () => {
  assert.throws(() => parseUserConfig({ endpoint: 'ftp://server' }), /endpoint must start with http:\/\/ or https:\/\//);
  assert.throws(() => parseUserConfig({ endpoint: 'api.example.com' }), /endpoint must start with http:\/\/ or https:\/\//);
  assert.throws(() => parseUserConfig({ endpoint: 123 }), /endpoint must start with http:\/\/ or https:\/\//);
});

it('非法JSON字符串抛Error', () => {
  assert.throws(() => parseUserConfig('{ not json }'), /Invalid JSON/);
  assert.throws(() => parseUserConfig('{"a":1,}'), /Invalid JSON/);
});

it('输入类型既不是字符串也不是对象(含null)时抛错', () => {
  assert.throws(() => parseUserConfig(42), /Config must be JSON string or object/);
  assert.throws(() => parseUserConfig(null), /Config must be JSON string or object/);
});

it('features为空对象时按默认补齐', () => {
  const result = parseUserConfig({ features: {} });
  assert.deepStrictEqual(result.features, { cache: true, logging: false });
});

it('features不是对象时按默认补齐', () => {
  const result = parseUserConfig({ features: 'oops' });
  assert.deepStrictEqual(result.features, { cache: true, logging: false });
});

it('extras保留未知键且支持嵌套对象', () => {
  const input = {
    retries: 2,
    timeoutMs: 6000,
    endpoint: 'https://example.org',
    features: { cache: true },
    notes: { a: 1, b: [1, 2] },
    theme: 'dark'
  };
  const result = parseUserConfig(input);
  assert.deepStrictEqual(result.extras, { notes: { a: 1, b: [1, 2] }, theme: 'dark' });
});

}); });

示例详情

该提示词已被收录:
“程序员必备:提升开发效率的专业AI提示词合集”
让 AI 成为你的第二双手,从代码生成到测试文档全部搞定,节省 80% 开发时间
√ 立即可用 · 零学习成本
√ 参数化批量生成
√ 专业提示词工程师打磨

📖 如何使用

模式 1:即插即用(手动档)
直接复制参数化模版。手动修改 {{变量}} 即可快速发起对话,适合对结果有精准预期的单次任务。
加载中...
💬 模式 2:沉浸式引导(交互档)
一键转化为交互式脚本。AI 将化身专业面试官或顾问,主动询问并引导您提供关键信息,最终合成高度定制化的专业结果。
转为交互式
🚀 模式 3:原生指令自动化(智能档)
无需切换,输入 / 唤醒 8000+ 专家级提示词。 插件将全站提示词库深度集成于 Chat 输入框。基于当前对话语境,系统智能推荐最契合的 Prompt 并自动完成参数化,让海量资源触手可及,从此彻底告别“手动搬运”。
安装插件
🔌 发布为 API 接口
将 Prompt 接入自动化工作流,核心利用平台批量评价反馈引擎,实现"采集-评价-自动优化"的闭环。通过 RESTful 接口动态注入变量,让程序在批量任务中自动迭代出更高质量的提示词方案,实现 Prompt 的自我进化。
发布 API
🤖 发布为 Agent 应用
以此提示词为核心生成独立 Agent 应用,内嵌相关工具(图片生成、参数优化等),提供完整解决方案。
创建 Agent

🕒 版本历史

当前版本
v2.1 2024-01-15
优化输出结构,增强情节连贯性
  • ✨ 新增章节节奏控制参数
  • 🔧 优化人物关系描述逻辑
  • 📝 改进主题深化引导语
  • 🎯 增强情节转折点设计
v2.0 2023-12-20
重构提示词架构,提升生成质量
  • 🚀 全新的提示词结构设计
  • 📊 增加输出格式化选项
  • 💡 优化角色塑造引导
v1.5 2023-11-10
修复已知问题,提升稳定性
  • 🐛 修复长文本处理bug
  • ⚡ 提升响应速度
v1.0 2023-10-01
首次发布
  • 🎉 初始版本上线
COMING SOON
版本历史追踪,即将启航
记录每一次提示词的进化与升级,敬请期待。

💬 用户评价

4.8
⭐⭐⭐⭐⭐
基于 28 条评价
5星
85%
4星
12%
3星
3%
👤
电商运营 - 张先生
⭐⭐⭐⭐⭐ 2025-01-15
双十一用这个提示词生成了20多张海报,效果非常好!点击率提升了35%,节省了大量设计时间。参数调整很灵活,能快速适配不同节日。
效果好 节省时间
👤
品牌设计师 - 李女士
⭐⭐⭐⭐⭐ 2025-01-10
作为设计师,这个提示词帮我快速生成创意方向,大大提升了工作效率。生成的海报氛围感很强,稍作调整就能直接使用。
创意好 专业
COMING SOON
用户评价与反馈系统,即将上线
倾听真实反馈,在这里留下您的使用心得,敬请期待。
加载中...