用 Groovy 减少代码冗余

 
Groovy 简洁的语法将开发人员从那种需要进行代码编译但却无助于表达 什么 是程序真正想要实现的典型的 Java™ 结构中解放了出来。在 实战 Groovy 系列的这一复兴篇中,Groovy 开发人员兼特约专栏作家 J. Scott Hickey 带您进行一系列对常规 Java 代码和 Groovy 代码的比较,展示这门令人兴奋的语言如何将您解放出来,让您能够专注于编码的重要方面。

通常,程序员们转而选择诸如 Groovy 之类的编程语言,是为了构建快速的实用程序,快速编写测试代码,甚至创建构成大型的 Java 应用程序的组件,而 Groovy 先天具有这样一种能力,它能够减少传统的基于 Java 系统所固有的许多冗余并降低其复杂度。Groovy 简洁而灵活的语法将开发人员从那种需要进行代码编译却无助于表达什么 是程序真正想要实现的典型的 Java 结构中解放出来。不仅如此,Groovy 轻松的类型通过减少一些接口和超类使代码不再复杂,这些接口和超类都是常规 Java 应用程序用以支持不同具体类型间的通用行为所需的。

为了举例说明 Groovy 如何减少 Java 应用程序所涉及的无用数据,我将使用 Bruce Tate 和 Justin Ghetland 的 Spring: A Developer's Notebook(参见 参考资料) 中的样例代码,该书介绍了如何使用 Spring 进行控制反转。每当回顾一个 Java 样例,我都会将其与实现相同功能的相应的 Groovy 源代码进行比较,您将很快发现 Groovy 通过减少 Java 编程的不同方面(冗余且不必要地传递了应用程序的行为)而使应用程序代码变得多么地清晰。

Groovy 之声

在 Bruce 和 Justin 这本书的第一章中,创建了一个简单的自行车商店应用程序,其中包含有四个类。首先,我将向您展示一个简单的名为 Bike 的 JavaBean 类,该类代表了一辆库存的自行车。然后,我会考查自行车商店的类型,名为 RentABike。它包含了一个 Bike 集。还有一个命名为 CommandLineView 的用于显示自行车列表的类,该类依赖于 RentABike 类型。最后,有一个用于集成这些部分以创建工作应用程序的类,该类利用 Spring 来传递完整地配置了 RentABike 类型的 CommandLineView 类 —— 免去了复杂的硬编码。

停用 JavaBean!

清单 1 中一个代表自行车的类在常规 Java 代码中被实现为一个简单的 JavaBean,它是 Java 开发人员可能已经编写好的成百上千的类的一个典型。通常来说,JavaBean 并没有什么特殊之处 —— 其属性被声明为 private,且可通过 public getter 和 setter 对其进行访问。


清单 1. Java 代码中的 Bike JavaBean
    
import java.math.BigDecimal;

public class Bike {
private String manufacturer;
private String model;
private int frame;
private String serialNo;
private double weight;
private String status;
private BigDecimal cost;

public Bike(String manufacturer,String model,int frame,
String serialNo,double weight,String status) {
this.manufacturer = manufacturer;
this.model = model;
this.frame = frame;
this.serialNo = serialNo;
this.weight = weight;
this.status = status;
}

public String toString() {
return "com.springbook.Bike : " +
"manufacturer -- " + manufacturer +
"/n: model -- " + model +
"/n: frame -- " + frame +
"/n: serialNo -- " + serialNo +
"/n: weight -- " + weight +
"/n: status -- " + status +
"./n"; }

public String getManufacturer() { return manufacturer; }

public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}

public String getModel() { return model; }

public void setModel(String model) { this.model = model; }

public int getFrame() { return frame; }

public void setFrame(int frame) { this.frame = frame; }

public String getSerialNo() { return serialNo; }

public void setSerialNo(String serialNo) { this.serialNo = serialNo; }

public double getWeight() { return weight; }

public void setWeight(double weight) { this.weight = weight; }

public String getStatus() { return status; }

public void setStatus(String status) { this.status = status; }

public BigDecimal getCost() { return cost; }

public void setCost(BigDecimal cost) {
this.cost = cost.setScale(3,BigDecimal.ROUND_HALF_UP);
}

}

清单 1 是一个只有一个构造方法和六个属性的小例子,但其代码却填满了浏览器的整个页面!清单 2 显示了在 Groovy 中定义的相同的 JavaBean:


