单元测试生成

410 浏览
42 试用
10 购买
Nov 24, 2025更新

一键生成全面单元测试,覆盖所有场景,代码更健壮,测试更高效!

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

例如: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' });
});

}); });

示例详情

解决的问题

开发者的工作场景描述

解决的问题

针对 根据给定代码与框架编写覆盖完整场景的单元测试 的日常工作场景,该工具旨在解决以下问题:

  • 手动编写测试用例效率低下,难以覆盖所有边界条件和异常场景
  • 测试代码质量参差不齐,难以保证测试的准确性和可维护性
  • 缺乏系统化的测试策略,导致测试覆盖率不足,代码质量风险高

工具介绍

工具名称: 单元测试编写
功能简介: 根据给定代码与框架编写覆盖完整场景的单元测试,自动生成高质量的测试用例,确保代码逻辑正确性和边界条件覆盖,提升测试效率和代码质量。

协同场景

使用场景描述:

构建完整的代码质量保障流程,从代码编写到测试验证再到持续集成。

具体协作步骤:
  1. 使用代码审查与缺陷识别:对源代码进行质量审查,识别潜在缺陷和优化点
  2. 使用单元测试编写:基于审查后的代码生成完整的单元测试用例
  3. 使用持续集成方案:配置自动化测试流水线,确保测试持续执行
  4. 使用代码性能优化:分析测试结果,对性能瓶颈进行针对性优化

适用用户

软件开发工程师

快速生成复杂代码的高覆盖率单元测试,减少编写测试的时间成本,专注核心开发工作。

测试工程师

通过智能生成的测试用例补全关键场景漏洞,确保代码稳定性和业务可靠性。

项目经理

借助自动化测试增强团队开发效率,更放心地交付高质量产品上线。

特征总结

轻松生成覆盖全面的单元测试,自动适配多种测试框架与编程语言,减少重复工作。
智能识别代码逻辑,涵盖核心功能、边界情况与错误处理,确保测试场景无遗漏。
生成高质量的描述性测试用例名称,有助于提升代码可读性与维护性。
支持按需模拟依赖,为复杂依赖关系的测试提供便利,避免手动定义繁琐步骤。
一键优化现有测试逻辑,快速发现并弥补测试盲区。
模板化参数设置,自由选择测试框架(如JUnit、Pytest等)和语言(如Python、Java等),满足个性化需求。
轻松生成边界测试和异常处理场景,有效提升代码的鲁棒性。
全自动覆盖典型“理想路径”场景,减少测试遗漏,实现业务逻辑高可靠性。
适配所有开发者角色,无需测试领域专业知识,也可快速上手构建高效测试。

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥20.00元
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 113 tokens
- 5 个可调节参数
{ 测试框架 } { 编程语言 } { 函数/类定义 } { 正常路径场景描述 } { 边界与异常场景描述 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
限时免费

不要错过!

免费获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59