找回密码
 FreeOZ用户注册
查看: 2420|回复: 16
打印 上一主题 下一主题

[论坛技术] 使用模拟对象(Mock Object)技术进行测试驱动开发

[复制链接]
跳转到指定楼层
1#
发表于 17-12-2008 16:20:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
FROM: http://www.ibm.com/developerwork ... ject/?ca=drs-tp4608
级别: 初级
方 世明 ([email=fangshim@cn.ibm.com?subject=%E4%BD%BF%E7%94%A8%E6%A8%A1%E6%8B%9F%E5%AF%B9%E8%B1%A1%EF%BC%88Mock%20Object%EF%BC%89%E6%8A%80%E6%9C%AF%E8%BF%9B%E8%A1%8C%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91]fangshim@cn.ibm.com[/email]), 软件工程师, IBM

2008 年  12 月  12 日
测试驱动开发是敏捷开发中重要部分。在现实项目中,开发人员通常希望减少对其它模块的依赖,把测试的单元与系统其它单元隔离。本文介绍敏捷开发并探讨测试驱动开发的重要性。对 mock 技术进行理论分析,并结合当前流行的 mock 软件如 jMock 和 EasyMock等,展示测试驱动开发实例并进行比较。
                        敏捷开发
                        敏捷软件开发又称敏捷开发,是一种从上世纪 90 年代开始引起开发人员注意的新型软件开发方法。和传统瀑布式开发方法对比,敏捷开发强调的是在几周或者几个月很短的时间周期,完成相对较小功能,并交付使用。在项目周期内不断改善和增强。
                        2001 年初,在美国犹他州雪鸟滑雪胜地,17 名编程大师分别代表极限编程、Scrum、特征驱动开发、动态系统开发方法、自适应软件开发、水晶方法、实用编程等开发流派,发表“敏捷软件开发”宣言。其内容主要包括:
                       
  • 人和交互重于过程和工具;
  • 可以工作的软件重于求全责备的文档;
  • 客户协作重于合同谈判;
  • 随时应对变化重于循规蹈矩;
                        可见在敏捷软件开发中,交付高质量的软件是非常重要的。只有交付可以工作的软件,开发人员才能不断地完成更多功能,而不是将大部分时间投入在修复软件产品缺陷 (Bug) 。所以如何提高交付软件的质量,在敏捷开发实施过程非常重要。
                       

                               
登录/注册后可看大图


                               
登录/注册后可看大图

                               
登录/注册后可看大图


                               
登录/注册后可看大图

回页首


测试驱动开发
                        测试驱动开发,它是敏捷开发的最重要的部分。方法主要是先根据客户的需求编写测试程序,然后再编码使其通过测试。在敏捷开发实施中,开发人员主要从两个方面去理解测试驱动开发。
                       
  • 在测试的辅助下,快速实现客户需求的功能。通过编写测试用例,对客户需求的功能进行分解,并进行系统设计。我们发现从使用角度对代码的设计通常更符合后期开发的需求。可测试的要求,对代码的内聚性的提高和复用都非常有益。
  • 在测试的保护下,不断重构代码,提高代码的重用性,从而提高软件产品的质量。可见测试驱动开发实施的好坏确实极大的影响软件产品的质量,贯穿了软件开发的始终。
                        在测试驱动开发中,为了保证测试的稳定性,被测代码接口的稳定性是非常重要的。否则,变化的成本就会急剧的上升。所以,自动化测试将会要求您的设计依赖于接口,而不是具体的类。进而推动设计人员重视接口的设计,体现系统的可扩展性和抗变性。
                       

                               
登录/注册后可看大图


                               
登录/注册后可看大图

                               
登录/注册后可看大图


                               
登录/注册后可看大图

回页首


利用伪对象 (Mock Obect) 实现接口测试
                        在实施测试驱动开发过程中,我们可能会发现需要和系统内的某个模块或系统外某个实体交互,而这些模块或实体在您做单元测试的时候可能并不存在,比如您遇到了数据库,遇到了驱动程序等。这时开发人员就需要使用 MO 技术来完成单元测试。
                        最开始,Mock Object 是完全由测试者自己手工撰写的。在这里我们可以举个简单的例子。
                        我们有一个移动数字电视卡的接口程序。
                       
清单 1. VideoCardInterface 代码
                               
public interface VideoCardInterface {