清单 2. Bike GroovyBean
  
class Bike {

String manufacturer
String model
Integer frame
String serialNo
Double weight
String status
BigDecimal cost

public void setCost(BigDecimal newCost) {
cost = newCost.setScale(3,BigDecimal.ROUND_HALF_UP)
}


public String toString() {
return """Bike:
manufacturer -- ${manufacturer}
model -- ${model}
frame -- ${frame}
serialNo -- ${serialNo}
"""
}
}

您认为哪一个没那么多冗余呢?

Groovy 版的代码要少很多很多,这是因为 Groovy 的默认属性语义用 public 访问器和存取器自动定义了 private 域。例如,上述 model 属性现在有了自动定义的 getModel() 方法和 setModel() 方法。可以看到,这项技术的好处是,不必在一种类型中按照属性手工定义两个方法!这也解释了 Groovy 中的一条反复强调的定律:使普通的编码规则变得简单

Groovy 更新了的属性语法
最新版的 Groovy,JSR-06 简化了 Groovy 的属性语法。在此版本之前,属性要在变量声明前跟一个特定的 @Property 符号。但这个符号现在已经不再需要。在 JSR-06 中,只需指定一个类型和一个变量名,就会自动生成一个有着 public getter 和 setter 的 private 属性。也可以使用 def 关键字来代替特定的类型。

另外,在 Groovy 中,类的默认函数表达得更为简洁,而在常规 Java 代码(如 清单 1)中该函数必须显式编码。当需要用构造函数或 getter 或 setter 来完成一些特殊任务时,Groovy 真的很出色,因为只需瞥一眼代码,其精彩的行为就会立即变得十分明显。例如,在 清单 2 中很容易看出,setCost() 方法会将 cost 属性换算为三个十进制的位。

将这段不太显眼的 Groovy 代码同 清单 1 中的 Java 源代码进行比较。第一次阅读这段代码时,您注意到 setCost() 方法中嵌入了特殊的函数了吗?除非仔细观察,否则太容易看漏了!

Groovy 测试

清单 3 中 Bike 类的测试用例展示了如何使用自动生成的访问器。同时,出于进一步简化通用编程任务的考虑,测试用例也使用了更为简便的 Groovy 点属性名 标记来访问属性。相应地,能够通过 getModel() 方法或更为简洁的 b.model 形式来引用 model 属性。


清单 3. Groovy 中的 Bike 测试用例
  
class BikeTest extends GroovyTestCase {
void testBike() {
// Groovy way to initialize a new object
def b = new Bike(manufacturer:"Shimano",model:"Roadmaster")

// explicitly call the default accessors
assert b.getManufacturer() == "Shimano"
assert b.getModel() == "Roadmaster"

// Groovier way to invoke accessors
assert b.model == "Roadmaster"
assert b.manufacturer == "Shimano"
}
}

也注意到在上述 Groovy 例子中,不必定义一个如 清单 1 中定义的 Java 构造函数那样的能够接受全部六个属性的构造函数。同时,也不必要创建另一个 只含两个参数的构造函数来支持测试用例 —— 在对象创建过程中设置对象属性的 Groovy 的语义不需要这种冗余、烦人的构造函数(它们综合有多个参数但其作用却只是初始化变量)。

Groovy 和 IDE
像 Eclipse 那样的 IDE 使得自动创建 getter 和 setter 变得十分简单。这些工具也便利了从具体类中提取接口以利于对其进行重构。但我敢说,读那段代码的次数要比写它的次数多很多。 不幸的是,开发人员仍需要通过已经生成的代码和源文件来费力地分辨程序真正在实现什么。用这个高质量的 Java 工具究竟能不能节省创建 Groovy 源代码的敲键次数尚有争议,但一看即明的是 Groovy 代码更为简洁、更易于解释,且没那么复杂。在实际的有着数百个类和数千行代码的应用程序,这种优势就更为突出。

降低的复杂性

在前面的部分中,Bike GroovyBean 利用了 Groovy 的属性和构造语义以减少源代码中的冗余。在这一部分中,Groovy 版的自行车商店也将受益于额外的冗余减少特性,如针对多态的 duck-typing、集合类的改进及操作符重载。

Grooving 中使用多态

在 Java 自行车应用程序中,名为 RentABike 的接口是用来定义由自行车商店支持的 public 方法的。正如在清单 4 中说明的那样,RentABike 定义了一些简单的方法,这些方法用来返回商店中单个的 Bike 或所有 Bike 的列表。


