我遇到了一个反复出现的问题,即错误在一个版本中会出现一两次,并由用户报告,但是我和我的团队无法复制它们。

您使用什么技术来复制难以复制的错误?

我们正在使用Sentry来拾取异常但不会出现故障,这是要解决的最令人沮丧的错误,因为它们只出现一次却很少出现。

评论

请定义“毛刺”

先发现错误,然后运行100次,然后再次出现错误。

您需要向用户强调可重复性的重要性,除非他们能够描述暴露该错误所需的步骤,否则您几乎或无能为力。不要陷入说我去看看的陷阱。

问题在于它们确实为我们提供了复制的步骤,但是当我们尝试时,它不会中断。

这听起来像是底层代码中的初始化错误。用户看到的错误不是由其操作引起的,而是由发生故障时的计算机状态引起的。程序员不要试图模仿用户的操作,而是对异常跟踪的根本原因进行反向工程。如果我是正确的,并且这是初始化错误,则没有人能够按需重现症状,尤其是在调试或沙盒测试环境中。

#1 楼

您有

间歇性故障

欢迎大家。以我的经验,它们是质量工程的规范和祸根。
因此,首先,请接受并开始为其提供资源。

您可以使用一些技术:


允许足够的资源。准备需要花费数天/周的时间来修复间歇性故障
收集示例。某些间歇性故障直到我看到数十种故障模式后才显示出来。
在本地多次运行测试。至少100次(如果不是1000次)。查找图案
查看测试设置。测试不应共享任何数据,甚至不应共享参考数据
对于测试,请使用隐式等待元素而不是显式等待(睡眠)
使用并增强日志以显示特定事件的更多信息
在您的ci服务器上测试100或1000次

我随时间收集的有用的调查信息:


浏览器
用户类型
使用Ajax
一天中的时间
屏幕尺寸
浏览器版本
使用javascript
频率随时间变化
各种资源ID
功能已执行
与释放推送/重新启动的关系


评论


您观察并收集有关偶尔失败的测试的数据。有时可能需要数月甚至数年才能看到故障背后的模式。

–迈克尔·杜兰特(Michael Durrant)
17-6-29在10:53



情况并不完全相同,但是我记得30年前,开发人员无法重现用户正在报告的崩溃(用户可以很容易地产生崩溃)。事实证明,不同之处在于开发人员的用户名字符数为奇数,而用户的偶数数为;这足以使触发错误与不触发错误有所不同。

–马丁·邦纳(Martin Bonner)支持莫妮卡(Monica)
17年6月29日在13:18

Martin Bonner的轶事添加了另一种方法来查找此类问题:逐一比较用户和您的应用程序环境-来自不同的代码或构建版本,外部库,环境变量,操作系统限制(用户访问权限等),直到发现差异为止。不幸的是,其中可能有很多。重现环境,然后再次测试。

–not2精明
17年6月29日在13:27

@MartinBonner如果用户可以轻松重现该错误,则它不是间歇性错误。这只是我们尚不知道触发器的错误。对于报告此错误的用户来说,真正的间歇性错误也是间歇性的。

–Peter M.-代表莫妮卡(Monica)
17年6月29日在16:39

@ bobo2000取决于您“不知道如何测试”的意思。如果通过“测试”表示“运行少量代码并获得明确的确定性失败信号”,则您过于乐观。相反,您正在“测试”以查找故障,而不是对其进行调试。通过错误描述,您可以了解引起问题的一般区域。开始撞上它。做据说无法正常工作数百次的事情。对无法正常工作的事物进行数百种变体。在工作和不工作的环境之间使用差异,以形成数百个假设并测试每个假设。

– R.M.
17年6月29日在17:54

#2 楼

詹姆斯·巴赫(James Bach)的“如何研究间歇性问题”提供了对该主题的非常彻底的处理。以下是他的92条建议的概述:

如果存在此错误,则有以下原因:


