这是一个实现细节吗?或者它是一个应该用 Jest 测试的功能?

Posted

技术标签:

【中文标题】这是一个实现细节吗?或者它是一个应该用 Jest 测试的功能?【英文标题】:Is this an implementation detail? Or is it a feature that should be tested with Jest? 【发布时间】:2021-06-16 03:29:00 【问题描述】:

我有一个更新时间的问候组件。根据一天中的时间打印问候语

早安,简 下午好,简 晚上好,简

我有一个名为getTimeOfDay 的函数并测试该函数是否正常工作。如果你有兴趣view my tests here.

在我的组件中,我有一个计时器,它每分钟检查一次消息是否应该更新。

const [date, setDate] = useState(new Date())
const timeOfDay = getTimeOfDay(date)

useEffect(() => 
  const timer = setInterval(() => 
    setDate(new Date())
  , 60000)

  return () => 
    clearInterval(timer)
  
, [])

我一直在反复考虑是否测试此消息是否随着时间的推移而正确更改。我知道测试实现细节是不好的做法,我不确定这是一个实现细节还是应该测试的功能。

如果这是我应该测试的东西,我似乎无法轻松地实现一个计时器来检查消息,然后将计时器加速 8 小时。你有什么想法?

【问题讨论】:

好像you can set a mock date in Jest。还找到了this article。因此,似乎可以在一天中的不同时间对此进行测试。 将时间视为依赖项(参见here 的一种方式),那么很容易编写多个不同时间的测试——早上、下午、晚上,如果你愿意,可以更详细地了解转换点。 所以我认为我首先关心的是这是否是一个实现细节。 @Jamie 在这种情况下有点模糊。在纯粹的技术上,它一个实现细节。如果你把函数当作一个黑盒子,你不知道它是否使用Date。另一方面,如果它总是返回“晚安,Jane”,那么这是一个错误的实现。这些是需要测试以确保正确性的案例。因此,从实用的角度来看,应该可以测试该函数在不同时间是否正确运行。 jonsharpe 将依赖项外部化的建议是完美的中间立场 - 它不再是实现细节。 【参考方案1】:

我的最终测试如下:

describe('Greeting', () => 
  const name = 'Jack'
  
  it('renders component as expected', () => 
    const wrapper = mount(<Greeting name=name /> )
    expect(wrapper.text().includes(name)).toBe(true)
  )

  it('Should update message after time', () => 
    jest.useFakeTimers()
    setMockDate(new Date('Feb 22, 2021 11:59:00'))
    const wrapper = mount(<Greeting name=name />)

    const greetingText = wrapper.text()
    setMockDate(new Date('Feb 22, 2021 12:00:00'))
    jest.advanceTimersByTime(60000)
    expect(wrapper.text()).not.toBe(greetingText)
  )

  it('Should clear interval on unmount', () => 
    const spyOnSetInterval = jest.spyOn(window, 'setInterval')
    const spyOnClearInterval = jest.spyOn(window, 'clearInterval')
    spyOnSetInterval.mockReturnValueOnce((33 as unknown) as NodeJS.Timeout)
    const wrapper = mount(<Greeting name=name />)

    wrapper.unmount()
    expect(spyOnClearInterval).toHaveBeenCalledWith(33)
  )
)

我创建了一个辅助函数来模拟日期。最终版本是:

/**
 * @param Date expected
 * @returns Function Call to remove Date mocking
 */
const setMockDate = (expected: Date): AnyObject => 
  const RealDate = Date

  function MockDate(mockOverride?: Date | number) 
    return new RealDate(mockOverride || expected)
  

  MockDate.now = () => expected.getTime()
  MockDate.prototype = RealDate.prototype

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  global.Date = MockDate as any

  return () => 
    global.Date = RealDate
  


export default setMockDate

【讨论】:

【参考方案2】:

您可以使用模拟函数更改测试日期以覆盖测试中的日期。

    Date.now = jest.fn(() => new Date( "2021-01-01 8:50:00"))
    
    const result = getTimeOfDay(switchToMorning)
    expect(result).toEqual('Morning')

【讨论】:

可能是因为 switchToMorning 在创建时具有硬编码日期。当你想在测试函数中使用上述方法时,你应该只使用new Date()

以上是关于这是一个实现细节吗?或者它是一个应该用 Jest 测试的功能?的主要内容,如果未能解决你的问题,请参考以下文章

应用程序架构师应该编写代码吗?

试图用 jest 测试我的巢 api,返回未定义

我应该如何为样式化组件编写 Jest 测试用例并监视 css 以验证样式是不是正确?

用 jest 测试 Vue 过滤器

用 Jest 只运行一个测试

我认为这是一个编译器错误,这不应该影响我的代码,但它是 [关闭]