清单 4. 由 Java 定义的 RentABike 接口
  
import java.util.List;

public interface RentABike {
List getBikes();
Bike getBike(String serialNo);
void setStoreName(String name);
String getStoreName();
}

此接口允许多态行为并在下面两种重要情况下提供了灵活性。其一,如果要决定将实现由 ArrayList 转变为数据库形式,余下的应用程序与该变化是隔绝的。其二,使用接口为单元测试提供灵活性。例如,如果要决定为应用程序使用数据库,可以轻易地创建一个该类型的模拟实现,而且它不依赖于实时数据库。

清单 5 是 RentABike 接口的 Java 实现,它使用 ArrayList 来存储多个 Bike 类:


清单 5. RentABike 接口的 Java 实现
    
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListRentABike implements RentABike {
private String storeName;
final List bikes = new ArrayList();

public void setStoreName(String name) {
this.storeName = name;
}

public String getStoreName() {
return storeName;
}

public ArrayListRentABike(String storeName) {
this.storeName = storeName;
bikes.add(new Bike("Shimano","Roadmaster",20,"11111",15,"Fair"));
bikes.add(new Bike("Cannondale","F2000 XTR",18,"22222",12,"Excellent"));
bikes.add(new Bike("Trek","6000",19,"33333",12.4,"Fair"));
}

public String toString() { return "com.springbook.RentABike: " + storeName; }

public List getBikes() { return bikes; }

public Bike getBike(String serialNo) {
Iterator iter = bikes.iterator();
while(iter.hasNext()) {
Bike bike = (Bike)iter.next();
if(serialNo.equals(bike.getSerialNo())) return bike;

}
return null;
}
}

现在将 清单 45 中的 Java 代码同 清单 6 中的 Groovy 代码进行比较。Groovy 版的代码很灵巧地避免了对 RentABike 接口的需求。


清单 6. Groovy 的 ArrayListRentABike 实现
  
public class ArrayListRentABike {
String storeName
List bikes = []
public ArrayListRentABike(){
// add new instances of Bike using Groovy's initializer syntax
bikes << new Bike(manufacturer:"Shimano",model:"Roadmaster",
frame: 20,serialNo:"11111",weight:15,status:"Fair")
bikes << new Bike(manufacturer:"Cannondale",model:"F2000",
frame: 18,serialNo:"22222",weight:12,status:"Excellent")
bikes << new Bike(manufacturer:"Trek",model:"6000",
frame: 19,serialNo:"33333",weight:12.4,status:"Fair")
}

// Groovy returns the last value if no return statement is specified
public String toString() { "Store Name:=" + storeName }

// Find a bike by the serial number
def getBike(serialNo) { bikes.find{it.serialNo == serialNo} }

}

Groovy 像其他动态语言(如 Smalltalk 或 Ruby)一样支持具有 “duck typing” 的多态 —— 在运行时,如果一个对象表现得像个 duck ,它就会被视为 duck ,从而支持 接口的多态。有了 Groovy ArrayListRentABike 实现,不但减少了成行的代码,而且由于少创建和维护一个模块,复杂性也降低了。那是非常重要的冗余减少!

除了 duck typing,清单 6 中的默认属性语法还简单地定义了两个普通属性,storeNamebikes,如同拥有了 getter 和 setter 一样。这样做的好处和在 清单 12 中比较 JavaBean-GroovyBean 时所说明的好处是一样的。尤其是,清单 6 还阐明了另一个用以减少代码冗余的 Groovy 特性 —— 操作符重载。请注意如何使用 << 操作符来代替 add() 方法。通过减少一层嵌套的括号使代码的可读性得以改善。这也是 Groovy 众多通过减少冗余而改善代码可读性的特性中的一种。

Groovy 方式下的和谐集合
用诸如 eachfind 的方法来使用闭包简化了最常见的任务集,如循环和查找。将 清单 5 中 Java 版本的 getBike()清单 6 中 Groovy 版的进行比较。在 Groovy 中,很明显是通过其序列号来寻找 Bike。而在 Java 版中,定义了一个 Iterator 并计算列表中下一个条目,这很多余,且不利于理解该应用程序真正要实现的功能,即寻找一辆自行车。

透明的代码

