单元测试作为提升代码质量的有效方法,目前在国内各大互联网公司的开发团队中,尤其是业务团队中却鲜少被使用。这主要由于大家对于单元测试有一些认知错误,或者没有正确的打开方式。至今我们团队在小剧场、零代码运营平台等项目都进行了一些单元测试的实践运用,单元测试对我们的代码质量及开发效率都大有裨益,故希望通过此篇分享我们在单元测试的一些实践经验,帮助大家更好的使用单元测试来为自己的系统质量保驾护航。

方法篇

为什么需要单元测试

单元测试的定义

一个单元测试是一段自动化的代码,这段代码调用被测试的工作单元,之后对这个单元的单个最终结果的某些假设进行检验。单元测试几乎都是用单元测试框架编写的。单元测试容易编写,能快速运行。单元测试可靠、可读,并且可维护。只要产品代码不发生变化,单元测试的结果是稳定的。

基于上方的单元测试定义,我进一步总结了一些单元测试的基本特征,只有满足以下所有特征才是合格的单元测试。

自动化检测

单元测试应该是无需开发手动进行验证的,运行单元测试应该自动进行结果的校验并输出不通过的单元测试用例。反例是通过结果的输出手动比对。

快速运行

单元测试应该是很快被执行的,一个项目所有的测试用例应该在10s内全部被执行验证,快速的单元测试才能在开发过程中反复检测。反例是单元测试依赖Spring IOC容器启动。

无依赖

单元测试不应该依赖任何外部资源,包括文件系统、数据库、RPC,所有对外资源的调用都应该采用MOCK的方式。

结果稳定

单元测试运行检测不应该依赖运行时刻的部署环境、时间、地理位置等外部信息。反例是一个错误的单元测试依赖了当前时间,当在未来运行时会因为时间的变动而导致结果不一致。

粒度小

单元测试检测的功能范围应该是小粒度的,这里会在后文做更详细的说明。

持续维护

单元测试非一次性的代码,他也需要随同业务代码的发展持续进行维护。不易维护、不可阅读的单元测试是无意义的,最终会成为系统代码中的累赘,最后也无人关心这些测试代码是否通过了。

单元测试与其他测试的区别

大家对单元测试的最常见误区之一就是将单元测试与集成测试概念混淆。单元测试与集成测试最大的区别在于单元测试不依赖任何外部资源,并且测试的范围只限定于单个功能单元,而集成测试会依赖外部资源,并且核心是将某一业务流程全部走通。系统测试是必须系统完整运行,并通过Http接口等进行链路验证,相较于集成测试它的编写及运行成本更高,检测范围更广。

测试类型 编写及运行成本 用例数量
系统测试 (End to End Test)
集成测试 (Integration Test)
单元测试 (Unit Test) 低成本,高效运行

单元测试的作用

质量及信心的提升

单元测试最核心的作用是对代码质量的提升,通过单元测试我们可以对系统内各个功能单元都建立完备的自动化测试。相较于测试同学黑盒模式的测试验证,单元测试有其天然的优势,可以在逻辑代码内直接进行检测验证,穷举所有必要的用例。将生产代码进行有效的单元测试覆盖,也会极大的提升开发对代码质量的信心。

活文档

维护良好,用例清晰的单元测试,天然就是一个可调用的活文档。一个功能逻辑内各自用例只需要通过运行单元测试即可一目了然。

良好解耦的尺度

对一些老的代码我们在补充单元测试时发现难以进行单元测试的编写,这是由于此处逻辑设计不够耦合,导致难以拆解独立测试。

测试友好的代码必然是解耦的,所以我们可以将单元测试作为方法耦合的尺度,若无法简单的进行单元测试,功能方法必然存在不合理的耦合。

// 难以测试的功能逻辑
public void checkVideoStatus(Long videoId){
// ... 检测视频是否应该下架的逻辑
boolean needRemovie = ...;
// 检测状态的逻辑与执行结果的逻辑耦合
if (needRemovie) {
videoDao.removie(videoId);
}
} // 便于测试的功能逻辑
public boolean checkVideoStatus(){
// ... 检测视频是否应该下架的逻辑
boolean needRemovie = ...;
return needOffline;
}

重构保障

单元测试不仅可以保障本次需求的质量,良好单元测试的维护更是一种长期的投资,通过建立对各个逻辑的单元测试检测,会为各层逻辑覆盖检测,为系统重构提供质量保障。

关于单元测试的成本