可能性1:系统的行为没有不同。明显的间断是观察的结果。

可能性2:系统是不同的,因为它是一个不同的系统。

可能性3:系统是不同的,因为它是一个系统。

情形4:系统的行为有所不同,因为输入了不同的信息。

情形5:其他可能性被放大了,因为您的系统心理模型以及在什么方面以某种重要方式影响它的不正确或不完整。


请查看链接的博客,以获取针对每种原因的建议。

这是不可预测的,因为有一些您不了解的东西

此答案(和其他答案)列出了已知的各种因素,这些因素会在软件中造成意外或明显的不可预测的行为。该软件本身无法创建不确定的随机输出,句号。但是,它有很多输入,很多都是隐式的-例如线程的执行时间,网络延迟,日期和时间,is年等。-可以产生不确定的随机输出。如果软件的输出是这些随机输入的函数,则其输出也可能是随机的。

任何测试都必须隐含一些输入(出于实际原因,例如可用于编写测试的时间,维护问题,技术限制)。指定函数的执行时间作为您运行的每个测试的输入是愚蠢的(这会使测试设置复杂化!!)。但是,如果控制/更改显式/已知输入无法重现问题,请找到一些隐式/未知输入,然后控制/更改这些输入。

然后,问题就变成了理解哪些隐式/未知输入会影响行为的方法:


我们通常所说的间歇​​性问题是:系统的神秘和不良行为,至少观察到一次,即我们还无法按需显示。

我们的挑战是通过解决围绕它的谜团,将间歇性错误转变为常规错误。之后,程序员会头疼


您的任务是确定导致这种行为的条件。链接的博客和此处的其他答案给出了一长串可能的问题。确定一些似乎可行的方法,然后进行调查。重复直到可以重现该错误为止。

注意:我对詹姆斯·巴赫(James Bach)的可能性#1,#2和#3提出这一点,并宽松地定义“输入”以包括状态和系统本身。如果您坚持这样做,请在阅读我刚刚写的内容时将“输入”替换为“输入,状态或系统特征”。

对问题空间进行二元搜索

选择要控制/变化的因素时,请通过将问题空间平分来选择最能带来收益的因素。

示例:
如果间歇性地未收到您的通知电子邮件,并且您已识别出可能的因素的简短列表,则为:


您的域已被列入黑名单
某个邮件提供商存在问题
某些代码路径未触发通知
垃圾邮件过滤器阻止邮件通过
用户没有实际采取会触发该通知的动作
等。

要执行“二进制搜索”,您想进行一次测试,以一次消除大多数可能的因素。在这种情况下,可能正在记录发送电子邮件的尝试。如果没有尝试发送丢失的电子邮件,则必须为3、5或6。如果日志记录表明尝试发送丢失的电子邮件,则必须为1、2、4或6。重复新的问题空间。如果发现因素6,请生成一个新列表。

反例将“黑暗中拍摄”,直到您将其击中。示例:设置一个测试,该测试将从每个主要的电子邮件提供商向帐户发送通知。如果其中一个不能接收通知,则它必须是因素2(如果只有一个提供商,则可能是因素4)。否则,它是因子1、3、4、5或6。如果幸运的话,这将花费更少的时间。如果您不走运,请延长。

如果您认为您的已知因子列表(在此示例中:项目1-5)小于或等于未知因子的数量(在此示例中:项目6),则首先选择适当的bisection是一项测试,用于确定是否应用了任何已知因素,或者都不使用(在这种情况下,您需要重新列出)。

TLDR


列出可能导致它的原因的简短列表


咨询链接的文章,其他答案,google,同事等。


被卡住了吗?记住:这不是黑魔法,只是您还不了解的东西。请参阅步骤#1
进行测试,该测试将消除该列表的一半(请考虑:二进制搜索)。
根据需要重复。


评论


即使起步较晚,您的答案也在不断扩大,您正在获得XP。第一个答案还不错。欢迎登机!带上这些链接和答案!

