unity ar core_内部概览:Core Foundation团队和改进Unity的测试实践

unity ar core

Pictured above: Me (right) fighting a developer (left) about our testing practices and standards.

上图:我(右)与开发人员(左)就我们的测试实践和标准展开争执。

As a Software Developer Engineer in Test (SDET) on the Core Foundation team, I would like to share with you our findings from a testing workshop we held recently. Core Foundation is composed of industry veterans with autonomy to focus on the highest-impact changes that will shape Unity’s codebase and our developer workflows. Among our goals are reducing iteration time, increasing quality, and reducing waste, but we discovered that being a living example of best practices for our peers is something we are really passionate about. That’s why we’ve invested in things like Bulletproof Monday (where we spend 20% of our time on personal projects each Monday); writing guidelines and being part of the code convention discussion; or organizing a 5-minute workout every other hour at the office!

作为Core Foundation团队的测试软件开发工程师(SDET),我想与您分享我们最近举办的测试研讨会的发现。 Core Foundation由经验丰富的行业资深人士组成,他们具有自主权,专注于将影响Unity代码库和我们开发人员工作流程的影响最大的变更。 我们的目标之一是减少迭代时间,提高质量并减少浪费,但是我们发现,成为同行的最佳实践的生动典范是我们真正的热情所在。 这就是为什么我们投资于“防弹星期一”(每个星期一我们将20%的时间用于个人项目)之类的原因; 编写指南并参与代码约定讨论; 或每隔一小时在办公室组织一次5分钟的锻炼!

改进测试实践 (Improving testing practices)

At Unity, we work with amazingly skillful colleagues, but many come from an industry with testing practices that, in my opinion, are not best-suited for running software as a service. Test automation benefits from having solid guidelines as much as any other piece of code, so we decided it was time well spent for the team SDETs to prepare a short workshop, sharing what we find in early (and mainly automated) testing, and discussing where can we have the biggest impact. We strive to be the example everyone can rely on, so our testing practices have to be a way to move forward.

在Unity,我们与技术精湛的同事一起工作,但是许多人来自具有测试实践的行业,在我看来,这些实践并不适合运行软件即服务。 测试自动化与其他任何代码一样,都具有坚实的指导原则,因此,我们认为SDET团队花了很多时间来准备一个简短的研讨会,分享我们在早期(主要是自动化)测试中发现的内容,并讨论在哪里我们能产生最大的影响吗? 我们努力成为每个人都可以依靠的榜样,因此我们的测试实践必须成为前进的一种方式。

测试车间 (Testing workshop)

The workshop consisted of three parts, as well as a 30-minute warm-up doing a Test Driven Development (TDD) kata, followed by a discussion about TDD. For the first part of the workshop, we introduced a set of bugs on an independent branch, triggered our current automation, and let our developers debug it and write some tests that will aid in future debugging. We did it in small teams so the developers could pair while the SDETs provided help and kept the exercise meaningful. For instance, here’s one of the bugs we purposefully introduced for the workshop (it was about small bugs that might slip through our review process):