大家容易对单元测试产生的另一个误区是单元测试的引入会增加不必要的成本,影响业务的快速上线。但从我们在小剧场等业务的实践中发现单元测试的使用,不仅提高了我们的质量也提升了我们的开发效率。

在一个业务需求中,开发时间一般只占比到百分之三十,其余为调试及测试时间。编写单元测试的确需要在开发阶段开销一定的时间成本,但是在编写单元测试时实际我就是在进行自测了,且单元测试调试快速,定位问题精准,无需部署调试。当我们对复杂的策略逻辑完成单元测试后,可以极大的缩短我们的联调和提测时间。从实践结果来看,由于更少的自测联调成本,采用单元测试反而提升了我们的开发效率。

另一方面单元测试具有长期的价值,这一份测试代码的存在,可以长久的守护逻辑的正确,当后续逻辑修改了之前的部分功能而使得老功能不符合预期时,单元测试可以快速定位到问题,很大程度的避免了修改老逻辑导致的问题。

如何写好单元测试

什么场景适合单元测试

若使用了单元测试,我们不必过于追求单元测试的覆盖率,并非所有代码都需要进行单元测试的覆盖,哪些地方适合单元测试我总结如下:

  • 代码中存在运算逻辑的代码,以小剧场为例,剧的上下架运算逻辑、运营数据的通用过滤逻辑适合。
  • 一些数据的简单到库的增删改查逻辑,不需要进行单元测试的覆盖,这些地方是比较简单的,覆盖意义无非是单纯的方法调用。

单元测试的粒度

前文中单元测试的基本特征中谈到,单元测试的粒度要小,这里的粒度指的是测试的边界范围要小,一个单元测试测试多个联动的业务功能是不合理的。我们以订单开单功能为例,一个业务的业务用例往往可以按树结构进行拆分为多层次的子功能。

一个订单的开单功能又涉及到商品的库存检测、促销的优惠检测、支付的价格计算等子功能,在对开单进行单元测试时,其核心需要测试的是开单功能对各个子功能结果的逻辑串联,不应该直接在此单元测试内向下进行调用,所有子功能的结果都应该按用例场景采用MOCK进行返回。

而所有子功能的正确应该由更下一级功能的单元测试自行保障,所以开单的单元测试编辑应该仅限于在开单流程层级,而不应该下渗到子功能的验证当中。

以此可以为系统功能建立多层次的单元测试体系,每一层次的功能都是经过检测的,并且也可以基于单元测试用例了解功能。

关于TDD

聊到单元测试,不得不谈到TDD,TDD全称Test-Driven Development 测试驱动开发,是一种不同于传统开发流程的开发方法,它要求在实现功能逻辑前,先进行单元测试的编写。因为有单元测试的提前介入,所以编写的代码必然是测试友好的。另一方面很重要的一点是使我们从调用视角来考虑顶层接口设计,自上而下的系统设计避免了自下而上的过度实现或实现与诉求不一致的情况。

TDD模式在我们团队并未进行广泛的使用,但在进行系统设计实现过程中我们充分的吸收了TDD自上而下的设计方式,进行代码功能开发的前期我们会先进行接口、模型的定义。

TDD的三定律

  • 在编写不能通过的单元测试前,不可以编写生产代码
  • 只可以编写刚好无法通过的单元测试,不能编译也算不通过
  • 只能编写刚好通过当前失败测试的生产代码

实操篇

上文用一定的篇幅介绍了什么是单元测试以及单元测试的一些理论规范,接下来我们将基于Spock单元测试框架来介绍单元测试的落地实操。

spock的介绍

spock与junit等单元测试框架一样都是java生态内比较流行的单元测试框架,不同点在于spock基于groovy动态语言,这使得spock相较于传统Java单元测试框架具备了更强的动态能力,从语法风格来比较 spock 单元测试的语义性更强,代码本身更能简洁直观的体现出测试用例。下对spock与传统java单元测试进行对比。

传统的java单元测试

    @Test