–Peter M.-代表莫妮卡(Monica)
17年6月29日在18:46

“软件中不存在随机行为”,这是不正确的,即使不是设计为随机的,各种软件也会表现出随机行为。多线程/异步/分布式的任何事物通常都是不确定的。磁盘和网络延迟是随机因素。

– jcai
17-6-29在23:33



@Arcinde,实际上我同意你的看法。但是从学步上来说,如果随机性的来源是来自某种硬件,例如磁盘,那么我会认为它是一种硬件随机数生成器。另一方面,如果它来自多线程/异步/分布式系统中的软件复杂性,那么严格来说,它不是随机的-也许不确定,但不是随机的。将系统边界扩展到足够宽,不确定性系统将变得确定性。这就是我的意思。

–alexanderbird
17年6月29日在23:51

“也许不确定,但不是随机的”-我必须不同意。在现代处理器上,发生了很多事情,甚至“花费多少个时钟周期”通常是真正随机的,而不仅仅是不可行的。在这些时间尺度上的异步信号受散粒噪声/ EMI /等的影响太大,无法确定。而且,现代处理器上的异步信号比人们想象的要多。

–TLW
17年6月30日在4:49

与其说“随机”,不如说是混乱的行为。早在1975年,就提供了数学证明,即任何具有三个自变量的系统都将表现出混沌行为。虽然混乱不是随机的,但无法预测系统将处于哪个状态(期间三暗示混乱,James A York)

–pojo-guy
17年7月11日在4:46

#3 楼

从不同的角度进行分析以找到确切原因

我假设您正在尝试在报告该问题的相同环境中调试该问题。问题:


堆积更多日志:尝试捕获尽可能多的日志。首先,您可以依靠它来获取有关此类问题的一些有价值的信息。
检查与数据相关的依赖性:有时数据是此类问题的主要贡献者。因此,一种好的方法是连接到报告问题的同一数据库,或者创建副本并尝试复制问题。但是总而言之,不应忽略数据方面。
多线程环境:多个线程有可能更新应用程序数据。有时可能是导致应用程序意外行为的原因。请用户报告问题的更详细步骤:
用户可能会发现某些与问题无关的操作,而与该问题无关报告,但此类操作可能需要导入以进行分析。除此以外,您还应该询问用户用于测试的数据(如果尚未完成的话)。
尝试确定在此期间发生的所有查询/事件。

我知道,这就像是一名狙击手,等待问题再次发生,有时会令人沮丧。

评论


您推荐什么类型的日志?

– bobo2000
17年6月30日在10:05

这是我们正在构建的网络应用

– bobo2000
17年6月30日在10:05

您应该同时进行服务器端和客户端日志记录。我的意思是,这还取决于问题的类型。如果问题与UI有关,则客户端日志记录可能就足够了。如果发生功能/数据相关问题,建议使用服务器端日志记录。并且只需编写简单的日志,使您能够在每个逻辑步骤之后记录信息。

–阿拉克
17年7月3日在10:45

#4 楼

除技术方面外,您可能还需要考虑组织方面。

您很可能会获得一张故障单(错误报告),该故障单会长期处于打开/分配状态(未解决)。时间(例如几个月)。在某些组织中,这可能会使您看起来表现不佳。在这种情况下,请与您的老板或老板的老板一起找到合适的解决方案,以确保“官方KPI”不会向南走。它可能是票务系统中的一种新状态,例如“长期调查中”,它将停止计时器/ KPI计数器。 (或每天至少一次,如果发生频率更高)。这不必是通常的详细错误报告,而只是确认该错误仍然存​​在。该错误可能会在某个软件版本发布后的某个时间点消失,该版本修复了完全不同的错误。而且你不会知道。或者有人可以断定“与此同时,我们已经发布了10个主要版本,此错误必须已经得到修复”,并且尽管未得到修复也将其关闭。在这个问题上,即使您尚未确定。然后告诉您,感谢他们在此问题上的一如既往的支持(并且可能会谅解)。确保他们将自己视为解决方案之路的重要组成部分,而不是令人讨厌的事情,并认真对待它们。