该研讨会由三个部分组成,以及30分钟的热身做一个测试驱动开发(TDD) 卡塔 ,随后约TDD的讨论。 在研讨会的第一部分中,我们在一个独立的分支上引入了一组错误,触发了我们当前的自动化,并让我们的开发人员对其进行调试并编写了一些有助于将来进行调试的测试。 我们是在小型团队中进行的,因此开发人员可以在SDET提供帮助的同时进行配对,并使练习有意义。 例如,这是我们在研讨会上故意引入的错误之一(与可能会贯穿我们的审查过程的小错误有关):

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
float Matrix4x4f::GetDeterminant() const
{
    double m00 = Get(0, 0);  double m01 = Get(0, 1);  double m02 = Get(0, 2);  double m03 = Get(0, 3);
    double m10 = Get(1, 0);  double m11 = Get(1, 1);  double m12 = Get(1, 2);  double m13 = Get(1, 3);
    double m20 = Get(2, 0);  double m21 = Get(2, 1);  double m22 = Get(2, 2);  double m23 = Get(2, 3);
    double m30 = Get(3, 0);  double m31 = Get(3, 1);  double m32 = Get(3, 2);  double m33 = Get(3, 3);
    double result =
        m03 * m12 * m21 * m30 - m02 * m13 * m21 * m30 - m03 * m11 * m22 * m30 + m01 * m13 * m22 * m30 +
        m02 * m11 * m23 * m30 - m01 * m12 * m23 * m30 - m03 * m12 * m20 * m31 + m02 * m13 * m20 * m31 +
        m03 * m10 * m22 * m31 - m00 * m13 * m22 * m31 - m02 * m10 * m21 * m31 + m00 * m12 * m23 * m31 +
        m03 * m11 * m20 * m32 - m01 * m13 * m20 * m32 - m03 * m10 * m23 * m32 + m00 * m13 * m21 * m32 +
        m01 * m10 * m23 * m32 - m00 * m11 * m23 * m32 - m02 * m11 * m20 * m33 + m01 * m12 * m20 * m33 +
        m02 * m10 * m21 * m33 - m00 * m12 * m21 * m33 - m01 * m10 * m22 * m33 + m00 * m11 * m22 * m33;
    return (float)result;
}

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
float Matrix4x4f :: GetDeterminant ( ) const
{
     double m00 = Get ( 0 , 0 ) ;    double m01 = Get ( 0 , 1 ) ;    double m02 = Get ( 0 , 2 ) ;    double m03 = Get ( 0 , 3 ) ;
     double m10 = Get ( 1 , 0 ) ;    double m11 = Get ( 1 , 1 ) ;    double m12 = Get ( 1 , 2 ) ;    double m13 = Get ( 1 , 3 ) ;
     double m20 = Get ( 2 , 0 ) ;    double m21 = Get ( 2 , 1 ) ;    double m22 = Get ( 2 , 2 ) ;    double m23 = Get ( 2 , 3 ) ;
     double m30 = Get ( 3 , 0 ) ;    double m31 = Get ( 3 , 1 ) ;    double m32 = Get ( 3 , 2 ) ;    double m33 = Get ( 3 , 3 ) ;
     double result =
         m03 * m12 * m21 * m30 - m02 * m13 * m21 * m30 - m03 * m11 * m22 * m30 + m01 * m13 * m22 * m30 +
         m02 * m11 * m23 * m30 - m01 * m12 * m23 * m30 - m03 * m12 * m20 * m31 + m02 * m13 * m20 * m31 +
         m03 * m10 * m22 * m31 - m00 * m13 * m22 * m31 - m02 * m10 * m21 * m31 + m00 * m12 * m23 * m31 +
         m03 * m11 * m20 * m32 - m01 * m13 * m20 * m32 - m03 * m10 * m23 * m32 + m00 * m13 * m21 * m32 +
         m01 * m10 * m23 * m32 - m00 * m11 * m23 * m32 - m02 * m11 * m20 * m33 + m01 * m12 * m20 * m33 +
         m02 * m10 * m21 * m33 - m00 * m12 * m21 * m33 - m01 * m10 * m22 * m33 + m00 * m11 * m22 * m33 ;
     return ( float ) result ;
}

I know all of you are math experts, so it will be really easy to find! Of course, it wasn’t as easy for our team members, as the only information they had was the unit tests report, where some apparently unrelated tests were failing. As we were working with unit test, one of our priorities is “good feedback is fast feedback”, so we were checking how long the team needed to find the problem. It took some time to pinpoint but, after discovering where the bug was, some questions arose: How can we add tests that will give us better information if this happens again? How much more testing is required? What can we do to avoid a regression on this change?

我知道你们都是数学专家,所以这真的很容易找到! 当然,对于我们的团队成员而言,这并不容易,因为他们掌握的唯一信息是单元测试报告,其中一些显然 无关的测试失败了。 在进行单元测试时,我们的重点之一是“好的反馈就是快速的反馈”,因此我们检查了团队发现问题需要多长时间。 需要花一些时间来确定,但是在发现错误的位置之后,出现了一些问题:如果再次发生这种情况,我们如何添加测试,以便为我们提供更好的信息? 需要多少测试? 我们如何避免这种变化的回归?

The answer to the first question was easy: just add one unit test calculating the correct determinant of a given matrix. But the important question was the second one. Was that enough? Well, this was a synthetical exercise, as GetDeterminant hasn’t seen any change for a really long time. But, knowing some details about how to obtain a determinant, can we think of some test cases that will give us a better coverage than a handpicked matrix? Are there any tests that will result in better feedback? This is not a math class, but the calculations to obtain the determinant of a 4×4 matrix can be reduced to four different operations; having them as different tests lets you find where the problem is just by reading the test names!