public void testVersionFilter() {
PowerMockito.mockStatic(CurrentScope.class, CommonWebScope.class);
PowerMockito.when(CurrentScope.appVer()).thenReturn(AppVer.of("4.0.0"));
PowerMockito.when(CurrentScope.clientId()).thenReturn(ClientId.ANDROID);
PowerMockito.when(CommonWebScope.appType()).thenReturn(AppType.ANDROID_CN ); TestResource resource;
// 在android ios 最小版本之上
resource = TestResource.builder()
.androidMinVersion("3.0.0")
.androidMaxVersion("6.0.0")
.iosMinVersion("4.0.0")
.iosMaxVersion("4.0.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(true); // 在android ios 最小版本之上 android 在最大版本上
resource = TestResource.builder()
.androidMinVersion("3.0.0")
.androidMaxVersion("4.0.0")
.iosMinVersion("4.0.0")
.iosMaxVersion("4.0.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(false); // 在android 最小版本之下 ios之上
resource = TestResource.builder()
.androidMinVersion("7.0.0")
.iosMinVersion("3.0.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(false); PowerMockito.when(CurrentScope.appVer()).thenReturn(AppVer.of("5.0.0"));
PowerMockito.when(CurrentScope.clientId()).thenReturn(ClientId.IPHONE);
PowerMockito.when(CommonWebScope.appType()).thenReturn(AppType.IOS_KWAI); // 在android ios 最小版本之上
resource = TestResource.builder()
.androidMinVersion("3.0.0")
.iosMinVersion("4.0.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(true); resource = TestResource.builder()
.androidMinVersion("4.0.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(true); resource = TestResource.builder()
.androidMinVersion("3.0.0")
.iosMinVersion("4.0.0")
.iosMaxVersion("6.0.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(true); resource = TestResource.builder()
.androidMinVersion("3.0.0")
.iosMinVersion("4.0.0")
.iosMaxVersion("4.5.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(false); // 在android 最小版本之上 ios之下
resource = TestResource.builder()
.androidMinVersion("3.0.0")
.iosMinVersion("7.0.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(false); // 在android 最小版本之下 ios之上
resource = TestResource.builder()
.androidMinVersion("7.0.0")
.iosMinVersion("3.0.0")
.build();
Assertions.assertThat(resource.isValid()).isEqualTo(true);
}

错误结果的报告

org.opentest4j.AssertionFailedError:
Expecting:
<true>
to be equal to:
<false>
but was not.
Expected :false
Actual :true
<Click to see difference>

基于groovy的spock重写

    def "测试通用版本过滤"() {
setup:
PowerMockito.mockStatic(CurrentScope.class, CommonWebScope.class);
PowerMockito.when(CurrentScope.appVer()).thenReturn(AppVer.of(appVer));
PowerMockito.when(CurrentScope.clientId()).thenReturn(clientId);
PowerMockito.when(CommonWebScope.appType()).thenReturn(appType); when:
def valid = new TestResource(
androidMinVersion: androidMinVersion,
androidMaxVersion: androidMaxVersion,
iosMinVersion: iosMinVersion,
iosMaxVersion: iosMaxVersion
).isValid() then:
valid == checkResult where:
appVer | clientId | appType | androidMinVersion | androidMaxVersion | iosMinVersion | iosMaxVersion || checkResult
"4.0.0" | ClientId.ANDROID | AppType.ANDROID_CN | "3.0.0" | "6.0.0" | "4.0.0" | "4.0.0" || true
"4.0.0" | ClientId.ANDROID | AppType.ANDROID_CN | "3.0.0" | "4.0.0" | "4.0.0" | "4.0.0" || false
"4.0.0" | ClientId.ANDROID | AppType.ANDROID_CN | "7.0.0" | null | "3.0.0" | null || false "5.0.0" | ClientId.IPHONE | AppType.IOS | "3.0.0" | null | "4.0.0" | null || true
"5.0.0" | ClientId.IPHONE | AppType.IOS | "4.0.0" | null | null | null || true
"5.0.0" | ClientId.IPHONE | AppType.IOS | "3.0.0" | null | "4.0.0" | "6.0.0" || true
"5.0.0" | ClientId.IPHONE | AppType.IOS | "3.0.0" | null | "4.0.0" | "4.5.0" || false
"5.0.0" | ClientId.IPHONE | AppType.IOS | "3.0.0" | null | "7.0.0" | null || false
"5.0.0" | ClientId.IPHONE | AppType.IOS | "7.0.0" | null | "3.0.0" | null || true
}

错误结果的报告

Condition not satisfied:

resource.isValid() == checkResult
| | | |
| true | false
| false
com.demo.play.model.activity.TestResource@1c58d7be <Click to see difference> at com.demo.play.model.activity.AdminResourceSpockTest.测试通用版本过滤(AdminResourceSpockTest.groovy:44)

通过以上传统java单元测试与spock单元测试的比对可以感受到,spock单元测试更为简短精炼,语义化更强,在异常用例报告上,spock也更清晰明了。

引入

目前spock主流两个版本 1.x 及 2.x ,当前 2.x 无法与 PowerMock 协同使用,若有大量静态方法需要 Mock 请使用 spock 1.x 版本

spock 2.x 版本

由于2.x基于junit5,不再兼容powerMock,目前无太好的静态方法mock。但是groovy升级到3.0,在闭包等方式上更简练了。

        <!-- spock 2.0 并会自动引入groovy3.0 -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>2.0-M2-groovy-3.0</version>
<scope>test</scope>
</dependency>

spock 1.x 版本(推荐)

spock 1.x 版本可以与 powerMock配合使用,可以满足基本所有单元测试场景,但不支持 groovy3 tab 闭包

	<!-- spock -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.2-groovy-2.4</version>
<scope>test</scope>
</dependency>

这样我们可以开始编写spock单元测试了,在 /src/test/(java|groovy)/ 对应的测试包下新建Groovy单元测试类,下是一个最简单的spock测试实例

// 继承 spock.lang.Specification
class FileRestTest extends Specification {
// 这是一个简单的单元测试方法
// 这里方法定义方式称为契约,可以直接用中文
def "单元测试方法"() {
expect:
Math.max(1, 2) == 3
}
}

Blocks

    def "测试 fileKey 与 host 的映射"() {
given:
def hostMap = ["play-dev.local.cn": "play-admin-dev"]
def fileRest = new FileRest() when:
def resultKey = fileRest.parseFileKeyByHostMap(uri, host, hostMap) then:
resultKey == checkKey where:
uri |host |checkKey
"/look-admin/index.html" |"look.test.demo.com" |null
"/banner/index.html" |"play-dev.local.cn" |"play-admin-dev/banner/index.html"
"/banner/" |"play-dev.local.cn" |"play-admin-dev/banner/index.html"
"/banner" |"play-dev.local.cn" |"play-admin-dev/banner/index.html"
}

上源码展示了一个完整的spock单元测试方法,与java最大的不同spock的单元测试可以拥有given、when、then等块对单元测试逻辑进行分隔。

分块 替换 功能 说明
given setup 初始化函数、MOCK 非必要
when expect 执行待测试的函数 when 和 then 必须成对出现
then expect 验证函数结果 when 和 then 可以被 expect 替换
where 多套测试数据的检测 spock的特性功能
and 对其余块进行分隔说明 非必要

用expect替换 when & then

def "测试 fileKey 与 host 的映射"() {
given:
def hostMap = ["play-dev.local.cn": "play-admin-dev"]
def fileRest = new FileRest() expect:
def resultKey = fileRest.parseFileKeyByHostMap(uri, host, hostMap)
// 在spock expect、then块中不需要显示的断言语句
// 当一个表达式返回为boolean时其自动作为断言存在
resultKey == checkKey where:
...
}

where

where主要作用用于提供多组测试数据,其可以以数据表或者列表的形式来为参数进行多次赋值。

// 数据表:
def "测试最大数"() {
expect:
Math.max(a,b) == result // 这样一个单元测试会验证两组数据
// 第一组 a = 1 , b = 2, result = 2
// 第二组 a = 3 , b = 0, result = 3
where:
a |b |result
1 |2 |2
3 |0 |3
} // 数据列表
def "测试最大数"() {
expect:
Math.max(a,b) == result // 这样一个单元测试会验证两组数据
// 第一组 a = 1 , b = 2, result = 2
// 第二组 a = 3 , b = 0, result = 3
where:
a << [1,3]
b << [2,0]
result << [2,3]
}

MOCK

spock的mock功能非常强大,语义性较传统java mock框架更突出。

mock 对象的构造

    // 通过传入class参数来构建
def person = Mock(Person)
def person = Spy(Person)
def person = Stub(Person) // 通过声明类型来构建 Spy\Stub同
Person person = Mock() // 交互式的创建 Spy\Stub同
Person person = Mock {
getName() >> "bob"
}
def person = Mock(Person) {
getName() >> "bob"
}

简单的返回值mock

	def "演示mock"() {
given:
def resource = Mock(Resource)
// >> 用于Mock返回
resource.getResourceId() >> 1L expect:
resource.getResourceId() == 1L
}

链式mock

	def "演示mock"() {
given:
def resource = Mock(Resource)
// 第一次、二次、三次、三次 分别返回 1、2、3、4
resource.getResourceId() >>> [1L,2L,3L] >> 4L expect:
resource.getResourceId() == 1L
resource.getResourceId() == 2L
resource.getResourceId() == 3L
resource.getResourceId() == 4L
} def "演示mock"() {
given:
def resource = Mock(Resource)
// 第一次调用
1 * resource.getResourceId() >> 1L
// 第二次到第三次
2 * resource.getResourceId() >> 2L
// 第四次到第六次
3 * resource.getResourceId() >> 3L expect:
resource.getResourceId() == 1L
resource.getResourceId() == 2L
resource.getResourceId() == 2L
resource.getResourceId() == 3L
resource.getResourceId() == 3L
resource.getResourceId() == 3L
}

异常mock

	def "演示mock"() {
given:
def resource = Mock(Resource)
resource.getResourceId() >> {throw new IllegalArgumentException()} when:
resource.getResourceId() then:
thrown(IllegalArgumentException)
}

复杂逻辑mock

	def "演示mock"() {
given:
def person = Mock(Person)
person.say(_) >> {
args -> "hello " + args[0]
} when:
def msg = person.say("world") then:
msg == "hello world"
}

mock & spy & stub

mock 返回的是一个所有方法都为空值的对象,当我们对一个对象部分方法进行mock,但其余方法希望基于真实对象逻辑时可以采用Spy()。

spy基于一个真实的对象,只有被Mock的方法会按指定值返回,其余方法行为与真实对象一致。

stub与mock的区别在于它只有mock的方法返回对应值,未mock的方法会返回无意义的Stub对象本身,另外mock、spy对象能统计调用次数。

	static class Person {
def getName() {
"tom"
} def getAge() {
18
}
} def "演示mock"() {
given:
def mockPerson = Mock(Person)
def spyPerson = Spy(Person)
def stubPerson = Stub(Person)
mockPerson.getName() >> "bob"
spyPerson.getName() >> "bob"
stubPerson.getName() >> "bob" expect:
mockPerson.getName() == "bob"
spyPerson.getName() == "bob"
stubPerson.getName() == "bob"
mockPerson.getAge() == null
spyPerson.getAge() == 18
stubPerson.getAge() != 18 && stubPerson.getAge() != null
}

mock static method (2.x 版本无法支持)

spock 有GroovyMock可以在Groovy代码范围内对静态方法进行mock,但无法进行Java 静态方法的mock。加入powerMock依赖:


<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.8.21</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-classloading-xstream</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>

使用powerMock mock 静态方法

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
// 此注解指定要mock的静态方法类
@PrepareForTest(value = [CurrentScope.class, CommonWebScope.class])
class AdminResourceSpockTest extends Specification { def "测试通用版本过滤"() {
setup:
// 这里对所有需要mock的静态方法类进行mock
PowerMockito.mockStatic(CurrentScope.class, CommonWebScope.class);
// 当调用 CurrentScope.appVer() 时将按thenReturn内的值返回
PowerMockito.when(CurrentScope.appVer()).thenReturn内的值返回(AppVer.of(appVer));
PowerMockito.when(CurrentScope.clientId()).thenReturn(clientId);
PowerMockito.when(CommonWebScope.appType()).thenReturn(appType); when:
def valid = new TestResource(
androidMinVersion: androidMinVersion,
androidMaxVersion: androidMaxVersion,
iosMinVersion: iosMinVersion,
iosMaxVersion: iosMaxVersion
).isValid() then:
valid == checkResult; where:
...
}
}

assert

spock断言非常简练,在then或者expect区块中,一个简单的boolean语句就可以作为一个断言,无需其他工具或关键词

	then:
// 简单的boolean表达式
code == 1 // 调用次数统计 http://spockframework.org/spock/docs/1.3/all_in_one.html#_cardinality
// mockObj这个mock对象在调用中被调用过一次,参数中 _ 代表可以是任意值匹配
1 * mockObj.run(_)
// 参数中指定传递1时被调用过一次
1 * mockObj.run(1)
// 被调用过1次及以上,且最多三次
(1..3) * mockObj.run(_)
// 至少调用一次
(1.._) * mockObj.run(_)
// 任何mock对象中方法名为run的方法被调用一次
1 * _.run(_)

groovy的一些语言特性

由于groovy具备很多语法特性,可以使得我们单元测试的编写更简洁清晰

对象构建

	// 用闭包
def resource = new Resource().with {
setResourceId(1L)
return it
} // 用构造传参
def resource = new Resource(resourceId: 1L)

字符串

groovy字符串可以通过 ''' 来编写无需转意符的字符,这在我们写一些测试json数据时帮助很大。

	// java
String json = "{\"name\":\"tom\",\"age\":18}"
// groovy
def json = '''{"name": "tom","age": 18}'''

列表

    def resourceList = [
new Resource(resourceId: 2L),
new Resource(resourceId: 3L),
new Resource(resourceId: 1L),
new Resource(resourceId: 4L)
] // << 可以替代 list.add
resourceList << new Resource(resourceId: 5L) << new Resource(resourceId: 6L)

运用Spock编写高质量单元测试的更多相关文章

  1. 使用NUnit为游戏项目编写高质量单元测试的思考

    0x00 单元测试Pro & Con 最近尝试在我参与的游戏项目中引入TDD(测试驱动开发)的开发模式,因此单元测试便变得十分必要.这篇博客就来聊一聊这段时间的感悟和想法.由于游戏开发和传统软 ...

  2. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  3. 怎样编写高质量的java代码

    代码质量概述     怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友 ...

  4. 编写高质量代码改善C#程序的157个建议——导航开篇

    前言 由于最近工作重心的转移,原来和几个同事一起开发的项目也已经上线了,而新项目就是在现有的项目基础上进行优化延伸扩展.打个比方,现在已经上线的项目行政案件的Web管理网站(代码还没那么多相比较即将要 ...

  5. 编写高质量代码:改善Java程序的151个建议(第二章:基本类型)

    编写高质量代码:改善Java程序的151个建议(第二章:基本类型) 目录 建议21:用偶判断,不用奇判断 建议22:用整数类型处理货币 建议23:不要让类型默默转换 建议24:边界还是边界 建议25: ...

  6. 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)

    编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...

  7. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  8. Effective Python之编写高质量Python代码的59个有效方法

                                                         这个周末断断续续的阅读完了<Effective Python之编写高质量Python代码 ...

  9. 怎样编写高质量的 Java 代码

    代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友提出宝贵 ...

随机推荐

  1. (四)整合 RocketMQ ,实现请求异步处理

    整合 RocketMQ ,实现请求异步处理 1.RocketMQ简介 1.1 架构图片 1.2 角色分类 1.3 通信机制 2.实现案例 2.1 项目结构图 2.2 配置文件 2.3 生产者配置 2. ...

  2. Web漏洞扫描-Burp Suite

    Web漏洞扫描-Burp Suite 一.Burp Suite概述 二.功能及特点 三.Burp Suite安装 四.Burp Suite使用 一.Burp Suite概述 安全渗透界使用最广泛的漏扫 ...

  3. Chrome标签整理

    程序人生 设计素材类网站 关于生活 求职相关网站 Web前端 科技新闻相关网站 优秀资源内含丰富学习资料 项目实例视频资料等 面试简历相关 适合初学者自学的编程网站 国内优秀博客 由于平时经常浏览一些 ...

  4. 在线安装mysql

    http://www.cnblogs.com/wishwzp/p/7113403.html

  5. 1.DHCP的定义

    1.Dynamic Host Configuration Protocol (DHCP)是一种客户端/服务器协议,可自动向Internet协议(IP)主机提供其IP地址和其他相关配置信息,例如子网掩码 ...

  6. 2020第十一届蓝桥杯第二场JavaB组

    第一题:门牌制作(624) 题目大意: 判断1到2020里面共有多少个'2': 解析: 本题简而言之就是找'2'这一个数 第一种方法:遍历将其转换为字符然后再遍历寻找 第二种方法:直接用数值的方法进行 ...

  7. SignalR入坑笔记

    什么是SignalR ASP.NET Core SignalR 是一个开源库,它简化了向应用程序添加实时 web 功能的功能. 实时 Web 功能使服务器端代码能够即时将内容推送到客户端. ASP.N ...

  8. 2020牛客暑期多校训练营(第四场)BCFH

    BCFH B. Basic God Problem 题意 给出c和n,求fc(n). 题解 递归到最后 fc 函数肯定等于1,那么就变成了求c被乘了几次,只要找到 x 最多能被分解成多少个数相乘就好了 ...

  9. JavaScript——面向对象与原型

    在最外面使用this,此时this是window作用域下的,因此他指向全局变量 对象冒充: 实例属性不会共享!

  10. DSC注册Agent失败- InternalServerError

    问题 有大概5台Agent Server,注册的时候,发现2台可以成功,其他的不成功. 注册失败的错误日志如下: 初步尝试 首先,Pull Server已经平稳的运行了几年了,此次注册还有部分Agen ...