[转载]基于TFS实践敏捷-实现用户场景

您是新用户的 Visual Studio 应用程序生命周期管理 (ALM) 和 Team Foundation Server (TFS) 吗? 您想知道如何您和您的团队可以获得最大受益的这些工具来生成您的应用程序的最新版本?

然后花几分钟就可以走逐步完成该两个章节教程,并按照 Peter 和朱丽亚在 Fabrikam 纤程的两个开发人员的生活的一天 — — 虚构的公司,提供有线电视和相关的服务。 您将看到如何使用 Visual Studio 和 TFS 签出并更新代码、 暂停工作时被打断、 请求代码评审、 签入您的更改,并执行其他任务的示例。

Peter 简要介绍了他积压工作,并选择他目前从事的任务。 他写他计划开发的代码的单元测试。 通常情况下,他将在一小时内多次运行测试,则逐渐编写更详细的测试,然后编写代码,使其传递。 他经常与将使用的方法写入他的同事讨论他的代码的接口。


说明


本主题讨论了我的工作和代码覆盖率功能都只能在Visual Studio 高级专业版和 Visual Studio 旗舰版.

本主题中

  • 查看个人的积压工作,并准备好开始工作的任务
  • 创建第一个单元测试
  • 创建新代码的存根 (stub)
  • 运行第一个测试
  • 同意该 API
  • 红、 绿、 重构命令。.
  • 代码覆盖率
  • 何时我们完成?
  • 签入更改

查看个人的积压工作,并准备好开始工作的任务

在团队资源管理器,Peter 打开 我的工作页。 团队已同意在当前冲刺 (sprint) 期间 Peter 将从事评估发票状态,产品积压工作中的最高优先级别项。 王建军决定着手实施的数学函数,子任务的最高优先级别的积压工作项。 他将此任务从可用的工作项 列出到 在进度的工作项 & 更改列表。

若要查看个人的积压工作,并准备好开始工作的任务

  1. 在团队资源管理器:
    1. 如果您已经不连接到团队项目,您想要在中,然后连接到团队项目
    2. 选择主页,然后选择我的工作。
  1. 在我的工作 页上,拖动任务从 可用的工作项 列表到 中进行工作项部分。
    您还可以选择在任务可用工作项 列表,然后选择 开始。

草拟增量工作计划

Peter
通常会制定一系列的小步骤中的代码。 每个步骤通常时间不会长于一个小时,并且可能需要短短十分钟。
在每个步骤中,他将新的单元测试,并更改,以便它可以通过新的测试,除了他已写入的测试,他正在开发的代码。
有时他写之前更改该代码,新的测试,他有时会在编写测试之前更改的代码。 有时他重构。 也就是说,他只是提高代码而无需添加新的测试。
他永远不会更改通过,测试,除非他决定它正确地表示一项要求。

在每一小步结束时,他将负责相关的代码的这一区域的所有单元测试。
他不会考虑该步骤完成之前,每个测试通过。

但是,他不会检查该代码到
Team Foundation Server 直到他完成整个任务。

Peter
写下这一系列小幅度的大致规划。 他知道准确的详细信息和最新的顺序可能会更改为他工作。 这是他最初的列表为此特定任务的步骤:

  1. 创建测试方法存根 (stub) — —
    即只的方法的签名。
  2. 满足特定的一个典型事例。
  3. 测试范围广泛。 请确保代码正确响应大范围值。
  4. 负的异常。 正常处理不正确的参数。
  5. 代码覆盖率。 请确保至少 80%的代码被执行单元测试。

一些他的同事们在他们的测试代码中的注释编写这种计划。
其他人只需记住他们的计划。 Peter 查找有用的任务工作项的描述字段中写入他列出的步骤。
如果他认为暂时转用更紧迫的任务,他知道在哪里可以找到列表,在他能够返回到它。

创建第一个单元测试

Peter
首先创建单元测试。 他开始与单元测试因为他要编写使用他的新类的代码的示例。

这是他正在测试,该类库的第一个单元测试,因此他创建了一个新的单元测试项目。
他打开新项目 对话框并选择 Visual C#, 测试,然后 单元测试项目。