         public void open();

         public void changeChannel(int i);

         public void close();

         public Byte[] read();

         public boolean fault();
}

                        下面是每个方法的功能说明:
                       
  • open  打开移动数字电视卡。
  • changeChannel 切换移动数字电视频道。必须在打开之后才可以正常工作,否则就提示错误信息。
  • close 关闭移动移动电视卡。必须在打开之后才可以正常工作,否则就提示错误信息。
  • read  读取字节流。必须在打开之后才可以正常工作,否则就提示错误信息。
  • fault 显示当前工作状态。
                        由于相对应的硬件开发工作还没有完成,我们无法基于这样的接口程序进行实际的测试。所以开发人员基于接口,实现了部分移动电视卡的逻辑。
                       
清单 2. MockVCHandler 代码
                               
public class MockVCHandler implements VideoCardInterface {

         private boolean initialized = false;
         private boolean error = false;
         private int channel;
         private static final int DEFAULTCHANNEL = 1;

         public void open() {
                 initialized = true;
                 channel = DEFAULTCHANNEL;
         }

         public void changeChannel(int i) {
                 if (!initialized) {
                         Assert.fail("Trying to change channel before open");
                 }
                 if (i <= 0) {
                         Assert.fail("Specified channale is out-of-range");
                 }
                 this.channel = i;
         }

         public void close() {
                 if (!initialized) {
                         Assert.fail("Trying to close before open");
                 }
         }

         public Byte[] read() {
                 if (!initialized) {
                         Assert.fail("Trying to read before open");
                         return null;
                 }
                 if (channel > 256) {
                         error = true;
                         Assert.fail("Channel is out-of-range");
                 }
                 return new Byte[] { '0', '1' };
         }

         public boolean fault() {
                 return error;
         }
}

                        通过以上的实现,我们可以测试每个动作之间的先后逻辑关系,同时可以测试数据流读取出错的逻辑。如果测试人员进一步对数据流进行测试。还可以自我生成一段二进制字节流并进行测试。通过以上的实现,我们可以大大加快开发进度。在传统开发流程中,软件开发和测试不得不等大部分硬件都已经可以使用的条件下才可以开发测试。使用 MO 技术,使得硬件和软件在一定程度上可以同步开发和测试。
                        但是测试人员完全依靠自己实现这些类,不可避免的会带来测试用例编写效率低下和测试用例编写困难的弊病,甚至可能会影响 XP 实践者“测试先行”的激情。此时,各种各样帮助创建 MockObject 的工具就应运而生了。目前,在 Java 阵营中主要的 Mock 测试工具有jMock,MockCreator,MockRunner,EasyMock,MockMaker 等,在微软的 .Net 阵营中主要是NMock,.NetMock,Rhino Mocks 和 Moq 等。
                       

                               
登录/注册后可看大图


                               
登录/注册后可看大图

                               
登录/注册后可看大图


                               
登录/注册后可看大图

回页首


jMock 框架介绍
                        总体上来说,jMock 是一个轻量级的模拟对象技术的实现。它具有以下特点:
                       
  • 可以用简单易行的方法定义模拟对象,无需破坏本来的代码结构表;
  • 可以定义对象之间的交互,从而增强测试的稳定性;
  • 可以集成到测试框架;
  • 易扩充;
                        与大多数 MOCK 框架一样,我们可以在 IDE 中使用并进行开发。本文以最常用的 Eclipse 为例。
                        下载 jMock
                        在 jMock 官方网站,我们可以下载当前稳定版本 jMock2.5.1 。
                        配置类路径
                        为了使用 jMock 2.5.1,您需要加入下面的 JAR 文件到当前的类路径。
                       
  • jmock-2.5.1.jar
  • hamcrest-core-1.1.jar
  • hamcrest-library-1.1.jar
                                                       
图 1. 已添加到 TestingExample 项目中 jMock 的 JAR 文件
                               

                               
登录/注册后可看大图
                       
                        使用 jMock 模拟接口
                        我们首先必须引入 jMock 的类,定义我们的测试类,创建一个 Mockery 的对象用来代表上下文。上下文可以模拟出对象和对象的输出,并且还可以检测应用是否合法。
                       
import org.jmock.Mockery;
import org.jmock.Expectations;

public class AJmockTestCase {
       
