框架重构:测试中的DateTime.Now

本文探讨了在C#中使用DateTime.Now计算年龄时遇到的问题,并提供了三种解决方案:增加方法参数、使用IoC框架及使用委托。最终推荐使用委托方式以减少维护成本。

存在的问题

  • DateTime.Now是C#语言中获取计算机的当前时间的代码;
  • 但是,在对使用了DateTime.Now的方法进行测试时,由于计算机时间的实时性,期望值一直在变化。如:计算年龄。
public static class DateTimeExtensionMethods
{
    public static int Age(this DateTime date)
    {
        DateTime now = DateTime.Now;
        ......
    }
}
new DateTime(2008,8,12).Age().ShouldEqual(???);

解决方案一:给Age方法增加一个参数,将当前时间传进去

// method
public static int Age(this DateTime date, DateTime now){...}
// test
new DateTime(2008,8,12).Age(new DateTime(2017,8,13)).ShouldEqual(9);
缺点:
  • 显示传入当前时间麻烦
  • 多一个参数,维护成本也会增加
  • 感觉怪怪的,不符合习惯

解决方案二:使用IoC框架实现

public interface ISystemClock
{
    DateTime Now { get; }
}
public class SystemClock : ISystemClock
{
    public DateTime Now 
    { 
        get { return DateTime.Now; } 
    }
}
public static class DateTimeExtensionMethods
{
    public static int Age(this DateTime date)
    {
        DateTime now = IoC.Get<ISystemClock>().Now;
        ......
    }
}
// 真实系统
IoC.Register<ISystemClock, SystemClock>();
// test
var mock = MockRepository.GenerateMock<ISystemClock>();
mock.Stub(x=>x.Now).Return(new DateTime(2017,8,13);
IoC.Register<ISystemClock>(mock);
缺点
  • 需要使用IoC框架
  • 操作繁琐,代码量有点多

解决方案三:使用委托(当前最佳方案)

public static class SystemClock
{
    public static Func<DateTime> Now = () => DateTime.Now;
}
public static class DateTimeExtensionMethods
{
    public static int Age(this DateTime date)
    {
        DateTime now = SystemClock.Now();
        int age = now.Year - date.Year;
        if (now.Month == date.Month)
            age = (now.Day < date.Day) ? age - 1 : age;
        else if (now.Month < date.Month)
            age = age - 1;
        return age;
    }
}
[Subject(typeof(DateTime), "Age")]
public class when_convert_birthday_to_age
{
    Establish context = () => SystemClock.Now = () => new DateTime(2013, 8, 25);

    public class with_yesterday_is_birthday
    {
        Because of = () => result = new DateTime(1980, 8, 24).Age();
        It 应该计算出正确的年龄 = () => result.ShouldEqual(33);
    }
        
    public class with_today_is_birthday
    {
        Because of = () => result = new DateTime(1980, 8, 24).Age();
        It 应该计算出正确的年龄 = () => result.ShouldEqual(33);
    }

    public class with_tomorrow_is_birthday
    {
        Because of = () => result = new DateTime(1980, 8, 26).Age();
        It 应该计算出正确的年龄 = () => result.ShouldEqual(32);
    }

    private static int result;
}

You can implement ICleanupAfterEveryContextInAssembly to perform cleanup after every context.
machine.specifications官网

// 每个测试执行完后,需把SystemClock.Now还原
public class ResetTheClock : ICleanupAfterEveryContextInAssembly
{
    public void AfterContextCleanup()
    {
        SystemClock.Now = () => DateTime.Now;
    }
}

转载于:https://www.cnblogs.com/tuty/p/6751603.html

# ================== Python基础课程:模块导入机制详解 ================== # 采用总-分-总结构讲解 # 每行代码均有详细注释,适合测试同学理解 # =============== 总:模块导入的本质与作用 =============== """ 📚 什么是模块导入? 模块导入是Python组织代码的核心机制,允许我们将功能封装在同文件中 通过导入机制实现代码复用和功能扩展 🔧 导入的本质: 1. 当执行 `import module` 时,Python会: - 搜索指定模块(.py文件) - 执行该模块中的所有代码 - 将模块中的定义(变量/函数/类)加载到当前命名空间 2. 导入操作实际上创建了一个模块对象引用 🎯 为什么测试需要掌握导入? - 测试框架(如pytest)基于模块化设计 - 测试数据生成常需导入第三方库(Faker, random) - 测试用例管理依赖模块化组织 """ # =============== 分:三种导入方式详解 + 测试场景示例 =============== # --- 1. 基础导入:import 模块名 --- # ✅ 适用场景:使用模块中多个功能时 import random # 将整个random模块加载到当前命名空间 # What:生成随机测试数据 # Why:测试需要模拟各种边界值 # How:使用random模块的多个函数 print("随机整数(边界测试):", random.randint(0, 100)) # 测试边界值0和100 print("随机浮点数(压力测试):", random.uniform(1.0, 100.0)) # 压力测试使用 # 示例:生成批量测试用户 def generate_test_users(count): """生成指定数量的测试用户""" users = [] for i in range(count): # 使用random模块的多个功能 user_id = random.randint(1000, 9999) age = random.randint(18, 60) score = round(random.uniform(0, 100), 2) users.append(f"用户{user_id}-{age}岁-分数{score}") return users print("生成的测试用户:", generate_test_users(3)) # --- 2. 精准导入:from ... import ... --- # ✅ 适用场景:只需模块的特定功能 from datetime import datetime # 仅导入datetime类 # What:记录测试执行时间 # Why:性能测试需要精确计时 # How:直接使用datetime类 def measure_performance(): """测量函数执行时间""" start = datetime.now() # 直接使用datetime类 # 模拟耗时操作 import time time.sleep(0.5) end = datetime.now() duration = (end - start).total_seconds() print(f"⏱️ 执行耗时:{duration:.3f}秒") return duration measure_performance() # --- 3. 别名导入:from ... import ... as --- # ✅ 适用场景:模块名冲突或简化长模块名 from faker import Faker as Fk # 导入Faker类并重命名为Fk # What:生成模拟测试数据 # Why:需要逼真的测试数据 # How:使用别名简化代码 fk = Fk('zh_CN') # 创建中文数据生成器 # 生成测试数据 test_person = { "姓名": fk.name(), "身份证": fk.ssn(), "手机号": fk.phone_number(), "地址": fk.address() } print("📄 生成的测试数据:", test_person) # =============== 总:模块导入最佳实践 =============== """ 🔍 导入机制深度解析: 1. 模块搜索路径: - 内置模块 → 当前目录 → PYTHONPATH → 安装路径 2. 导入缓存: - 模块首次导入后会被缓存(sys.modules) - 后续导入直接使用缓存 💡 测试开发中的导入最佳实践: 1. 标准库导入 → 第三方库导入 → 本地模块导入(PEP8规范) 2. 避免在函数内部导入(除非有特殊需求) 3. 使用别名解决命名冲突: from package1 import utils as utils1 from package2 import utils as utils2 ⚠️ 常见导入陷阱: 1. 循环导入:模块A导入B,同时B导入A → 重构代码结构解决 2. 命名空间污染:避免使用 from module import * 3. 重载问题:使用 importlib.reload() 谨慎处理 🔧 测试专用导入技巧: 1. 动态导入(测试同实现): module = __import__('data_generators') 2. 条件导入(兼容同环境): if ENV == "test": from mock_db import Database else: from real_db import Database """ # =============== 综合练习:测试数据生成器 =============== import random # 导入整个random模块 from datetime import datetime, timedelta # 导入多个时间类 from faker import Faker as DataGen # 使用别名 def generate_test_dataset(size): """生成综合测试数据集""" start_time = datetime.now() print(f"🛠️ 开始生成{size}条测试数据 ({start_time.strftime('%H:%M:%S')})") dataset = [] faker_gen = DataGen() for i in range(size): # 使用各种导入的功能 record = { "id": i + 1, "name": faker_gen.name(), "birthdate": datetime.now() - timedelta(days=random.randint(365 * 18, 365 * 60)), "score": random.randint(0, 100), "is_active": random.choice([True, False]) } dataset.append(record) end_time = datetime.now() duration = (end_time - start_time).total_seconds() print(f"✅ 生成完成!耗时:{duration:.2f}秒,速率:{size / duration:.1f}条/秒") return dataset # 生成测试数据(10条示例) test_data = generate_test_dataset(10) print("第一条测试数据:", test_data[0]) 再加几个例子吧,要加什么request pytest,文件操作的,因为我后面要单独说
08-07
import datetime import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.service import Service import win32com.client speaker = win32com.client.Dispatch("SAPI.SpVoice") # 打开浏览器 s = Service(r"D:\chromedriver.exe") # 使用原始字符串避免转义问题 browser = webdriver.Chrome(service=s) # 修正类名首字母大写 # 进入京东界面 browser.get("http://www.jd.com") time.sleep(3) # 扫码登录(修正元素定位和方法) try: browser.find_element(By.LINK_TEXT, "你好,请登录").click() # 修正链接文本和方法 print("请扫码") time.sleep(8) except Exception as e: print(f"登录失败: {str(e)}") browser.quit() exit() # 打开购物车(修正URL格式) browser.get("http://cart.jd.com/cart_index") # 修正URL中的空格 time.sleep(5) # 定时抢购逻辑 target_time = datetime.datetime(2023, 3, 3, 20, 59, 0) while datetime.datetime.now() < target_time: time.sleep(0.1) # 减少CPU占用 # 结算流程 try: # 去结算 while True: try: browser.find_element(By.LINK_TEXT, "去结算").click() print("进入结算页面") break except: browser.refresh() time.sleep(0.5) # 提交订单 while True: try: browser.find_element(By.LINK_TEXT, "提交订单").click() print("主任,结算提交成功,请支付订单!") speaker.Speak("主任,结算提交成功,请支付订单!") break except: time.sleep(0.5) except Exception as e: print(f"执行出错: {str(e)}") finally: time.sleep(10) browser.quit()
04-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值