单元测试项目提供了 C# 文件到其中,他可以编写他的示例。 在此阶段中,他只是要说明他新方法之一调用方式:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Fabrikam.Math.UnitTest
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        // Demonstrates how to call the method.
        public void SignatureTest()
        {
            // Create an instance:
            var math = new Fabrikam.Math.LocalMath();

// Get a value to calculate:
            double input = 0.0;

// Call the method:
            double actualResult = math.SquareRoot(input);

// Use the result:
            Assert.AreEqual(0.0, actualResult);
        }
    }
}

他在测试方法中会写入示例是因为他希望他写入了他的代码时,本示例才有效。

若要创建一个单元测试项目和方法

通常会创建新的测试项目,每个项目正在进行测试。 如果已存在一个测试项目,您可以只是添加新的测试方法和类。

此过程使用 Visual Studio 的单元测试框架,但您还可以使用来自其他提供商的框架。 测试资源管理器的工作原理与其他框架,很好,只要您安装相应的适配器。

  • 如果不存在,请创建一个测试项目。
    • 在新项目 对话框中,选择一种语言如 Visual Basic, Visual C++ 或 Visual C#。 选择测试 和 单元测试项目。
  • 将测试添加到测试类提供。 每个单元测试是一种方法。
    必须为每个单元测试将前缀TestMethod特性,并且单元测试方法应具有无参数。 您可以使用任何所需的单元测试方法的名称:

    [TestMethod]
            public void SignatureTest()
            {...}         
  • 每个测试方法应调用的方法Assert类,以指示它是否已通过或失败。 通常情况下,您验证操作的预期和实际结果相等:

    Assert.AreEqual(expectedResult, actualResult);
  • 测试方法可以调用其他普通的方法不具有TestMethod特性。
  • 可以将测试组织到多个类。 每个类的前面必须有TestClass特性。

    [TestClass]
    public class UnitTest1
    { ... }

有关如何在 c + + 编写单元测试的详细信息,请参阅 用 Microsoft 适用于 C++ 的单元测试框架编写 C/C++ 单元测试.

创建新代码的存根 (stub)

接下来,Peter 将为他的新代码创建一个类库项目。 目前正在开发的代码项目和单元测试项目。 他从测试项目中添加引用的项目正在开发的代码。

在新项目中,他将添加新类,并将至少允许要成功生成的测试方法的最低版本。 要做到这一点的最快方法是从测试中的调用生成的类和方法的存根。

public double SquareRoot(double p)
        {
            throw new NotImplementedException();
        }

若要从测试生成的类和方法

首先,创建要添加新的类,除非它已存在的项目。

若要生成的类

  1. 将光标放在要从中生成,例如,类的示例 LocalMath. 在快捷菜单中,选择生成代码, 新型。
  2. 在新型 对话框中,将 项目的类库项目。 在此示例中,它是 Fabrikam.Math。

若要生成方法

  • 例如,将光标放在该方法中,调用 SquareRoot. 在快捷菜单中,选择生成代码, 方法存根 (stub)。

运行第一个测试

Peter 生成并运行测试,通过按 CTRL + R、 t。 测试结果显示了一个红色的失败指标和出现在列表中的下一个测试失败测试。

对代码进行简单的更改:

public double SquareRoot(double p)
        {
            return 0.0;
        }

他再次运行该测试并将它传递:

运行单元测试

  • 测试 菜单上,选择 运行, 的所有测试
    -或者-
  • 如果测试资源管理器中打开,请选择所有运行
    -或者-
  • 请将光标置于某个测试代码文件,然后按 CTRL + R、
    T
  • 如果下一个测试出现失败测试
    测试,例如,双击以打开该名称。
    显示测试未通过的点。

若要测试的完整列表请参阅
选择 显示所有。 若要返回到的摘要,请选择主页视图

若要查看详细信息的测试结果后
测试资源管理器中选择该测试。

若要定位到代码的测试
中双击该测试中测试资源管理器中,或者选择打开测试在快捷菜单上。

若要调试测试
打开的一个或多个测试,快捷菜单,然后选择调试选中的测试

若要在后台中运行测试时生成解决方案
切换生成后运行的测试。 第一个运行以前失败的测试。

同意该接口

Peter 调用
Lync,他的同事朱丽亚和共享他的屏幕。 她将用自己的组件。 他显示了他最初的示例。

朱丽亚认为该示例是确定,但注释,"大量的功能将通过该测试"。