         Mockery context = new Mockery();

}

                        然后我们创建一个 calcService 去模拟 ICalculatorService 接口。在这里我们以 add() 方法为例,我们针对 add() 方法定义预期值 assumedResult 。之后我们去调用 add(1,1) 时,就可以得到预期值。
                       
// set up
         final ICalculatorService calcService = context.mock(ICalculatorService.class);
               
         final int assumedResult = 2;
               
        // expectations
        context.checking(new Expectations() {{
                 oneOf (calcService).add(1, 1); will(returnValue(assumedResult));
}});

                        清单 3 和 4 分别显示了 ICalculatorService 和 AJmockTestCase 的代码。
                       
清单 3. ICalculatorService 代码
                               
public interface ICalculatorService {

         public int add(int a, int b);

}

                       
清单 4. AJmockTestCase 代码
                               
import org.jmock.Mockery;
import org.jmock.Expectations;

public class AJmockTestCase {

         Mockery context = new Mockery();

         public void testCalcService() {

                 // set up
                 final ICalculatorService calcService = context
                                 .mock(ICalculatorService.class);

                 final int assumedResult = 2;

                 // expectations
                 context.checking(new Expectations() {
                         {
                                 oneOf(calcService).add(1, 1);
                                 will(returnValue(assumedResult));
                         }
                 });

                 System.out.println(calcService.add(1, 1));

         }

}

                        在 jMock 中,开发人员可以按照下面的语法定义预期值,从而实现更复杂的应用。例如我们可以模拟底层驱动程序的输出,在上层应用程序中使用这些模拟数据。具体可以参考 jMock 的官方网站。
                       
invocation-count (mock-object).method(argument-constraints);
    inSequence(sequence-name);
    when(state-machine.is(state-name));
    will(action);
then(state-machine.is(new-state-name));

                       

                               
登录/注册后可看大图


                               
登录/注册后可看大图

                               
登录/注册后可看大图


                               
登录/注册后可看大图

回页首


EasyMock 框架介绍
                        在实际开发中,不少开发人员也使用 EasyMock 来进行测试驱动开发。 EasyMock 具有以下的特点
                       
  • 在运行时 (runtime) 改变方法名或参数顺序,测试代码不会破坏;
  • 支持返回值和异常;
  • 对于一个或多个虚拟对象,支持检查方法调用次序;
  • 只支持 Java 5.0 及以上版本;
                        与大多数 MOCK 框架一样,我们可以在 IDE 中使用并进行开发。本文以最常用的 Eclipse 为例。
                        下载 EasyMock
                        在 EasyMock 官方网站,我们可以下载当前稳定版本 EasyMock2.4 。
                        配置类路径
                        为了使用 EasyMock 2.4,您需要加入下面的 JAR 文件到当前的类路径。
                       
  • easymock.jar
                                                       
图 2. 已添加到 TestEasyMock 项目中 EasyMock 的 JAR 文件
                               

                               
登录/注册后可看大图
                       
                        使用 EasyMock 模拟接口
                       
清单 5. ILEDCard 代码
                               
public interface ILEDCard {
         String getMessage();

         void setMessage(String message);
}

                       
清单 6. LED 代码
                               
public class LED {
         private ILEDCard ledCard;

         public LED(ILEDCard ledCard) {
                 this.ledCard = ledCard;
         }

         public String ShowMesage() {
                 return this.ledCard.getMessage();
         }

         public void setMessage(String message) {
                 this.ledCard.setMessage(message);
         }
}

                        我们首先创建一个 Mock 的对象 mockLEDCard 来代表 LED 卡的行为,并初始化 LED 对象。
                       
protected void setUp() throws Exception {
             super.setUp();
             mockLEDCard = createMock(ILEDCard.class);
             led = new LED(mockLEDCard);
}

                        之后我们对 ShowMessage 方法进行测试。
                       
public void testGetWord() {
        expect(mockLEDCard.getMessage()).andReturn("This is a EasyMock Test!");
        replay(mockLEDCard);

        led.ShowMesage();
        verify(mockLEDCard);
}

                        清单 7 显示了完整的代码。
                       
清单 7. AEasyMockTestCase 代码
                               
import static org.easymock.EasyMock.*;
import junit.framework.TestCase;

public class AEasyMockTestCase extends TestCase {

         private LED led;
         private ILEDCard mockLEDCard;