定期检查您的“无法解决的故障单”,以使问题始终在您的脑海中。在三到六个月后,您可能遇到了一段代码,这时您突然被启蒙而震惊,突然看到了晦涩的副作用,在某些晦涩难懂的情况下,它可能会触发此类错误。显然,您不会在数周或数月的时间里争夺某个单一的错误,而是主要从事其他工作。这样可以避免您太沮丧。经过数周成功的工作并获得了不同成果的回报后,您会从不同的角度看待问题。

与同事(可能是老板或用户的老板)讨论该错误。您的同事可能有一些想法,或者在三,六个月后偶然发现了一段代码……您就明白了。老板可能会提供组织帮助,例如提高用户知名度,甚至允许您在几天或几周内捕获所有网络流量(加上从IT基础架构部门获得帮助或任何需要的帮助),或在用户工作站上安装监视软件。或者让数据库专家忙碌起来,以便他们可以监视机器负载级别,死锁,数据损坏(yup),查询缓慢等。

您还可以检查部署和构建过程(也称为配置管理) )。有时,错误是由不可复制的版本引入的(同一软件版本的两个版本会产生不同的行为-例如,如果一个版本具有许多手动步骤,或者在不同的计算机上进行,则可能会发生这种情况)。其他错误是由于工作站或服务器上某些其他软件的怪异副作用引起的。

#5 楼

这是我指示开发人员执行的操作:

1)添加日志记录;允许用户选择高级日志记录。如果打开日志记录“解决”了问题,那么这是一个计时问题。我们将日志记录添加到我们怀疑与用户看到的问题有关的代码中的位置。

2)让用户拍摄问题发生时他们正在做的事情的视频。

3)运行某种代码分析工具并解决问题。根据您的语言,可以使用一些工具来分析代码并发出警告。解决尽可能多的问题。

关于运行视频:
我在iOS应用程序方面遇到问题。用户给了我有关如何重现该问题的非常详细的说明。我们得到了iPad的确切型号和iOS的确切信息,无法重现该问题。我们要求提供视频(他们使用其他电话使用该应用为她视频)。

用户是一位非常有经验的10键操作员。她将手悬停在ipad上,将用所有五个手指按下按钮。她非常快。由此,我们立即知道这是一个时机问题。

评论


“添加日志”-一种技术是在应用运行时向草稿文件添加详细的自动日志。然后向用户在应用程序中的某个位置提供命令,用户界面可“生成崩溃日志”,以将其复制到崩溃后立即可访问的位置,如果QA团队要求将其附加到错误报告中。这样,您不必依靠用户的想法(或半记得)他/她的所作所为!

–alephzero
17年6月30日在1:34



什么类型的日志?

– bobo2000
17年6月30日在10:04

#6 楼

这里有一些想法:


强烈同意记录!具有时间戳的毫秒数(事件A在01:23:45.999和事件B在01:23:46.0​​00之间的差异较大,而事件A在01:23:45.000和事件B在01:23:46.999之间的差异很大)。
添加特定于错误的日志记录:如果您可以检测到何时发生了错误,则记录事件。
循环缓冲区为历史记录提供了低成本存储。
对象自检的状态保持一致。
添加跟踪对象信息:创建此对象的时间和内容是什么?通常可以压缩到几个字节。
单元测试的覆盖率很好。
测试自动化,包括伪随机。
线程历史:每个对象都有写一个1的“检查点”。 .2字节值记录失败。线程A在检查点123和124之间...
对象历史记录:每个对象都有按位或的标志,用于记录已发生的操作(至少一次),并将其记录为某个值,该值会记录故障。对象B调用了Format(),但未调用Init()。