第一个问题的答案很简单:只需添加一个单元测试即可计算给定矩阵的正确行列式。 但是重要的问题是第二个问题。 够了吗? 嗯,这是一个综合练习,因为GetDeterminant很长时间以来都没有看到任何变化。 但是,在了解了有关如何获取行列式的一些细节之后,我们是否可以考虑一些测试案例,这些案例将比我们精心挑选的矩阵提供更好的覆盖范围? 是否有任何测试可以带来更好的反馈? 这不是数学课程,但是可以将获得4×4矩阵行列式的计算简化为四个不同的运算;即, 将它们用作不同的测试可以使您仅通过阅读测试名称就可以找到问题所在!

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
    TEST(GetDeterminant_FirstColumn)
    {
        const float kExpectedDeterminant = 104;
        float source[16] =
        {1, 0, 0, 0,
         0, 3, 1, 7,
         0, 3, 9, 7,
         0, 2, 0, 9};
        Matrix4x4f m(source);
        float result = m.GetDeterminant();
        CHECK_EQUAL(kExpectedDeterminant, result);
    }
    TEST(GetDeterminant_SecondColumn)
    {
        const float kExpectedDeterminant = -104;
        float source[16] =
        {0, 1, 0, 0,
         3, 0, 1, 7,
         3, 0, 9, 7,
         2, 0, 0, 9};
        Matrix4x4f m(source);
        float result = m.GetDeterminant();
        CHECK_EQUAL(kExpectedDeterminant, result);
    }
    TEST(GetDeterminant_ThirdColumn)
    {
        const float kExpectedDeterminant = 104;
        float source[16] =
        {0, 0, 1, 0,
         3, 1, 0, 7,
         3, 9, 0, 7,
         2, 0, 0, 9};
        Matrix4x4f m(source);
        float result = m.GetDeterminant();
        CHECK_EQUAL(kExpectedDeterminant, result);
    }
    TEST(GetDeterminant_FourthColumn)
    {
        const float kExpectedDeterminant = -104;
        float source[16] =
        {0, 0, 0, 1,
         3, 1, 7, 0,
         3, 9, 7, 0,
         2, 0, 9, 0};
        Matrix4x4f m(source);
        float result = m.GetDeterminant();
        CHECK_EQUAL(kExpectedDeterminant, result);
    }

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
     TEST ( GetDeterminant_FirstColumn )
     {
         const float kExpectedDeterminant = 104 ;
         float source [ 16 ] =
         { 1 , 0 , 0 , 0 ,
         0 , 3 , 1 , 7 ,
         0 , 3 , 9 , 7 ,
         0 , 2 , 0 , 9 } ;
         Matrix4x4f m ( source ) ;
         float result = m . GetDeterminant ( ) ;
         CHECK_EQUAL ( kExpectedDeterminant , result ) ;
     }
     TEST ( GetDeterminant_SecondColumn )
     {
         const float kExpectedDeterminant = - 104 ;
         float source [ 16 ] =
         { 0 , 1 , 0 , 0 ,
         3 , 0 , 1 , 7 ,
         3 , 0 , 9 , 7 ,
         2 , 0 , 0 , 9 } ;
         Matrix4x4f m ( source ) ;
         float result = m . GetDeterminant ( ) ;
         CHECK_EQUAL ( kExpectedDeterminant , result ) ;
     }
     TEST ( GetDeterminant_ThirdColumn )
     {
         const float kExpectedDeterminant = 104 ;
         float source [ 16 ] =
         { 0 , 0 , 1 , 0 ,
         3 , 1 , 0 , 7 ,
         3 , 9 , 0 , 7 ,
         2 , 0 , 0 , 9 } ;
         Matrix4x4f m ( source ) ;
         float result = m . GetDeterminant ( ) ;
         CHECK_EQUAL ( kExpectedDeterminant , result ) ;
     }
     TEST ( GetDeterminant_FourthColumn )
     {
         const float kExpectedDeterminant = - 104 ;
         float source [ 16 ] =
         { 0 , 0 , 0 , 1 ,
         3 , 1 , 7 , 0 ,
         3 , 9 , 7 , 0 ,
         2 , 0 , 9 , 0 } ;
         Matrix4x4f m ( source ) ;
         float result = m . GetDeterminant ( ) ;
         CHECK_EQUAL ( kExpectedDeterminant , result ) ;
     }