         protected void setUp() throws Exception {
                 super.setUp();
                 mockLEDCard = createMock(ILEDCard.class);
                 led = new LED(mockLEDCard);
         }

         protected void tearDown() throws Exception {
                 super.tearDown();
         }

         public void testGetWord() {
                 expect(mockLEDCard.getMessage()).andReturn("This is a EasyMock Test!");
                 replay(mockLEDCard);

                 led.ShowMesage();
                 verify(mockLEDCard);
         }

         public void testSetWord() {
                 mockLEDCard.setMessage("Another test");
                 replay(mockLEDCard);

                 led.setMessage("Another test");
                 verify(mockLEDCard);
         }
}

                        通过上文对 jMock 和 EasyMock 的介绍,我们可以发现 jMock 可以灵活的定义对象的行为。例如mock.expects(once()).method("method2").with( same(b1), ANYTHING).will(returnValue(method2Result)); 这点在 EasyMock 里比较难于实现。
                       

                               
登录/注册后可看大图


                               
登录/注册后可看大图

                               
登录/注册后可看大图


                               
登录/注册后可看大图

回页首


Rmock 及其它
                        目前比较流行的 mock 工具,还有 RMock, 目前的版本的是 2.0,当使用 jUnit开发测试用例时,它支持设置-修改-运行-验证这样的工作流。它加强了基于交互和基于状态的测试,同时有更好的测试工作流定义。 Rmock还可以使用 DynamicSuite 来解决维护 TestSuites 的问题。
                        市场上还有支持各种语言的 Mock object 的框架,如 pMock(Python),NMockLib(C#),Mocha(Ruby),JSMock(JavaScript),mockpp(C++) 。
                       

                               
登录/注册后可看大图


                               
登录/注册后可看大图

                               
登录/注册后可看大图


                               
登录/注册后可看大图

回页首


结语
                        在软件开发过程中,开发人员需要注重测试驱动开发,并利用模拟对象的技术来帮助进行测试。许多开发人员不习惯于频繁编写测试。即使需要编写测试,通常都是简单的进行主要功能测试。如果要测试代码的某些难以到达的部分,选择各种 Mock object 的框架可以降低开发测试的复杂度。
                        同时在硬件相关的程序开发中尤其是驱动开发,应用 Mock 技术将极大地提高软件开发的进度,并且减少代码中的缺陷,大大提高硬件软件兼容性和可靠性。
               

参考资料
学习

获得产品和技术
  •                                 若要获得 jMock 的副本,您可以从 jMock 下载某个版本。
  •                                 若要获得 EasyMock 的副本,您可以从 EasyMock 下载某个版本。


关于作者

                               
登录/注册后可看大图


                               
登录/注册后可看大图
方世明就职于 IBM 中国软件开发中心存储部门,从事存储设备管理软件的开发工作。
回复  

使用道具 举报

2#
发表于 17-12-2008 17:03:24 | 只看该作者
jMock 的 mock.expects(once()).method("method2").with( same(b1), ANYTHING).will(returnValue(method2Result)); 不算强

Mocha 可以做到
mock.expects(:method).at_least(3)
mock.expects(:method).at_least_once
mock.expects(:method).at_most(4)
mock.expects(:method).at_most_once
mock.expects(:method).never
mock.expects(:method).once
mock.expects(:method).times(2)
mock.expects(:method).times(2..4)

评分

参与人数 1威望 +30 收起 理由
coredump + 30 谢谢分享!

查看全部评分

回复  

使用道具 举报

3#
发表于 17-12-2008 17:05:34 | 只看该作者
我知道不应该这样,但是我就是对这些框架感觉比较不爽。

总觉得如果不是及其简单易学易用的东西,那么即使使用以后带来了一定的好处,那也没啥意思。就好比花10w刀以上买个跑车,虽然快,拉风,但是没有5w刀以下的实惠小跑车来的爽,也缺少成就感。

仅仅是极其主管的个人感受。
回复  

使用道具 举报

4#
发表于 17-12-2008 19:50:07 | 只看该作者

回复 #3 someonehappy 的帖子

我理解你的感受

大多数情况下,Mock 的使用是痛苦的

EDD 给我的感觉更像是让测试通过,而不是 break 要测试的软件

但是对于不易获得、接口清晰稳定且 Unit Test 必须用到的资源,Mock 还是值得的

对本身就是项目范围内的类的 Mock 要慎重
回复  

使用道具 举报

5#
发表于 17-12-2008 19:59:47 | 只看该作者
提示: 作者被禁止或删除, 无法发言
这些东西就是纯扯蛋。千万不要听这些家伙胡说八道,这些玩意只能用来吹牛,根本没p用,纯浪费时间。技术界的吹水风搞笑风也很严重,这几年有点用的也就个spring 和hibernate,在我看来struts那些都是垃圾。
回复  

使用道具 举报

6#
发表于 17-12-2008 20:21:22 | 只看该作者

回复 #5 black_zerg 的帖子

有道理

Spring 对于这些扯淡的东西的流行起到了推波助澜的作用

技术本身是无辜的,对技术的不适当地使用是有罪的
回复  

使用道具 举报

7#
发表于 18-12-2008 09:38:26 | 只看该作者
我也感覺怪怪的,這些東西貌似就是‘為了測試而測試’,‘為了騙錢而騙錢’搞出來的。。
回复  

使用道具 举报

8#
发表于 18-12-2008 17:40:37 | 只看该作者
楼上的几位的有些意见不敢苟同。  structs, spring, hibenate 都有自己存在的理由和适用的范围,

unit test. 我自己也使用过,感到对提高软件的质量是有很大帮助的。 至于mock,由于工作中没有用到,业余看了看,没什么深入研究。
回复  

使用道具 举报

9#
 楼主| 发表于 18-12-2008 18:26:34 | 只看该作者
我现在参加的项目如果把所有单元测试,回归测试的代码都去掉的话,估计谁也不敢把build出来的东西release出去。
回复  

使用道具 举报

10#
发表于 18-12-2008 21:05:09 | 只看该作者
反对意见是有道理的。

以前没有这么多关于软件质量保证的理论和产品,照样产生了非常伟大的软件产品。

现在配备了大量的QA,编写大量的测试,反倒只能发布漏洞百出的东西。

我想在这个转变的过程中,软件开发人员的目标发生了微妙的变化。

以前是要满足需求,现在更多的是考虑通过测试和不 break build。
回复  

使用道具 举报

11#
 楼主| 发表于 18-12-2008 21:10:28 | 只看该作者
原帖由 yuba 于 18-12-2008 20:05 发表
以前没有这么多关于软件质量保证的理论和产品,照样产生了非常伟大的软件产品。
.


以前那些伟大的软件作品也是充分测试过得,而且应该是各个千锤百炼,精雕细刻的,只不过在那些QA理论和产品出来前,为了测试和保证软件 质量付出的代价也是很惊人的,而这些理论和产品正是从这些早期的软件实践中总结提炼出来的阿。我觉得一点都不矛盾。
回复  

使用道具 举报

12#
发表于 18-12-2008 21:38:23 | 只看该作者
提示: 作者被禁止或删除, 无法发言
直接的说,你给每个方法写测试先,然后反复的测试。而我只测试复杂逻辑或没有界面能检测的逻辑。两者速度起码差一倍不止,而且写太多的junit, 第一会发现很多无聊和繁琐,第二思路完全被打断,完全没有行云流水的感觉。测试代码的质量也很重要,否则根本就是没什么用, 用hibernate spring, 干的最多的事情就是 load一下,save一下,就这破玩意有什么必要测试?这些东西就是吹牛用的,  起码在java应用最多的大多数数据库应用里没什么用。 可能也就是通讯和硬件相关的模块可以测测,在么就是写个通用类库什么的也许值得测测,在应用系统里大面积测那就是纯扯淡,完全就是想当然。 毫无疑问这个玩艺最后就变成个鸡肋,你折腾了半天,用了大于一倍的时间精力,mock了一堆东西,结果测试人员和UAT还是会给你一堆bug。而且客户根本不管那么多,要的就是在他的环境里能跑,数据库mock那么好弄的么?几家联合开发,还有地图数据库乱七八糟的,所有的初始数据那些乱七八糟的那么好模拟出来还能模拟的那么全面准确?mock的跑过了真环境就没问题了? 我要是客户我宁愿相信测试组也不信你这个也不知道质量怎么样的junit.  这东西完全就是看起来好的馊主意。不排除有些情况适用,但千万别迷信了。
最简单的说,你这多出来一倍的时间charge谁去就是个很实在的问题。
还有上面说的struts,那就是脱裤子放p的典范,还吹mvc,web搞p的Mvc,搞的屏幕狂刷新还得意的不得了,mvc在web里真要论的话,只有jsf有点样子(所以最失败),struts那就是不伦不类的居然也火了。web应用无非就是一个来一个去,一个dispather加保持客户状态的问题,看看这帮人激动的搞出来那么多理论自以为革命了。我就看不上那个,所以现在也就用spring mvc,我就搞不懂这些破玩意叫什么mvc,那个是m那个是v都说不清楚。其实浏览器才是view,所有传来传去的都是data,server就是个service,收个数据回个数据就这么简单的事,愣给搞那么复杂没几个配置文件几个类你都响应不了一个请求。

传说中的测试驱动我也是听过没见过,个人是极度不支持。人是生物不是机器,把东西弄的越复杂,你就越容易出错,之后维护和升级都更难。
就再说个实际例子,派你去给旧系统开发个模块你到了现场一刷junit跑出来几千个错,以前开发的早飞走了,仔细一看因为数据库早升级了那些测试本身质量也烂又年久失修。你说你怎么搞,这玩意实际意义又在那里。

[ 本帖最后由 black_zerg 于 18-12-2008 21:55 编辑 ]
回复  

使用道具 举报

13#
 楼主| 发表于 18-12-2008 22:05:03 | 只看该作者

回复 #12 black_zerg 的帖子

直观上都能看出来不用测试的干嘛还去测试阿

测试代码的作用是确保边界的稳定性,这个边界有可能是组件的接口,也有可能是存储数据的格式和内容,也有可能是通信协议。总之,不是最内部的那些getters,setters.

Mock应该用在模拟项目的3rd party组件上,这些东东直接连起来的话要么不可能,要么要劳师动众的,所以才要Mock,或者是用在接口定义非常清晰的模块之间。
回复  

使用道具 举报

14#
发表于 18-12-2008 22:16:35 | 只看该作者
提示: 作者被禁止或删除, 无法发言
有的人较真阿,愣提倡每个方法都测试还说没测试没质量,包括所有的controller,真没法说,当时候spring因为方便测试controller不是还很得意么。我手上就有流传下来的项目,无数的junit,也真是感人,不过写那么多重复的测试我看他们脑子都糊涂了,所以全是bug.最多见的测试就是controller里面写了返回 A ,然后 junit里面测试controller是不是返回这个A,再就是用hibernate去load一个实体,然后junit里检查这个实体load的对不对, 我看了都要感动哭了,结果现在过了两年,客户数据早变了,junit全跑不过。
所以很多概念跟着喊喊就好,千万别较真。

[ 本帖最后由 black_zerg 于 18-12-2008 22:26 编辑 ]
回复  

使用道具 举报

15#
 楼主| 发表于 18-12-2008 22:22:15 | 只看该作者

回复 #14 black_zerg 的帖子

晕,那不是较真,那是矫枉过正  

让我想起以前一个同事,说是完全掌握了C++和OOP编程,人家的程序是这样的:
class MyProgram
{
public:
           function1();
           function2();
};
int main()
{
MyProgram program;
program.function1();
program.function2();
}

太多的马谡和赵括类型的程序员和项目经理混进了软件开发的队伍
回复  

使用道具 举报

16#
发表于 19-12-2008 08:59:45 | 只看该作者
原帖由 black_zerg 于 18-12-2008 22:16 发表
最多见的测试就是controller里面写了返回 A ,然后 junit里面测试controller是不是返回这个A


哈哈

我最近的项目就是这样的

为什么呢

是为了让测试覆盖工具出来的报表漂亮吧

以前强调编码只占5%,结果其他的至少50%都没有做好,现在编码只好占50%了
回复  

使用道具 举报

17#
发表于 19-12-2008 09:29:54 | 只看该作者
原帖由 coredump 于 18-12-2008 21:10 发表
以前那些伟大的软件作品也是充分测试过得,而且应该是各个千锤百炼,精雕细刻的,只不过在那些QA理论和产品出来前,为了测试和保证软件 质量付出的代价也是很惊人的,而这些理论和产品正是从这些早期的软件实践中 ...


最大的差别是

写软件的从工匠变成纪律部队了
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 2-11-2024 18:24 , Processed in 0.058218 second(s), 33 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表