Groovy 中的 duck-typing 和属性语义通过减少代码行数来减少冗余;然而,也可以通过增加透明度来减少冗余。在 清单 6 中,请注意在 ArrayListRentABike 构造函数中创建新 Bike 对象的方式。Groovy 名称和值的初始化语法比 Java 版的略微详细,但这些额外的代码却使整个代码更为透明 —— 将这一点与 清单 5 中 Java 版的进行比较,哪个属性被初始化为哪个值会立即明显 起来。不回过头来看 Bike JavaBean 源代码,您能记起哪个参数是 frame,哪个是 new Bike("Shimano"、 "Roadmaster"、20、 "11111"、15、 "Fair")weight 吗?尽管我刚写过,但我还是记不起来!








一个更小的、更加 Groovy 化的自行车商店视图

到目前为止,我将 Bike 和自行车商店类型在 Java 和 Groovy 下进行了比较。现在,到了更近距离地看一下自行车商店的视图 的时候了。在清单 7 中,该视图类具有一个 rentaBike 属性,该属性引用 RentABike 接口并在行动上说明 Java 版的多态。由于 Java 要求所有类属性都必须是声明过的类型,而不是针对某个特定的实现进行编码,我向一个接口编程,该接口使这个类跟 RentABike 实现的改变分隔开来。这是很好的、扎实的 Java 编程实践。


清单 7. Java 版的自行车商店视图
  
public class CommandLineView {
private RentABike rentaBike;

public CommandLineView() {}

public void setRentaBike(RentABike rentaBike) {
this.rentaBike = rentaBike;
}

public RentABike getRentaBike() { return this.rentaBike; }

public void printAllBikes() {
System.out.println(rentaBike.toString());
Iterator iter = rentaBike.getBikes().iterator();
while(iter.hasNext()) {
Bike bike = (Bike)iter.next();
System.out.println(bike.toString());
}
}
}

将清单 7 中的 Java 视图与清单 8 中的 Groovy 视图进行比较,请注意我声明了带 def 关键字的 rentaBike。这是 duck-typing 的实践,与 Java 版的很像。我正在实践好的软件设计,这是因为我还没有将视图和特定的实现耦合起来。但我也能够 定义接口就实现解耦。


清单 8. Groovy 的 CommandLineView
  
public class CommandLineView {
def rentaBike // no interface or concrete type required,duck typing in action

def printAllBikes() {
println rentaBike
rentaBike.bikes.each{ println it} // no iterators or casting
}
}

Bike 和自行车商店类型一样,Groovy 的 CommandLineView 没有了为 RentABike 属性所显式编写 的 getter 或 setter 的冗余。同样,在 printAllBikes() 方法中,通过使用 each 来打印在集合里找到的每辆自行车,我再一次利用了 Groovy 强大的集合功能的改进。







使用 Spring 进行组装

在前面的部分中,已经介绍了 Groovy 相比 Java 是如何定义自行车、自行车商店和自行车商店视图的。现在该介绍如何将整个应用程序组装起来并在命令行视图中使用 Spring 来显示库存自行车列表了。

工厂的工厂

在 Java 编程中,一旦定义了一个接口,就可以使用工厂模式将创建真实的实现类的责任委派给一个对象工厂。使用 Spring 作为一个工厂极大地减少了冗余,并在 Groovy 和 Java 中都能够使用,在最终的代码样例中,Spring 负责在 Java 和 Groovy 中创建一个 CommandLineView 类型的实例。

在清单 9 中,配置 Spring 是为了在返回一个 CommandLineView 实例前,创建并将自行车商店的 ArrayList 实现注入 CommandLineView 中。这意味着,不需要引用在 清单 78 的 Java 或是 Groovy 版的命令行视图中的 ArrayList 实现。在 Java 版中,被注入的类通常都会引用一个接口而不是实现。在 Groovy 中,由于使用 def 关键字,而允许利用 duck-typing。无论在哪个实例中,配置 Spring 的目的都是为了将自行车商店视图的实例和自行车商店类型的实例完整地配置起来


清单 9. Spring 配置文件
        
<beans>
<bean id="rentaBike" class="ArrayListRentABike">
<property name="storeName"><value>"Bruce's Bikes (spring bean)"</value></property>
</bean>
<bean id="commandLineView" class="CommandLineView">
<property name="rentaBike"><ref bean="rentaBike"/></property>
</bean>
</beans>

