首先想骂一下《XUint 测试模式 -测试码重构》这本书的译者。多好一本书被不负责任的翻译给糟蹋了。
现在将最前面一部份试着翻译一下,希望能对写好UT有所帮助:
0.1 为什么要重构测试?
测试【这里指测试代码或用例】会迅速成为敏捷开发过程的瓶颈。对于从来没有体会过简单、易于理解的测试代码与复杂、迟钝、难以维护的测试代码之间区别的人来说,这可能不会马上显而易见。生产效率的差别会让人大吃一惊。
本书的这部份会作为全书的一个“激发式例子”,它将给你展示重构测试代码能够带来多大的改变。这个例子将会从一个复杂的测试用例开始,一步步地,将它重构为简单而易懂的测试用例。在这个过程中,我将指出一些关键的坏味(smells)以及用于去除它们的模式。希望能提起你更多的胃口。
0.2 一个复杂的测试用例
这儿有一个我在多个项目到测试用例中经常出现的一类。
public void testAddItemQuantity_severalQuantity_v1(){
Address billingAddress = null;
Address shippingAddress = null;
Customer customer = null;
Product product = null;
Invoice invoice = null;
try {
// Set up fixture
billingAddress = new Address("1222 1st St SW",
"Calgary", "Alberta", "T2N 2V2","Canada");
shippingAddress = new Address("1333 1st St SW",
"Calgary", "Alberta", "T2N 2V2", "Canada");
customer = new Customer(99, "John", "Doe",
new BigDecimal("30"),
billingAddress,
shippingAddress);
product = new Product(88, "SomeWidget",
new BigDecimal("19.99"));
invoice = new Invoice(customer);
// Exercise SUT
invoice.addItemQuantity(product, 5);
// Verify outcome
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("inv", invoice, actItem.getInv());
assertEquals("prod", product, actItem.getProd());
assertEquals("quant", 5, actItem.getQuantity());
assertEquals("discount", new BigDecimal("30"),
actItem.getPercentDiscount());
assertEquals("unit price",new BigDecimal("19.99"),
actItem.getUnitPrice());
assertEquals("extended", new BigDecimal("69.96"),
actItem.getExtendedPrice());
} else {
assertTrue("Invoice should have 1 item", false);
}
} finally {
// Teardown
deleteObject(invoice);
deleteObject(product);
deleteObject(customer);
deleteObject(billingAddress);
deleteObject(shippingAddress);
}
}
这个用例有点长,也比应有的情况更加地复杂。这个“晦涩测试”(page186)难以理解,因为测试用例中那么多行代码使得看清全貌很困难。它还有很多其他问题,我们会一一叙述。
0.3 清理测试用例
先让我们来看看测试用例的几个部份。
1) 清理验证逻辑
首先让关注验证预期结果的那部份。我们或许可以从断言去推断这个用例去验证的条件。
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("inv", invoice, actItem.getInv());
assertEquals("prod", product, actItem.getProd());
assertEquals("quant", 5, actItem.getQuantity());
assertEquals("discount", new BigDecimal("30"),
actItem.getPercentDiscount());
assertEquals("unit price",new BigDecimal("19.99"),
actItem.getUnitPrice());
assertEquals("extended", new BigDecimal("69.96"),
actItem.getExtendedPrice());
} else {
assertTrue("Invoice should have 1 item", false);
}
一个需要修复的简单问题就是最后一行那个愚钝的断言。调用带有false参数的assertTrue就是想使得测试断言失败,那么为什么不直接一点呢?将其改为对fail的调用:
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("inv", invoice, actItem.getInv());
assertEquals("prod", product, actItem.getProd());
assertEquals("quant", 5, actItem.getQuantity());
assertEquals("discount", new BigDecimal("30"),
actItem.getPercentDiscount());
assertEquals("unit price",new BigDecimal("19.99"),
actItem.getUnitPrice());
assertEquals("extended", new BigDecimal("69.96"),
actItem.getExtendedPrice());
} else {
fail("Invoice should have exactly one line item");
}
可以把这个改动看成是“方法提取”重构(Fowler),因为我们将一个带硬编码参数的“陈述预期式断言”用一个意图更明确的“单一预期断言”去封装来调用。
当然,这组断言还有更多的问题,比如,我们为什么需要这么多断言?结果是许多断言都在测试通过LineItem的构造函数设置的成员变量,而这些测试应该由其他的单元测试来覆盖。那么为什么要在这里重复这些断言呢?重复断言只会产生更多当逻辑发生变化时需要维护的代码。
我们的解决办法是使用针对“预期对象”的一个简单的断言来代替针对对象每一个字段的断言。首先我们定义一个与预期结果类似的对象。在这个例子中,我们创建一个预期的LineItem对象,它的每个字段都是预期的值,包括通过product初始化好的unitPrice和extenedPrice。
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.96"));
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("invoice", expected.getInv(),
actItem.getInv());
assertEquals("product", expected.getProd(),
actItem.getProd());
assertEquals("quantity",expected.getQuantity(),
actItem.getQuantity());
assertEquals("discount",
expected.getPercentDiscount(),
actItem.getPercentDiscount());
assertEquals("unit pr", new BigDecimal("19.99"),
actItem.getUnitPrice());
assertEquals("extend pr",new BigDecimal("69.96"),
actItem.getExtendedPrice());
} else {
fail("Invoice should have exactly one line item");
}
一旦我们创建了“预期结果对象”,我们就可以使用针对对象的assertEquals:
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem expected =
new LineItem(invoice, product,5,
new BigDecimal("30"),
new BigDecimal("69.96"));
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("invoice", expected, actItem);
} else {
fail("Invoice should have exactly one line item");
}
明显地,“保持对象完整”重构方法【Fowler】使得代码更加简洁。但是!为什么测试用例中会有if?如果一个用例中有多条路径分支,我们怎么知道它到底执行了哪个分支?最好能去掉这种“条件式测试逻辑”。幸运的是,“守卫式断言”这是用来处理此类情况。简单采用“卫语句代替条件表达式”重构方法【Fowler】,用针对相同条件的一个断言来代替if…else fail()语句序列。这种“守卫式断言”会在条件不满足时终止执行,而不用引入“条件式测试逻辑”。
List lineItems = invoice.getLineItems();
assertEquals("number of items", 1,lineItems.size());
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.96"));
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("invoice", expected, actItem);
{52}至此,我们将11行验证语句缩减到4行,另外,这4行代码也更为简单(and those 4lines are a lot simpler code to boot to boot in addition to everything else you have mentioned)。(注:没有根据我们所写的代码行数来领取报酬,真是件好事啊!这也是为何说KLOC是生产效率糟糕度量的一个例子)。不少人认识这些重构已经足够好了,但是,我们还可以让这些断言更加明了吗?我们真正想验证的到底是什么?我们想说的是,只有一个lineItem,它应该与我们定义的expectedLineItem完全一致。我们可以使用“抽取方法”重构定义一个“自定义断言”来明白地将这种验证给表现出来。
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.96"));
assertContainsExactlyOneLineItem(invoice, expected);
这样好多了!现在,测试用例的验证部份只有两行。当我们将用例完整地再看看:
public void testAddItemQuantity_severalQuantity_v6(){
Address billingAddress = null;
Address shippingAddress = null;
Customer customer = null;
Product product = null;
Invoice invoice = null;
try {
// Set up fixture
billingAddress = new Address("1222 1st St SW",
"Calgary", "Alberta", "T2N 2V2", "Canada");
shippingAddress = new Address("1333 1st St SW",
"Calgary", "Alberta", "T2N 2V2", "Canada");
customer = new Customer(99, "John", "Doe",
new BigDecimal("30"),
billingAddress,
shippingAddress);
product = new Product(88, "SomeWidget",
new BigDecimal("19.99"));
invoice = new Invoice(customer);
// Exercise SUT
invoice.addItemQuantity(product, 5);
// Verify outcome
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.96"));
assertContainsExactlyOneLineItem(invoice, expected);
} finally {
// Teardown
deleteObject(invoice);
deleteObject(product);
deleteObject(customer);
deleteObject(billingAddress);
deleteObject(shippingAddress);
}
}
2) 清理夹具卸载逻辑
我们已经清理了结果验证逻辑,现在将注意力转向测试用例末尾的finally语句块。这些代码是做什么的?
} finally {
// Teardown
deleteObject(invoice);
deleteObject(product);
deleteObject(customer);
deleteObject(billingAddress);
deleteObject(shippingAddress);
}
大多数现代语言都有类似的tyr/finally结构,这些结构用来保证即使有错误或者异常发生时,某些代码还是会被执行。在一个“测试方法”中,finally语句块用于保证不管测试用例通过或不通过,那些清理用的代码都会执行到。一个失败断言会抛出一个异常,它会将执行控制交回“测试自动框架”[Test Automatin Framework]的异常处理代码,所以,我们使用finally块先去执行清理操作。这种方式让我们不再需要先捕获异常,再将其重新抛出。
这个测试用例中,finally语句块对测试中创建的每一个对象调用了deleteObject方法。很不幸,代码有一个致命的缺陷,你注意到了吗?
问题出在卸载过程本身。如果第一次调用deleteObject时抛出一个异常,会发生什么?正如代码所示那样,其他deleteObject调用不会被执行。解决方法是使用一个嵌套的try/finally语句将第一个deleteObject给包起来,这样可以保证第二个deleteObject调用总会被执行。但是,如果第二个调用失败了呢?这个例子中,我们需要总共6层的try/finally嵌套行。那将会使得测试用例的长度翻倍,我们不可能在第一个测试用例中编写和维护这么多代码。
} finally {
// Teardown
try {
deleteObject(invoice);
} finally {
try {
deleteObject(product);
} finally {
try {
deleteObject(customer);
} finally {
try {
deleteObject(billingAddress);
} finally {
deleteObject(shippingAddress);
}
}
}
}
问题出在我们现在有了一个“复杂卸载”(见含糊测试Obscure Test),要如何使得这段代码变好?我们怎么去测试这些测试代码?很明显,现在的办法并不十分有效。
当然,我们可以将这些代码移到tearDown方法中去,这样就能将它们从“测试方法”中移出。另外,由于tearDown方法的作用类似finally语句块,我们就能摆脱最外层的try/finally。遗憾的是,这个策略并没有触及到问题的根本:需要在每个测试用例中编写细节化的卸载代码。
我们可能通过使用“共享夹具”的方法一开始就避免创建对象,这样就不用在每个用例执行切换过程中去将对象销毁。不过,此方法会带来不少测试坏味,包括“不可重复式测试(Unrepeatable Test)”(见不稳定测试Erratic Test)、由于夹具共享引起交互的“交互式测试(Interacting Test)”。另外,共享夹具中对象的引用常常导致“神秘访客(Mystery Guests)”(见含糊测试)。
最好的解决方案是使用“新鲜夹具”,同时避免为每个用例编写卸载代码。为达到这个目的,我们可以使用能被自动垃圾回收掉的内存中夹具。然而,当创建的对象是持久化时(如,他们被保存到数据库中),这个办法就失效了。(未完。。。。)
现在将最前面一部份试着翻译一下,希望能对写好UT有所帮助:
0.1 为什么要重构测试?
测试【这里指测试代码或用例】会迅速成为敏捷开发过程的瓶颈。对于从来没有体会过简单、易于理解的测试代码与复杂、迟钝、难以维护的测试代码之间区别的人来说,这可能不会马上显而易见。生产效率的差别会让人大吃一惊。
本书的这部份会作为全书的一个“激发式例子”,它将给你展示重构测试代码能够带来多大的改变。这个例子将会从一个复杂的测试用例开始,一步步地,将它重构为简单而易懂的测试用例。在这个过程中,我将指出一些关键的坏味(smells)以及用于去除它们的模式。希望能提起你更多的胃口。
0.2 一个复杂的测试用例
这儿有一个我在多个项目到测试用例中经常出现的一类。
public void testAddItemQuantity_severalQuantity_v1(){
Address billingAddress = null;
Address shippingAddress = null;
Customer customer = null;
Product product = null;
Invoice invoice = null;
try {
// Set up fixture
billingAddress = new Address("1222 1st St SW",
"Calgary", "Alberta", "T2N 2V2","Canada");
shippingAddress = new Address("1333 1st St SW",
"Calgary", "Alberta", "T2N 2V2", "Canada");
customer = new Customer(99, "John", "Doe",
new BigDecimal("30"),
billingAddress,
shippingAddress);
product = new Product(88, "SomeWidget",
new BigDecimal("19.99"));
invoice = new Invoice(customer);
// Exercise SUT
invoice.addItemQuantity(product, 5);
// Verify outcome
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("inv", invoice, actItem.getInv());
assertEquals("prod", product, actItem.getProd());
assertEquals("quant", 5, actItem.getQuantity());
assertEquals("discount", new BigDecimal("30"),
actItem.getPercentDiscount());
assertEquals("unit price",new BigDecimal("19.99"),
actItem.getUnitPrice());
assertEquals("extended", new BigDecimal("69.96"),
actItem.getExtendedPrice());
} else {
assertTrue("Invoice should have 1 item", false);
}
} finally {
// Teardown
deleteObject(invoice);
deleteObject(product);
deleteObject(customer);
deleteObject(billingAddress);
deleteObject(shippingAddress);
}
}
这个用例有点长,也比应有的情况更加地复杂。这个“晦涩测试”(page186)难以理解,因为测试用例中那么多行代码使得看清全貌很困难。它还有很多其他问题,我们会一一叙述。
0.3 清理测试用例
先让我们来看看测试用例的几个部份。
1) 清理验证逻辑
首先让关注验证预期结果的那部份。我们或许可以从断言去推断这个用例去验证的条件。
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("inv", invoice, actItem.getInv());
assertEquals("prod", product, actItem.getProd());
assertEquals("quant", 5, actItem.getQuantity());
assertEquals("discount", new BigDecimal("30"),
actItem.getPercentDiscount());
assertEquals("unit price",new BigDecimal("19.99"),
actItem.getUnitPrice());
assertEquals("extended", new BigDecimal("69.96"),
actItem.getExtendedPrice());
} else {
assertTrue("Invoice should have 1 item", false);
}
一个需要修复的简单问题就是最后一行那个愚钝的断言。调用带有false参数的assertTrue就是想使得测试断言失败,那么为什么不直接一点呢?将其改为对fail的调用:
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("inv", invoice, actItem.getInv());
assertEquals("prod", product, actItem.getProd());
assertEquals("quant", 5, actItem.getQuantity());
assertEquals("discount", new BigDecimal("30"),
actItem.getPercentDiscount());
assertEquals("unit price",new BigDecimal("19.99"),
actItem.getUnitPrice());
assertEquals("extended", new BigDecimal("69.96"),
actItem.getExtendedPrice());
} else {
fail("Invoice should have exactly one line item");
}
可以把这个改动看成是“方法提取”重构(Fowler),因为我们将一个带硬编码参数的“陈述预期式断言”用一个意图更明确的“单一预期断言”去封装来调用。
当然,这组断言还有更多的问题,比如,我们为什么需要这么多断言?结果是许多断言都在测试通过LineItem的构造函数设置的成员变量,而这些测试应该由其他的单元测试来覆盖。那么为什么要在这里重复这些断言呢?重复断言只会产生更多当逻辑发生变化时需要维护的代码。
我们的解决办法是使用针对“预期对象”的一个简单的断言来代替针对对象每一个字段的断言。首先我们定义一个与预期结果类似的对象。在这个例子中,我们创建一个预期的LineItem对象,它的每个字段都是预期的值,包括通过product初始化好的unitPrice和extenedPrice。
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.96"));
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("invoice", expected.getInv(),
actItem.getInv());
assertEquals("product", expected.getProd(),
actItem.getProd());
assertEquals("quantity",expected.getQuantity(),
actItem.getQuantity());
assertEquals("discount",
expected.getPercentDiscount(),
actItem.getPercentDiscount());
assertEquals("unit pr", new BigDecimal("19.99"),
actItem.getUnitPrice());
assertEquals("extend pr",new BigDecimal("69.96"),
actItem.getExtendedPrice());
} else {
fail("Invoice should have exactly one line item");
}
一旦我们创建了“预期结果对象”,我们就可以使用针对对象的assertEquals:
List lineItems = invoice.getLineItems();
if (lineItems.size() == 1) {
LineItem expected =
new LineItem(invoice, product,5,
new BigDecimal("30"),
new BigDecimal("69.96"));
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("invoice", expected, actItem);
} else {
fail("Invoice should have exactly one line item");
}
明显地,“保持对象完整”重构方法【Fowler】使得代码更加简洁。但是!为什么测试用例中会有if?如果一个用例中有多条路径分支,我们怎么知道它到底执行了哪个分支?最好能去掉这种“条件式测试逻辑”。幸运的是,“守卫式断言”这是用来处理此类情况。简单采用“卫语句代替条件表达式”重构方法【Fowler】,用针对相同条件的一个断言来代替if…else fail()语句序列。这种“守卫式断言”会在条件不满足时终止执行,而不用引入“条件式测试逻辑”。
List lineItems = invoice.getLineItems();
assertEquals("number of items", 1,lineItems.size());
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.96"));
LineItem actItem = (LineItem) lineItems.get(0);
assertEquals("invoice", expected, actItem);
{52}至此,我们将11行验证语句缩减到4行,另外,这4行代码也更为简单(and those 4lines are a lot simpler code to boot to boot in addition to everything else you have mentioned)。(注:没有根据我们所写的代码行数来领取报酬,真是件好事啊!这也是为何说KLOC是生产效率糟糕度量的一个例子)。不少人认识这些重构已经足够好了,但是,我们还可以让这些断言更加明了吗?我们真正想验证的到底是什么?我们想说的是,只有一个lineItem,它应该与我们定义的expectedLineItem完全一致。我们可以使用“抽取方法”重构定义一个“自定义断言”来明白地将这种验证给表现出来。
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.96"));
assertContainsExactlyOneLineItem(invoice, expected);
这样好多了!现在,测试用例的验证部份只有两行。当我们将用例完整地再看看:
public void testAddItemQuantity_severalQuantity_v6(){
Address billingAddress = null;
Address shippingAddress = null;
Customer customer = null;
Product product = null;
Invoice invoice = null;
try {
// Set up fixture
billingAddress = new Address("1222 1st St SW",
"Calgary", "Alberta", "T2N 2V2", "Canada");
shippingAddress = new Address("1333 1st St SW",
"Calgary", "Alberta", "T2N 2V2", "Canada");
customer = new Customer(99, "John", "Doe",
new BigDecimal("30"),
billingAddress,
shippingAddress);
product = new Product(88, "SomeWidget",
new BigDecimal("19.99"));
invoice = new Invoice(customer);
// Exercise SUT
invoice.addItemQuantity(product, 5);
// Verify outcome
LineItem expected =
new LineItem(invoice, product, 5,
new BigDecimal("30"),
new BigDecimal("69.96"));
assertContainsExactlyOneLineItem(invoice, expected);
} finally {
// Teardown
deleteObject(invoice);
deleteObject(product);
deleteObject(customer);
deleteObject(billingAddress);
deleteObject(shippingAddress);
}
}
2) 清理夹具卸载逻辑
我们已经清理了结果验证逻辑,现在将注意力转向测试用例末尾的finally语句块。这些代码是做什么的?
} finally {
// Teardown
deleteObject(invoice);
deleteObject(product);
deleteObject(customer);
deleteObject(billingAddress);
deleteObject(shippingAddress);
}
大多数现代语言都有类似的tyr/finally结构,这些结构用来保证即使有错误或者异常发生时,某些代码还是会被执行。在一个“测试方法”中,finally语句块用于保证不管测试用例通过或不通过,那些清理用的代码都会执行到。一个失败断言会抛出一个异常,它会将执行控制交回“测试自动框架”[Test Automatin Framework]的异常处理代码,所以,我们使用finally块先去执行清理操作。这种方式让我们不再需要先捕获异常,再将其重新抛出。
这个测试用例中,finally语句块对测试中创建的每一个对象调用了deleteObject方法。很不幸,代码有一个致命的缺陷,你注意到了吗?
问题出在卸载过程本身。如果第一次调用deleteObject时抛出一个异常,会发生什么?正如代码所示那样,其他deleteObject调用不会被执行。解决方法是使用一个嵌套的try/finally语句将第一个deleteObject给包起来,这样可以保证第二个deleteObject调用总会被执行。但是,如果第二个调用失败了呢?这个例子中,我们需要总共6层的try/finally嵌套行。那将会使得测试用例的长度翻倍,我们不可能在第一个测试用例中编写和维护这么多代码。
} finally {
// Teardown
try {
deleteObject(invoice);
} finally {
try {
deleteObject(product);
} finally {
try {
deleteObject(customer);
} finally {
try {
deleteObject(billingAddress);
} finally {
deleteObject(shippingAddress);
}
}
}
}
问题出在我们现在有了一个“复杂卸载”(见含糊测试Obscure Test),要如何使得这段代码变好?我们怎么去测试这些测试代码?很明显,现在的办法并不十分有效。
当然,我们可以将这些代码移到tearDown方法中去,这样就能将它们从“测试方法”中移出。另外,由于tearDown方法的作用类似finally语句块,我们就能摆脱最外层的try/finally。遗憾的是,这个策略并没有触及到问题的根本:需要在每个测试用例中编写细节化的卸载代码。
我们可能通过使用“共享夹具”的方法一开始就避免创建对象,这样就不用在每个用例执行切换过程中去将对象销毁。不过,此方法会带来不少测试坏味,包括“不可重复式测试(Unrepeatable Test)”(见不稳定测试Erratic Test)、由于夹具共享引起交互的“交互式测试(Interacting Test)”。另外,共享夹具中对象的引用常常导致“神秘访客(Mystery Guests)”(见含糊测试)。
最好的解决方案是使用“新鲜夹具”,同时避免为每个用例编写卸载代码。为达到这个目的,我们可以使用能被自动垃圾回收掉的内存中夹具。然而,当创建的对象是持久化时(如,他们被保存到数据库中),这个办法就失效了。(未完。。。。)
相关推荐
在本案例中,我们看到三个不同阶段的代码文件,分别对应重构过程的不同步骤:原始代码(01.Original.zip)、添加第一个测试用例(02.FirstTestAdded.zip)和添加四个测试用例(03.FourTestAdded.zip)。这为我们提供...
1. **编写测试**:为每个待测试的函数或方法创建一个对应的测试用例,通常一个测试用例对应一种预期的行为。 2. **设置环境**:在测试开始前,可能需要初始化数据或配置环境,如创建数据库连接、模拟依赖等。 3. **...
"测试金字塔"是一个结构化测试组织的原则,提倡底层更多的单元测试,中间层的集成测试,以及较少的端到端测试。这有助于保持测试速度并降低维护成本。 "测试优先级与分类"模式强调根据业务价值和风险来确定测试的...
1. **红**:首先,编写一个失败的单元测试,即一个测试用例,这个用例对应于要实现的功能。测试通常会因为缺少相应的功能代码而失败。 2. **绿**:然后,编写最小的代码量,使测试通过。这通常是一个简单的实现,只...
- **目的**:确保每个测试之间没有残留的状态,每个测试都处于一致的初始条件下运行。 通过以上详细的知识点解析可以看出,《XUNIT测试模式--测试码重构》这本书不仅介绍了xUnit框架的基础概念和使用方法,更重要的是...
单元测试作为软件开发过程中的一个重要组成部分,能够帮助开发者确保代码的质量,并在重构过程中保持系统的稳定性。本文档基于葛亮2013年7月4日的分享内容进行深入探讨,旨在通过解析单元测试的基本概念、目的及实践...
- **引入参数对象(Introduce Parameter Object)**:当一个方法接受多个参数时,可以创建一个新的类来封装这些参数,提高代码的可读性和可维护性。 #### 五、重构工具 随着技术的发展,现在有许多工具可以帮助...
《软件重构讲义》这份资料,为开发者提供了一个深入理解重构概念和技术的框架。它不仅仅是对理论的简单阐述,更是通过具体实例和技术手段,向开发者展示如何在实际开发过程中,运用重构技术来改善软件设计、提高代码...
综上所述,"react-旨在重构一个react事例"的项目涵盖了React组件化开发、Flux架构的应用以及代码重构的实践。通过对这个项目的学习,你可以深入理解React和状态管理,并了解如何通过重构提升代码质量和应用程序的...
UT(Unit Testing)工程是软件开发过程中的一个重要阶段,它涉及到对软件代码的最小可测试单元进行独立验证,以确保每个功能模块的正确性。这篇总结将深入探讨在Vector Cast环境下进行UT工程时可能遇到的问题以及...
2. 内联函数(Inline Method):如果一个函数只在一个地方被调用,可以将其内容直接替换到调用位置,减少层次,提高效率。 3. 将类的职责分离(Split Class):如果一个类承担了过多职责,应将其拆分为多个更专注的...
4. **Ant**:Ant是Apache软件基金会的一个项目,是一个基于Java的构建工具。它使用XML来定义构建过程,包括编译、打包、测试和部署等任务。与传统的Makefile相比,Ant更具有平台无关性,易于理解和使用,尤其在大型...
在这个案例中,我们聚焦于一个影片出租点的程序,通过逐步的重构过程,来演示如何优化原始代码。 重构是一种在不改变代码外在行为的前提下,改进其结构的过程。这个过程通常伴随着单元测试,确保每次修改都不会破坏...
重构的第一步是构建一个可靠的测试环境,这是安全重构的保障。测试环境可以确保每次代码修改之后,软件的行为和功能都能得到验证,从而保障重构的安全性。 重构的下一步是开始逐步修改程序。在这个案例中,开发者将...
重构是软件开发过程中的一个重要环节,它涉及到代码的优化、设计改进和质量提升,而不改变程序的外部行为。本文将详细探讨重构的目的、对象、时机和方法,帮助开发者理解和掌握重构的核心理念。 **重构的目的(Why...
在描述中提到的"对经验模态分解后的各分量IMF进行重构代码,函数可直接调用",意味着这个压缩包中包含了一个名为"EMDchonggou.m"的MATLAB脚本文件,该文件提供了实现IMF重构功能的代码。用户可以直接运行这个函数,...
代码重构是软件开发过程中的一个重要环节,它是指在不改变代码外在行为的前提下,对代码结构、设计和实现进行改进,以提高代码的可读性、可维护性和内部结构。重构的主要目标是使软件更容易理解和修改,而不仅仅是...