Peter
答复,"是第一次测试只是为了确保名称和函数的参数正确。 现在我们可以编写捕获此函数的主要要求的测试。"

在一起他们编写下面的测试:

[TestMethod]
        public void QuickNonZero()
        {
            // Create an instance to test:
            LocalMath math = new LocalMath();

// Create a test input and expected value:
            var expectedResult = 4.0;
            var inputValue = expectedResult * expectedResult;

// Run the method:
            var actualResult = math.SquareRoot(inputValue);

// Validate the result:
            var allowableError = expectedResult/1e6;
            Assert.AreEqual(expectedResult, actualResult, allowableError,
                "{0} is not within {1} of {2}", actualResult, allowableError, expectedResult);
        }

提示


此函数中,彼得使用测试第一个开发,他首先将写入的特征的单元测试,然后将满足测试的代码。 在其他情况下,他发现这种做法是不现实的所以相反,他在编写代码后,他会写测试。 但他认为编写单元测试非常重要 — — 是否前面或后面的代码 — — 因为它们使代码保持稳定。

红、 绿、 重构命令

Peter 遵循的周期中他反复写一个测试并确认它无法正常工作,在编写代码以使该测试通过,然后考虑重构和 — — 即提高代码,而无需更改测试。

红色

Peter 按下 CTRL + R T 运行 Julia 用他创建的新测试。 他将所有测试之后,他始终运行它,以确保它出现故障之前他都写的代码,使其通过。 这是他了解到他忘了将断言放在他写了一些测试后一种做法。 查看失败的结果,他可以放心当他通过,测试结果正确指示要求已得到满足。

另一种有益的做法是将设置生成后运行的测试。 此选项将运行测试在后台每次生成解决方案,以便您具有连续的测试代码的状态报告。 Peter 已开始时它可能会使 Visual Studio 的缓慢响应,但他发现这很少发生可疑。

绿色