在清单 10 和 11 中,自行车商店组装类型用清单 9 中的配置文件创建了一个 Spring 的 ClassPathXmlApplicationContext 实例,然后,请求自行车商店视图的实例:


清单 10. Java 版本下调用 Spring 创建自行车商店视图
  
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class RentABikeAssembler {
public static final void main(String[] args) {
// Create a Spring application context object
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("RentABike-context.xml");

// retrieve an object from Spring and cast to a specific type
CommandLineView clv = (CommandLineView)ctx.getBean("commandLineView");

clv.printAllBikes();
}
}

请注意Java 版的清单 10 中,用以请求一个命令行视图实例的对 Spring 的调用要求向一个支持 printAllBikes() 方法的对象类型强制转换。在本例中,由 Spring 导出的对象将被强制转换为 CommandLineView

有了 Groovy 及其对 duck-typing 的支持,将不再需要强制转换。只需确保由 Spring 返回的类能够对合适的方法调用(printAllBikes())作出响应。


清单 11. Groovy 版的 Spring 组合件
  
import org.springframework.context.support.ClassPathXmlApplicationContext

class RentABikeAssembler {
public static final void main(String[] args) {
// Create a Spring application context object
def ctx = new ClassPathXmlApplicationContext("RentABike-context.xml")

//Ask Spring for an instance of CommandLineView,with a
//Bike store implementation set by Spring
def clv = ctx.getBean("commandLineView")

//duck typing again
clv.printAllBikes()
}
}

正如在清单 11 中看到的那样,在 Groovy 中,duck-typing 对减少冗余的贡献不仅体现在无需声明接口即可支持由 Spring 自动配置对象,其贡献还体现在简化了对完全配置的 bean 的使用(一旦它从 Spring 容器中返回)。







与 Groovy 相协调

至此,希望我已经阐明了 Groovy 的强大功能及其如何能如此深远地改变源代码的性质。与上述 Java 样例相比,Groovy 代码更简短也更易理解。任何人,无论是经验丰富的 Java 架构师还是非 Java 程序员,都能轻易地掌握 Groovy 代码的意图。Groovy 及其对动态类型的支持减少了要管理的文件。总之,使用 Groovy 减少了在典型的 Java 程序中所常见的大量冗余。这实在是福音啊!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