The second part of the workshop involved picking an area that could benefit from better coverage, and then writing the tests together. We shared the pain of writing unit tests for legacy parts of the code and discussed how to approach the challenge. What we discovered is how much value you can add to the tests when you understand the feature, as opposed to when you base your tests only on the implementation details. Testing against the implementation doesn’t offer as much value as focusing on User Cases, or functional scenarios; and the tests may need modification when parts of the implementation are changed, even if the functionality is the same. That is one of the main reasons why it is really important to write tests while you are working on the code — adding all these layers of information, and using them as a documentation example of how the functionality should be called and its expected behaviors. When you have to add the tests at a later point, all this information is usually lost, particularly if the person responsible for adding the tests is alien to that part of the code.

研讨会的第二部分涉及选择一个可以从更好的覆盖范围中受益的区域,然后一起编写测试。 我们分担了编写代码遗留部分的单元测试的痛苦,并讨论了如何应对挑战。 我们发现的是,您了解该功能后可以为测试增加多少价值,而不是仅基于实现细节进行测试。 对实现进行测试所提供的价值不如关注用户案例或功能方案那么多。 并且在实现的某些部分发生更改时,即使功能相同,也可能需要修改测试。 这就是为什么在处理代码时编写测试非常重要的主要原因之一-添加所有这些信息层,并将它们用作应如何调用功能及其预期行为的文档示例。 当您以后必须添加测试时,所有这些信息通常都会丢失,特别是如果负责添加测试的人员与代码的那部分无关。

To wrap things up, we held an open discussion focusing on all the questions we gathered during the previous exercises, as well as general questions regarding test practices in the organization. When are we doing enough testing? How shall we deal with legacy code? How much impact does good test coverage entail when debugging? The workshop allowed us to bring the importance of testing to the table, and by the end we were able to establish guidelines that will be followed during our code review process.

总结一下,我们进行了公开讨论,重点讨论了先前练习中收集到的所有问题以及有关组织中测试实践的常规问题。 我们什么时候做足够的测试? 我们将如何处理遗留代码? 调试时良好的测试覆盖率会带来多大影响? 这次研讨会使我们能够将测试的重要性带到桌面,最后,我们能够建立准则,这些准则将在我们的代码审查过程中遵循。

结论 (Conclusion)

Yes, I know, I can clearly hear the question that you’ve been chewing on: what was the purpose of this? What was the impact? Some of what we achieved during the workshop included the different groups inside the team having many “eureka” moments regarding testing; gaining an understanding of the pain points our Sustained Engineering peers suffer when debugging and patching legacy code; and agreeing on important (and less important) details about testing that should be discussed in our reviews. But it’s way more than that. The workshop also served as a way to demonstrate the influence of having someone taking an SDET’s role on a team used to other approaches to testing, and it also affected some of the group dynamics. It raised awareness of testing practices in our team, and it served as an example of good practices that we can show to the rest of the company. It is still too early to measure the entirety of the impact, but I feel that it has already paid off the 2 days we invested on it. A fun example of the effect was when Scott (our team lead) shared this message while working on his Hackweek 2017 project:

是的,我知道,我可以清楚地听到您一直在思考的问题:这样做的目的是什么? 有什么影响? 在研讨会上,我们取得了一些成就,其中包括团队中的各个小组,他们在测试方面有很多“尤里卡”时刻。 了解调试和修补旧代码时我们的可持续工程团队遭受的痛苦点; 并就应该在我们的评论中讨论的有关测试的重要(和较不重要)细节达成共识。 但是,不仅如此。 该讲习班还可以用来证明让某人担当SDET角色对习惯于其他测试方法的团队的影响,并且还影响了某些团队动态。 它提高了我们团队中对测试实践的认识,并且是可以向公司其他部门展示的良好实践的一个示例。 衡量影响的全部还为时过早,但是我认为它已经收回了我们花在这两天上的投资。 一个有趣的例子就是斯科特(我们的团队负责人) 在进行Hackweek 2017项目时分享了此消息:

But I am eager to know if you have experienced a similar activity in your organization. What is your approach to testing? If you are working in the game industry, do you have a role similar to SDET? Are you using any test automation? Also remember that Unity is always looking for talent, and I would love to have a new SDET peer! Please check out our careers page.

但是我很想知道您是否在组织中经历过类似的活动。 您的测试方法是什么? 如果您从事游戏行业,您是否具有与SDET类似的角色? 您是否正在使用任何自动化测试? 还请记住,Unity一直在寻找人才,我很想拥有一个新的SDET同行! 请查看我们的职业页面

Feel free to contact me on through Linkedin.

随时通过Linkedin与我联系。

翻译自: https://blogs.unity3d.com/2017/07/21/a-look-inside-core-foundation-team-and-improving-unitys-testing-practices/

unity ar core

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值