Peter 写他第一次尝试的方法,他正在开发的代码:

 public class LocalMath
    {
        public double SquareRoot(double x)
        {
            double estimate = x;
            double previousEstimate = -x;
            while (System.Math.Abs(estimate - previousEstimate) > estimate / 1000)
            {
                previousEstimate = estimate;
                estimate = (estimate * estimate - x) / (2 * estimate);
            }
            return estimate;
        }

Peter 将再次运行测试并通过了所有测试:

重构

现在,该代码将执行它的主要作用,Peter 探讨寻找方法使它更好,或以使其更易于在将来更改的代码。 他意识到他可以减少在循环中执行计算的数量:

public class LocalMath
    {
        public double SquareRoot(double x)
        {
            double estimate = x;
            double previousEstimate = -x;
            while (System.Math.Abs(estimate - previousEstimate) > estimate / 1000)
            {
                previousEstimate = estimate;
                estimate = (estimate + x / estimate) / 2;
                //was: estimate = (estimate * estimate - x) / (2 * estimate);
            }
            return estimate;
        }

他核实仍然通过了测试:


提示


每次更改您进行开发代码时应重构或扩展:

  • 重构功能意味着您不要更改测试,因为您不要添加新的功能。
  • 扩展意味着添加测试,而通过现有的和新的测试所需的代码更改。

如果要更新现有代码已更改的要求,您将同时删除旧不再代表当前要求的测试。

应避免更改的已通过测试。 而是添加新的测试。 仅编写表示实际要求的测试。

每次更改后运行的测试。

...,然后重复

Peter 继续他系列的扩展和重构的步骤,作为一个粗略的使用他的小步骤的列表。 他始终不在每个扩展之后, 执行重构的步骤,他有时连续执行重构的多个步骤。 但他始终对代码运行单元测试在每次更改后。

有时,他补充道,不需要任何更改的代码,但到他确信他的代码能够正常工作,它将一个测试。 例如,他要确保该函数可以通过输入一系列工作。 他将更多的测试,这样:

[TestMethod]
        public void SqRtValueRange()
        {
            LocalMath math = new LocalMath();
            for (double expectedResult = 1e-8;
                expectedResult < 1e+8;
                expectedResult = expectedResult * 3.2)
            {
                VerifyOneRootValue(math, expectedResult);
            }
        }
        private void VerifyOneRootValue(LocalMath math, double expectedResult)
        {
            double input = expectedResult * expectedResult;
            double actualResult = math.SquareRoot(input);
            Assert.AreEqual(expectedResult, actualResult, expectedResult / 1e6);
        }

此测试通过第一次运行:

只是为了确保这个结果不是一个错误,他暂时引入一个小错误他测试,以使其失败。 后出现故障,他修复了它再次。


提示


始终进行一次测试之前使其通过失败。

例外情况

彼得现在移到优异的输入为编写测试:

[TestMethod]
        public void RootTestNegativeInput()
        {
            LocalMath math = new LocalMath();
            try
            {
                math.SquareRoot(-10.0);
            }
            catch (ArgumentOutOfRangeException)
            {
                return;
            }
            catch
            {
                Assert.Fail("Wrong exception on negative input");
                return;
            }
            Assert.Fail("No exception on negative input");
        }

此测试将代码放到一个循环。 他又要使用取消测试资源管理器中的按钮。 这将在 10 秒内终止代码。

Peter 想要确保无限循环可能会生成服务器上发生。 虽然服务器施加在完成超时运行,它是非常长的超时值,并会导致长时间延迟。 因此,他将显式超时添加到此测试:

 [TestMethod, Timeout(1000)]
        public void RootTestNegativeInput()
        {...

显式超时使测试失败。

Peter 然后更新代码以处理此种例外情况:

  public double SquareRoot(double x)
        {
            if (x <= 0.0)
            {
                throw new ArgumentOutOfRangeException();
            }

回归

新测试通过,但没有回归。 现在将失败的测试,用来传递:

Peter 查找并修复错误:

 public double SquareRoot(double x)
        {
            if (x < 0.0)  // not <=
            {
                throw new ArgumentOutOfRangeException();
            }

它固定后,所有测试都通过:


提示


确保每个测试阶段后对代码所做的每项更改。

代码覆盖率

按时间间隔期间他的工作,并最后他在签入代码之前,Peter
获取代码覆盖率报告。 此图显示代码中有多少已经执行他测试。

Peter
先生的团队的目标是至少 80%的覆盖率。 由于可能很难实现高覆盖率为此类型的代码,它们可以放宽对生成的代码,此要求。

良好的覆盖范围并不保证该组件的全部功能都已经过测试,并不保证该代码将用于每个输入值的范围。
然而,没有任何相当关闭关系之间的代码行的覆盖范围和覆盖范围的组件的行为的空间。 因此,良好的覆盖范围可增强团队的信心,他们正在测试大部分应有的行为。

若要获取代码覆盖率报表中,在测试
菜单上,选择 运行, 分析所有测试的代码覆盖率。 然后再次运行所有测试。

Peter
获取总没有覆盖到的 86%。 当他在扩展中报表的合计时,它表明他正在开发的代码覆盖率 100%。 由于接受测试的代码是重要的分数,这是非常令人满意。
未覆盖的部分实际上是在自己的测试。 通过切换显示代码覆盖率突出显示按钮,Peter 可以看到尚未测试的测试代码中的哪些部分。
但是,他决定这些部分是覆盖范围并不重要,因为它们所测试的代码中,如果检测到错误,则将只使用。

若要验证特定测试达到到代码的特定分支,您可以设置显示代码覆盖率突出显示
,然后通过运行单个测试 运行在其快捷菜单上的命令。

何时我们完成?

Peter
继续更新中少量的代码步骤,直到他是满意的:

  • 所有可用的单元测试通过。
    具有非常大的一组单元测试的项目,可以是为开发人员等待他们所有运行不切实际。
    相反,该项目运行封闭的签入服务,所有运行自动的测试的每个签入的搁置集之前它将合并到源树。 在签入被拒绝在运行失败时。
    这样,开发人员可以在自己的计算机上运行的最少量的单元测试,然后继续其他工作,而不会中断生成的风险。 有关详细信息,请参阅 使用封闭签入生成过程以验证更改.
  • 代码覆盖率满足团队的标准。 75%是典型的项目的要求。
  • 他的单元测试模拟是必需的其中包括典型和优秀输入行为的各个方面。
  • 他的代码易于理解和扩展。

所有这些条件都满足,Peter
已准备就绪,他代码签入到源代码管理。

使用单元测试的代码开发的原则

彼得在开发代码时适用以下原则:

  • 开发单元测试和代码,并经常在开发过程中运行它们。
    单元测试表示组件的规格。
  • 除非需求已更改或测试了错误,则不会更改单元测试。
    添加新测试逐渐扩展代码的功能。
  • 目标至少 75%的被测试所覆盖的代码。
    时间间隔以及签入源代码之前查看代码覆盖率结果。
  • 签入代码和单元测试,以便它们可以通过连续或定期运行服务器构建。
  • 在实际应用,对于每个功能中,编写单元测试第一次。
    开发满足它的代码之前执行此操作。

签入更改

之前他将更改签入,Peter
再次使用 Lync 与他的同事 Julia 共享他的屏幕,这样她可以非正式的方式,并以交互方式查看与他他已创建。
测试仍然是他们讨论的重点,因为朱丽亚是主要感兴趣的代码不会,没有它的工作原理。 朱丽亚同意 Peter 已写的内容能满足她的需要。

Peter
签入所有他具有所做的更改,包括测试和代码,并将它们与完成他的任务相关联。 在签入团队的团队自动的生成系统来验证使用该小组的 CI
生成的生成过程他更改进行排队。 此生成过程,从而帮助最小化中的错误团队及其基本代码的生成和测试 — 在干净环境中独立的开发计算机 — — 团队取得的每项更改。

当生成完成时,将通知
Peter。 在生成结果窗口、 成功生成的他看到和所有测试传递。

若要签入的更改

  1. 在菜单栏中,选择查看,
    团队资源管理器。
  2. 在团队资源管理器,选择 主页 ,然后选择 我的工作。
  3. 在我的工作 页面上,选择 签入。
  4. 检查的内容挂起的更改页面,以确保:
    • 中列出了所有相关的更改包含更改
    • 所有相关的工作项中列出相关的工作项。
  5. 指定注释若要帮助您的团队了解这些更改的目的,当他们考虑的已更改的文件和文件夹的版本控制历史记录。
  6. 选择签入。

持续集成代码

有关如何定义一个持续集成生成过程的详细信息,请参阅
设置 CI 生成.
您已经设置了此生成过程后,您可以选择要通知的团队生成的结果。

有关详细信息,请参阅 运行、监视和管理生成.

博客转自:《ALM 开发人员生活中的一天:为用户情景编写新代码》

时间: 2024-10-26 02:49:15

[转载]基于TFS实践敏捷-实现用户场景的相关文章

基于TFS的.net技术路线的云平台DevOps实践

DevOps是近几年非常流行的系统研发管理模式,很多公司都或多或少在践行DevOps.那么,今天就说说特来电云平台在DevOps方面的实践吧. 说DevOps,不得不说DevOps的具体含义.那么,DevOps是什么呢?是开发+运维么?每个人都DevOps的理解都不尽相同,下面是一组对DevOps的定义,通过这组定义,我们基本可以看清DevOps是干啥的.在这众多的解释中,我认为有一种解释可以更贴切:DevOps是一种能力,具备此能力的团队可以高质量.快速的交付软件产品或服务.这个总结定义道出了

四渎《构建之法》——计划估计、敏捷流程、项目经理和用户场景

本周再次打开<构建之法>,这次我阅读时重点在于学习敏捷流程.项目经理和用户场景等相对较为宏观的内容. 第六章开篇即简单地介绍了敏捷开发的流程:Product Backlog->Sprint Backlog->Sprint->软件的增量发布.同时提出了一些敏捷开发的特色之处:团队成员自己主导任务的估计和分配,使其能动性得到较大的发挥:通过每日"例"会进行面对面的交流,报告工作进度.今日要工作的内容.遇见的问题:通过燃尽图或看版图展现项目进度.这是一种和我们之

【用户分析-用户场景】这TM才是产品思维!

@奶牛Denny :很长一段时间里,市场推广/营销(Marketing)在中国似乎是一个大家很忌讳的词汇.市场推广无非就是夸大包装,炒作一下,卖卖情怀——很多人都是这么觉得的,因为确实有一部分急功近利者是这么干的. 这些人,错过了很多的乐趣. 所以我想分享一个自己工作中的真实案例,来弥补一些乐趣.如果以自己的创业项目为例,总有王婆卖瓜之嫌,所以我要说的这个案例,是在一个大公司“内部创业”的故事. 今年上半年的时候我还在大众点评工作,负责其Marketing.而在四月至六月的这段时间里,点评打响了

[翻译]用一个用户场景来掌握它们

翻译自一篇博文,原文:One user scenario to rule them all 异步系列 剖析C#中的异步方法 扩展C#中的异步方法 C#中异步方法的性能特点. 用一个用户场景来掌握它们 c#中异步方法的几乎所有重要行为都可以基于一个用户场景进行解释:尽可能简单地将现有的同步代码迁移到异步.你应该能在方法的返回类型前面加上async关键字,在方法名最后加上Async后缀,在方法内部加上一些await关键字,就能得到一个功能完整的异步方法. 这个"简单"场景以许多不同的方式极

在实践中进行用户测试和评估

2019工业信息安全技能大赛个人线上赛第一场圆满收官.在保障竞赛平台成功稳定运行的同时,更多的还是思考竞赛平台在本次支撑过程中的用户体验,用户测试与评估情况.作为专业为工业信息安全打造的人才测评选拔和竞赛平台,实际的竞赛活动就是检验和测试产品最好的试金石,也是用户测试环节的最佳测试环境. 在产品设计与优化过程中,产品用户测试与评估环节是指请符合测试要求的目标用户来完成一些特定或具有代表性的任务,观察并记录用户使用产品的整个过程,包括用户使用的顺畅程度和情绪等多个方面.测试过程不是应对用户的不满和

典型用户与用户场景

懒学生 名字:    张三 性别.年龄:男,20岁 职业:某学院的学生 收入:无正式收入 知识层次和能力:大学 生活情况:生活比较懒散 用户偏好:热爱玩游戏,所以懒惰 典型场景:让别人带饭,以及帮忙取快递 典型描述:能休息会,就不会犹豫 用户场景 典型用户 张三 用户需求:张三会把自己的需求发布到易达软件,进行悬赏 假设李四也是软件的用户,他看到了这个悬赏,并且离他很近就可以完成悬赏 那么他就会接收悬赏,然后交给张三物品,同时支付相应的悬赏金. 那么这个任务也就相应的完成. 最后张三在软件上给李

私人助手-典型用户和用户场景分析

典型用户: (1)名字:洪烨. (2)年龄:20. (3)收入:0. (4)代表的用户在市场上的比例和重要性:代表的大学生用户比例较大.重要. (5)使用这个软件的典型场景:对于每门课程都有各种作业,她有的时候都不知道该做什么,最后弄得好多作业都堆了起来,也老是贴小纸条提醒自己每天什么时候背单词,什么时候去跑步等等.她就很需要这个私人助手软件有效率的提醒她什么时候该做什么事,就不用担心不知道做什么或者忘记什么时间该做什么事. (6)使用本软件/服务的环境:宿舍.教室.家里等等. (7)生活/工作

团队项目-典型用户及用户场景分析

一.典型用户分析 姓名 Jenny 年龄 20 职业 在校大学生 收入 无收入,月支出600左右 所代表的用户类型 在校大学生,并且对截图需求量比较大: 使用此软件的典型场景 1.在写实验报告时需将实验结果进行截图: 2.在浏览网页时,网页的长度大于屏幕的长度,需要滚动截屏的: 使用此软件/服务的环境 宿舍:机房. 生活/工作状况 在校大学生,需要大量截图 知识层次和能力(教育程度,对电脑和万维网的熟悉程度) 大学生,熟悉电脑的基本操作,对万维网段饿使用率较高 用户的动机.目的和困难(困难=需要

结队开发——典型用户以及用户场景分析

一.典型用户 姓名 天意 性别 男 年龄 20 职业 学生 支出 每月550元左右 代表的用户在市场上的比例和重要性 比例:绝大多数理工科大学生 重要性:软件主要用户之一,需求较大 使用这个软件的典型场景 做实验时,截图记录实验过程: 查资料时,发现有用资源但不想注册或使用下载豆,通过滚动截屏保存成图片格式. 使用本软件/服务的环境 宿舍.自习室.机房 生活/工作情况 上机课程,在自己的笔记本或机房电脑上做实验 知识层次和能力(教育程度,对电脑.万维网的熟悉程度) 教育程度较高,熟悉并经常使用电