背景:    8月29日,凌晨4点左右,某服务告警,其中一个节点直接down掉,收到告警的同事让运维重启。    9点左右,内存监控上发现内存异常,堆内存涨速很快,即便GC也没有什么效果,频繁GC。    9点38,服务各种超时,影响整个app使用。处理方式:    当时由于很想要
https://support.smartbear.comeadyapi/docs/soapui/steps/groovy.htmlGettestcaseobjectToobtaintheobjectwhichreferstothecontainingtestcase,usethefollowingcodesnippet:Groovy def case=testRunner.testCase; Byusingthe tes
有几个选项可用于执行自定义JMeter脚本并扩展基线JMeter功能。查看最流行的扩展机制,比较性能并分析哪一个是最好的。  这是乐队之战,JMeter风格。 BeanshellV.JSR223V.JavaRequestSampler 在我们之前的帖子中,  JMeterPerformance和TuningTips  ( 由fantastik
Scala和Java为静态语言,Groovy为动态语言Scala:函数式编程,同时支持面向对象Groovy:jvm上的脚本,较好兼容java语法,Groovy加强了Java集成。 可配置化的优势,可以将一些简单的逻辑公开给外部编辑和使用,增强了互操作性,复杂逻辑来说,可配置化代码的调试则会比较麻烦 Scala和Java
出处:https://www.jianshu.com/p/ce6f8a1f66f4一、一些内部元件的访问testRunner.testCase开头1、向下访问testRunner.testCase.testSteps[testStepName]testRunner.testCase.getTestStepByName("新增一个空间")2、向上访问,用于访问同一项目中的其他testSuites和testCase下
在运行groovy的junit方法时,报了这个错误:java.lang.ExceptionInInitializerError atorg.codehaus.groovy.reflection.ClassInfo.isValidWeakMetaClass(ClassInfo.java:271) atorg.codehaus.groovy.reflection.ClassInfo.getMetaClassForClass(ClassInfo.java:241) atorg.codeha
基本语法1.Grovvy的注释分为//和/**/和java的一样.2.Grovvy语法可以不已分号结尾.3.单引号,里面的内容严格的对应java中的String,不对$符号进行转义.defs1='iamastudent$'printlns1iamastudent$4.双引号“”的内容中如果有$号的话,会先对表达式先求值.de
Tiobe发布了最新一期(3月份)编程语言欢迎度榜单,其榜单根据互联网上有经验的程序员、课程和第三方厂商的数量,并使用搜索引擎(如Google、Bing、Yahoo!)以及Wikipedia、Amazon、YouTube统计出排名数据。TOP5几乎没有变化,Java和C语言牢牢占据前两名。Python相较去年上升一位进入TOP3,C++下
我有一个Google地图组件,作者可以在其中指定纬度和经度.我正在使用带有正则表达式的常规“输入”类型控件来验证它们是否是数字,但是,当试图解决指定范围的问题时(经度验证该值在[-180,180]内并且纬度[-90,90])但是,通过正则表达式进行验证似乎很麻烦,而且利用inputtype=“numb
我正在为未来的应用程序评估SpringBoot,并希望使用Groovy模板来实现其纯粹的可读性.不幸的是,我在迭代我添加到控制器返回的ModelAndView对象的对象列表时遇到了麻烦.这是我的控制器:@RestController@RequestMapping("/ships")publicclassShipsController{@Autowired
我有一个基于Spring的java应用程序,其中包含一些有用的组件.作为系统的一部分,我有一个groovy脚本,来处理一些报告.我想从groovy脚本中调用spring组件.当我用Java编写时,我需要在@Component中使用@Autowired注释,即@ComponentclassReporter{@AutowiredSearchServicesearchS
在Grailsi18n插件definedthusly中定义了一个messageSourcebean:messageSource(PluginAwareResourceBundleMessageSource){basenames=baseNames.toArray()fallbackToSystemLocale=falsepluginManager=manager....}是否可以覆盖我的resources.groovy中的fa
我正在寻找一种方法来反向工程RDBMS表(MSSQLServer)并生成JPA@EntityGroovy类.我们目前没有选择使用Grails和/或GORM,因此Grailsdb-reverse-engineer插件似乎很接近但不太正确.它生成符合GORM的类而不是JPA实体类.我们目前有一个gradle构建,它利用org.hibernate.tool.ant.Hibe
https://blog.csdn.net/Gdeer/article/details/83062523一、直接运行groovy程序因为groovy插件和android插件不兼容,所以不能在原始项目上使用groovy。 新建module,创一个JavaLibrary,取名lib。  修改lib/build.gradleapplyplugin:'java-library'depe
一、自动生成GET请求脚本1、配置Createascript在ngrinder管理台主页,点击script–>Createascript,并填写脚本名称和请求的url,如下所示:点击Create按钮,nGrinder会自动生成对应的脚本结构,如果没有参数需要设置的话,可以直接运行了。二、详细解析GET请求脚本ngrinder自动生成的脚本
我正在关注使用列表和地图作为构造函数的this博文.为什么以下列表无法强制反对?classTest{staticclassTestObject{privateinta=1;protectedintb=2;publicintc=3;intd=4;Strings="s";}stati
Information:java:Errorsoccurredwhilecompilingmodule'security'Information:javac1.8.0_131wasusedtocompilejavasourcesInformation:2019/6/98:31-Buildcompletedwith1errorand0warningsin3s116msError:java:读取E:\repository\org
ngrinder中的groovy脚本结构类似junit,同时在junit的基础之上封装了自己的注解,用来控制脚本的运行。一、运行逻辑图如下:此处只列出了groovy脚本的逻辑,jython脚本是类似的,在此不再单独介绍。二、各注解的使用比较三、关注点在ngrinder中,通常使用单进程多线程就足够大部分测试了,所以:
我有一个switch语句来处理javaenumfoo,并使用spock编写一些groovy单元测试.我已经添加了一个测试,它验证当前是否处理了每种类型的foo而没有抛出异常.现在我想测试一个无法识别的foo类型会导致抛出异常.要做到这一点,我将不得不嘲笑枚举,并已经看到这里概述的解决方案:MockingJ
我有一个groovy实体ClientInvoiceAttachmentExt,它扩展了java实体ClientInvoiceAttachment.ClientInvoiceAttachment具有@Id注释,但仍然看到“没有为实体指定的标识符”错误这是我的堆栈跟踪[Mar0317:11:54]ERROR|org.springframework.web.context.ContextLoader|Contex