关键词:
浅谈自动化测试
自动化测试(Test automation)是近年来逐渐兴起的软件开发流程,本系列文章将藉由作者过去的开发经验,与大家分享PHP/Laravel结合自动化测试的理论与实务,希望能对初学PHPUnit及自动化测试的同学有所帮助。查看以往系列文章,请点击文首合集。
通用型 Assertion 函数
1.assertEmpty
函数签名:assertEmpty(mixed $actual[, string $message = ''])
函数说明:从字面上来解释,这个函数是用来判断第1个输入参数 $actual 是否为 Empty,PHP常见空值可参考PHP官方函数 empty() 的说明。
示例:
/**
* Example for assertEmpty()
* @return void
*/
public function testAssertEmpty()
// 七种“空”值
$emptyArray = [];
$emptyString = '';
$null = null;
$zeroInt = 0;
$zeroFloat = 0.0;
$zeroIntString = '0';
$false = FALSE;
// 以下各 Assertion 皆会通过测试
$this->assertEmpty($emptyArray);
$this->assertEmpty($emptyString);
$this->assertEmpty($null);
$this->assertEmpty($zeroInt);
$this->assertEmpty($zeroFloat);
$this->assertEmpty($zeroIntString);
$this->assertEmpty($false);
要特别注意的是, "0" 会被认定为空值,但 "0.0" 却不会,这点要小心。
2.assertEquals
函数签名:assertEquals(mixed $expected, mixed $actual[, string $message = ''])
函数说明:此函数可用以判断,第1个参数 $expected 与第2个参数 $actual,两者的值是否相等。
示例:
/**
* Example for assertEquals()
* @return void
*/
public function testAssertEquals()
// 范例值
$string = 'string';
$intOne = 1;
$intZero = 0;
$floatOne = 1.0;
$floatZero = 0.0;
$array1 = [];
$array2 = ['string'];
$stdClass1 = new \\stdClass();
$stdClass1->a_field = 'a';
$stdClass2 = new \\stdClass();
$stdClass2->a_field = 'a';
// 以下各 Assertion 皆会通过测试
$this->assertEquals('string', $string);
$this->assertEquals(1, $intOne);
$this->assertEquals(0, $intZero);
$this->assertEquals(1.0, $floatOne);
$this->assertEquals(0.0, $floatZero);
$this->assertEquals([], $array1);
$this->assertEquals(['string'], $array2);
$this->assertEquals($stdClass1, $stdClass2);
// 以下为非相同资数据型态,但会判定为相等的情况
$this->assertEquals($intOne, $floatOne);
$this->assertEquals($intZero, $floatZero);
要特别注意的是,$intOne 会被认定为与 $floatOne 相等,$intZero 与 $floatZero 也是
3.assertSame
函数签名:assertSame(mixed $expected, mixed $actual[, string $message = ''])
函数说明:此函数可用以判断,第1个参数 $expected 与第2个参数 $actual,两者数据型态与值是否相等。
示例:
/**
* Example for assertSame()
* @return void
*/
public function testAssertSame()
// 范例值
$string = 'string';
$intOne = 1;
$intZero = 0;
$floatOne = 1.0;
$floatZero = 0.0;
$array1 = [];
$array2 = ['string'];
// 范例物件
$stdClass1 = new \\stdClass();
$stdClass1->a_field = 'a';
$stdClass2 = new \\stdClass();
$stdClass2->a_field = 'a';
$stdClass3 = $stdClass2;
// 以下各 Assertion 皆会通过测试
$this->assertSame('string', $string);
$this->assertSame(1, $intOne);
$this->assertSame(0, $intZero);
$this->assertSame(1.0, $floatOne);
$this->assertSame(0.0, $floatZero);
$this->assertSame([], $array1);
$this->assertSame(['string'], $array2);
// 以下 Assertion 会通过测试,因两者指向相同的物件参考位置
$this->assertSame($stdClass3, $stdClass2);
// 以下为非相同数据型态,但会判定为相等的情况
$this->assertSame($intOne, $floatOne);
$this->assertSame($intZero, $floatZero);
// 以下 Assertion 将会不通过测试,因两者所指向的物件参考位置不同
$this->assertSame($stdClass1, $stdClass2);
值得一提的是,与 assertEquals() 相同的是,$intOne 会被认定为与 $floatOne 相等,$intZero 与 $floatZero 也是;与 assertEquals() 不同的是,它会认定2个拥有相同属性结构的类别变量是不同的。
4.assertTrue
函数签名:assertTrue(bool $condition[, string $message = ''])
函数说明:此函数可用以判断,第1个参数是否为 TRUE 。
示例:
/**
* Example for assertTrue()
* @return void
*/
public function testAssertTrue()
$notEmptyString = 'a';
$notZeroInt = 1;
$notZeroFloat = 0.1;
$stdClass = new \\stdClass();
$true = TRUE;
// 以下各 Assertion 皆会通过测试
$this->assertTrue(true);
$this->assertTrue($true);
$this->assertTrue(1 == 1);
$this->assertTrue(1 === 1);
$this->assertTrue($true);
// 以下 Assertion 将会不通过测试
$this->assertTrue($notEmptyString);
$this->assertTrue($notZeroInt);
$this->assertTrue($notZeroFloat);
$this->assertTrue($stdClass);
特别注意,assertTrue的第1个参数的数据型态必须是bool,代入字符串或其他数据型态都会不通过。
5.assertFalse
函数签名:assertFalse(bool $condition[, string $message = ''])
函数说明:与 assertTrue 相对,此函数可用以判断,第1个参数是否为 FALSE 。
示例:
/**
* Example for assertFalse()
* @return void
*/
public function testAssertFalse()
$notEmptyString = 'a';
$notZeroInt = 1;
$zeroInt = 0;
$notZeroFloat = 0.1;
$zeroFloat = 0.0;
$stdClass = new \\stdClass();
$false = FALSE;
// 以下各 Assertion 皆会通过测试
$this->assertFalse(false);
$this->assertFalse($false);
$this->assertFalse(1 > 1);
$this->assertFalse(1 < 1);
$this->assertFalse(1 != 1);
// 以下 Assertion 将会不通过测试
$this->assertFalse($notEmptyString);
$this->assertFalse($notZeroInt);
$this->assertFalse($zeroInt);
$this->assertFalse($notZeroFloat);
$this->assertFalse($zeroFloat);
$this->assertFalse($stdClass);
同样要注意,assertFalse的第1个参数的数据型态必须是bool,代入字符串或其他数据型态都会不通过。
6.assertIsArray
函数签名:assertIsArray($actual[, $message = ''])
函数说明:顾名思义,此函数可用来判断第1个参数是否为 Array 型态。
示例:
/**
* Example for assertIsArraye()
* @return void
*/
public function testAssertIsArray()
$array1 = [];
$array2 = ['2'];
$array3 = ['key' => 'value'];
$collection = collect([]);
// 以下各 Assertion 皆会通过测试
$this->assertIsArray($array1);
$this->assertIsArray($array2);
$this->assertIsArray($array3);
$this->assertIsArray($collection->toArray());
// 以下 Assertion 将会不通过测试
$this->assertIsArray($collection);
特别注意的是,当你想判定的对象可能是 Collection 时,直接代入 assertIsArray() 将导致判定不通过。
7.assertIsInt
函数签名:assertIsInt($actual[, $message = ''])
函数说明:此函数可用来判断第1个参数是否为 Int 型态。
示例:
/**
* Example for assertIsInt()
* @return void
*/
public function testAssertIsInt()
$int1 = 0;
$int2 = 1;
$float1 = 0.0;
$float2 = 1.0;
$string1 = '0';
$string2 = '1';
// 以下各 Assertion 皆会通过测试
$this->assertIsInt($int1);
$this->assertIsInt($int2);
// 以下 Assertion 将会不通过测试
$this->assertIsInt($float1);
$this->assertIsInt($float2);
$this->assertIsInt($string1);
$this->assertIsInt($string2);
8.assertIsFloat
函数签名:assertIsFloat($actual[, $message = ''])
函数说明:此函数可用来判断第1个参数是否为 Float 型态。
示例:
/**
* Example for assertIsFloat()
* @return void
*/
public function testAssertIsFloat()
$float1 = 0.0;
$float2 = 1.0;
$float3 = -1.0;
// 以下各 Assertion 皆会通过测试
$this->assertIsInt($float1);
$this->assertIsInt($float2);
$this->assertIsInt($float3);
以上是几个通用型 Assertion 函数,有兴趣同学,可参考范例练习看看。
HTTP相关的Assertion函数
这几个函数,其使用方式和上面所介绍的略有不同。以下所列各函数,皆是基于 HTTP Response 来做验证测试,因此大家会看到 $response = $this->get('/') 像这样的语句,执行到这语句时,PHPUnit 将会执行HTTP Resquest GET / ,相当于用浏览器开启网站根网址,或是用 Postman 打网站的根网址。更多详细说明可参考此链接:https://laravel.com/docs/9.x/http-tests#introduction
除此之外,下面所提到的几个 Assertion 函数,并非 PHPUnit 内建,而是由 Laravel 所扩充,因此需注意是否有确实引用到 use Tests\\TestCase ,该档通常位于 tests/ 底下:
<?php
// tests/TestCase.php
namespace Tests;
use Illuminate\\Foundation\\Testing\\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
use CreatesApplication;
Cookie & Session
1.assertCookie
函数签名:$response->assertCookie($cookieName, $value = null)
函数说明:可验证回应中是否含有 $cookieName 的 Cookie,且是否与给定的 $value 值相等。
示例:
Route:
Route::get('/assertCookie', function ()
return response('')->withCookie('cookieName', 'cookieValue');
);
Test:
/**
* Example for assertCookie()
* @return void
*/
public function testAssertCookie()
$response = $this->get('/assertCookie');
// 通过测试
$response->assertCookie('cookieName', 'cookieValue');
2.assertCookieMissing
函数签名:$response->assertCookieMissing($cookieName)
函数说明:可验证回应中是否不含 $cookieName 的 Cookie。
示例:
Route:
Route::get('/assertCookieMissing', function ()
return response('')->withCookie('cookieName', 'cookieValue');
);
Test:
/**
* Example for assertCookieMissing()
* @return void
*/
public function testAssertCookieMissing()
$response = $this->get('/assertCookie');
// 通过测试
$response->assertCookieMissing('cookieName2');
3.assertSessionHas
函数签名:$response->assertSessionHas($key, $value)
函数说明:可验证在回应请求后,Laravel Session 储存库是否含有指定 Key 值的 Session。
示例:
Route:
Route::get('/assertSessionHas', function ()
Session::put('sessionKey', 'sessionValue');
return response('');
);
Test:
/**
* Example for assertSessionHas()
* @return void
*/
public function testassertSessionHas()
$response = $this->get('/assertSessionHas');
// 通过测试
$response->assertSessionHas('sessionKey');
HTTP
1.assertSuccessful、assertOk、assertNotFound、assertForbidden、assertUnauthorized、assertUnprocessable
函数签名:
$response->assertSuccessful()
$response->assertOk()
$response->assertNotFound()
$response->assertForbidden()
$response->assertUnauthorized()
$response->assertUnprocessable()
函数说明:这6个函数所验证的情境很单纯,都是验证 HTTP Status Code,细部分别如下:
assertSuccessful:回应为成功类HTTP Status Code(>= 200 and < 300)
assertOk:回应为 200 HTTP Status Code
assertNotFound:回应为 400 HTTP Status Code
assertForbidden:回应为 403 HTTP Status Code
assertUnauthorized:回应为 401 HTTP Status Code
assertUnprocessable:回应为 422 HTTP Status Code
示例:
Route:
Route::get('/notFound', function ()
return response('', 404);
);
Route::get('/ok', function ()
return response('', 200);
);
Route::get('/successful', function ()
return response('', 201);
);
Route::get('/forbidden', function ()
return response('', 403);
);
Route::get('/unauthorized', function ()
return response('', 401);
);
Route::get('/unprocessable', function ()
return response('', 422);
);
Test:
/**
* Example for assertSuccessful()、assertOk()、assertNotFound()、assertForbidden()、assertUnauthorized()、assertUnprocessable()
* @return void
*/
public function testAssertHttpStatusCode()
$response1 = $this->get('/notFound');
$response2 = $this->get('/ok');
$response3 = $this->get('/successful');
$response4 = $this->get('/forbidden');
$response5 = $this->get('/unauthorized');
$response6 = $this->get('/unprocessable');
// 以下各 Assertion 皆会通过测试
$response1->assertNotFound();
$response2->assertOk();
$response3->assertSuccessful();
$response4->assertForbidden();
$response5->assertUnauthorized();
$response6->assertUnprocessable();
2.assertJson
函数签名:$response->assertJson(array $data, $strict = false)
函数说明:此函数会验证回应是否为JSON格式,并且判断其JSON结构(包含栏位及值)是否包含给定的 $data 结构(包含栏位及值)。
示例:
Route:
Route::get('/assertJson', function ()
return response()->json(
[
'field1' => 'value1',
'field2' => 'value2',
]
);
);
Test:
/**
* Example for assertJson()
* @return void
*/
public function testAssertJson()
$response = $this->get('/assertJson');
// 通过测试
$response->assertJson(['field1' => 'value1']);
3.assertJsonStructure
函数签名:$response->assertJsonStructure(array $structure)
函数说明:与前一个函数不同,此函数只验证是否含有给定的结构(不验证值)。
示例:
Route:
Route::get('/assertJsonStructure', function ()
return response()->json(
[
'a' => [
'b' => [
'c',
],
],
]
);
);
Test:
/**
* Example for assertJsonStructure()
* @return void
*/
public function testAssertJsonStructure()
$response = $this->get('/assertJsonStructure');
// 通过测试
$response->assertJsonStructure(['a' => ['b']]);
以上介绍的就是几个 HTTP 相关 Assertion 函数。由于 Assertion 函数实在太多,取舍之下,只能先为大家介绍最重要与最常用的几个。
数据库 、阵列Assertion函数
下面再介绍几个数据库 Assertion 函数,与阵列 Assertion 函数。以下提到的数据库 Assertion 函数,并非 PHPUnit 内建,而是由 Laravel 所扩充,因此需注意是否有确实引用到 use Tests\\TestCase 。最后面介绍的2个阵列 Assertion 函数,则是PHPUnit内建的。
在介绍数据库 Assertion 函数前,要先请大家在根目录下的 phpunit.xml 的 <php> 标签内,加入以下 <env> 子标签:
<php>
<-- ...前略...->
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<-- ...后略...->
</php>
这2个设定值,会在执行PHPUnit测试时,暂时使用 phpunit.xml 内 <env> 所设定的数据库环境变量值,以避免改变到实际数据库的数据。同时,在数据库 Assertion 函数的示例中,皆会使用到 Illuminate\\Foundation\\Testing\\RefreshDatabase 这个 Trait。
让让我们看看吧!
数据库
1.assertDatabaseCount
函数签名:assertDatabaseCount($table, int $count, $connection = null)
函数说明:此函数可以验证,指定数据库连线下,指定数据表是否含有指定数量的数据。当没有指定数据库连线时,将会使用预设数据库连线。
示例:
<?php
namespace Tests\\Feature;
use Illuminate\\Foundation\\Testing\\RefreshDatabase;
use Tests\\TestCase;
class DatabaseTest extends TestCase
use RefreshDatabase;
/**
* Example for assertDatabaseCount()
* @return void
*/
public function testAssertDatabaseCount()
// 假设 users 数据表中有1笔数据,则以下 Assestion 将通过测试
$this->assertDatabaseCount('users', 1);
2.assertDatabaseHas
函数签名:assertDatabaseHas($table, array $data, $connection = null)
函数说明:此函数可以验证,指定数据库连线下,指定数据表是否含有指定数据结构与值的数据。与前一函数相同,当没有指定数据库连线时,将会使用预设数据库连线。
示例:
<?php
namespace Tests\\Feature;
use Illuminate\\Foundation\\Testing\\RefreshDatabase;
use Tests\\TestCase;
class DatabaseTest extends TestCase
use RefreshDatabase;
/**
* Example for assertDatabaseHas()
* @return void
*/
public function testAssertDatabaseHas()
// 假设 users 数据表中至少有1条数据
// 其 email 栏位值为 test@test.com
// 则以下 Assestion 将通过测试
$this->assertDatabaseHas('users', [
'email' => 'test@test.com'
]);
3.assertDatabaseMissing
函数签名:assertDatabaseMissing($table, array $data, $connection = null)
函数说明:此函数可以验证,指定数据库连线下,指定数据表是否 不 含有指定数据结构与值的数据。与前一函数相同,当没有指定数据库连线时,将会使用预设数据库连线。
示例:
<?php
namespace Tests\\Feature;
use Illuminate\\Foundation\\Testing\\RefreshDatabase;
use Tests\\TestCase;
class DatabaseTest extends TestCase
use RefreshDatabase;
/**
* Example for assertDatabaseMissing()
* @return void
*/
public function testAssertDatabaseMissing()
// 假设 users 数据表中无任1条数据
// 其 email 栏位值为 test@test.com
// 则以下 Assestion 将通过测试
$this->assertDatabaseMissing('users', [
'email' => 'test@test.com'
]);
阵列
1.assertArrayHasKey
函数签名:assertArrayHasKey($key, $array, string $message = '')
函数说明:这个函数相当单纯,可检查第2个参数中,是否含有第2个参数所述的键。
示例:
/**
* Example for assertArrayHasKey()
* @return void
*/
public function testAssertArrayHasKey()
$array = ['key' => 'value'];
$this->assertArrayHasKey('key', $array);
2.assertJsonStringEqualsJsonString
函数签名:assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = '')
函数说明:有时会需要验证结构比较复杂的关联式阵列,但PHPUnit被未提供相对应的关联式阵列 Assertion 函数,这时候便可使用此函数,将欲验证的值先做 json_encode ,代入第2参数,并将期望的关联式阵列结构与值,以JSON String的形式代入第1个参数。
示例:
/**
* Example for assertJsonStringEqualsJsonString()
* @return void
*/
public function testAssertJsonStringEqualsJsonString()
$array = [
'key' => 'value',
'object1' => [
'array' => [1, 2, 3,]
],
];
$this->assertJsonStringEqualsJsonString(
'"object1":"array":[1,2,3],"key":"value"',
json_encode($array)
);
以上大概介绍了20多个Assertion 函数,之后介绍运用函数撰写API自动化测试。
参考资料:
1.https://phpunit.readthedocs.io/en/9.5/assertions.html
2.https://laravel.com/docs/9.x/http-tests#available-assertions
3.https://laravel.com/docs/9.x/dusk#available-assertions
4.https://laravel.com/docs/9.x/database-testing#available-assertions
5.https://www.php.net/manual/en/function.empty.php
6.https://laravel.com/docs/9.x/http-tests
7.https://laravel.com/docs/9.x/database-testing
8.https://phpunit.readthedocs.io/en/9.5/assertions.html
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!
解决用tryexcept捕获assert函数产生的assertionerror异常时,导致断言失败的用例在测试报告中通过的问题(代码片段)
在使用Python3做自动化测试过程中可能会遇到,assert函数不加try except,就可以正常在报告里体现用例不通过,加上变成通过。这是因为在使用tryexcept时,捕获了assert函数产生的AssertionError异常,导致异常没有上抛,这时只需要... 查看详情
浅谈接口自动化测试
昨晚在某个测试交流群,听了一个测试老司机分享接口自动化测试的内容,对接口自动化有了更深的一些认识,也为接下来公司的接口自动化实施,提供了更多的思路。这篇博客,就说说功能测试到接口自动化的进阶,以及接口... 查看详情
浅谈接口自动化测试
...)昨晚在某个测试交流群,听了一个测试老司机分享接口自动化测试的内容,对接口自动化有了更深的一些认识,也为接下来公司的接口自动化实施,提供了更多的思路。这篇博客,就说说功能测试到接口自动化的进阶,以及接... 查看详情
浅谈自动化测试
...在学习python,正好部门技术结构调整,就开始了点工向UI自动化测试的转变,我要说瞌睡来了就掉枕头么?不过还好,可以将python的学习成果在自动化测试中实践。。。 1、about自动化测试定义:把人为驱动的测试转化为机器... 查看详情
浅谈硬件自动化测试框架
关于自动化测试框架,默认都是针对纯软件的,并且集中在web和app应用软件。故而准确的说应该是软件产品自动化测试框架。本文将针对硬件产品,谈谈个人对硬件自动化测试框架的一些看法。背景物联网技术正在高速发展,相... 查看详情
浅谈接口自动化测试
...晚在某个测试交流群,听了一个测试老司机分享接口自动化测试的内容,对接口自动化有了更深的一些认识,也为接下来公司的接口自动化实施,提供了更多的思路。 这篇博客,就说说功能测试到接口自动化... 查看详情
关于自动化学习浅谈二
什么是自动化测试?自动化测试的原理是什么?常用哪些工具?常用哪些框架?工具的原理又是什么?自动化如何学习?自动化的优点与缺点是什么?价值在哪里?学习自动化测试要了解与掌握哪些内容?哪些项目适合自动化测... 查看详情
googletest退出一个测试用例
googletest退出一个测试用例:googletest提供了三组宏分别用于测试导致进程崩溃、导致进程退出、抛异常的函数,他们分别是EXPECT_DEATH、EXPECT_EXIT、EXPECT_THROW及其各自的ASSERT版本。使用这些宏测试相应的函数不会干扰单元测试的工作... 查看详情
unity游戏开发浅谈unity游戏开发中的单元测试
...函数去进行验证,检查它的正确性。一个单元测试是一段自动化的代码,这段代码调用被测试的工作单元,之后对这个单元的单个最终结果的某些假设进行检验。单元测试使用单元测试框架编写,并要求单元测试可靠、可读并且... 查看详情
浅谈软件测试流程
浅谈软件测试流程 【摘要】软件测试从哪里开始到哪里结束?中间要经过哪些环节以及各环节要注意哪些事项。本文就有关问题结合个人实际工作经验进行阐述,鉴于每个环节都可以做为一个专题来进行探讨,所以受篇... 查看详情
1.assert
...例,是理解、重写而不是抄袭。 assert模块提供了断言测试的函数,用于测试。一般测试模块都是对这个模块的封装。此模块中只有函数,没有类。如果测试失败,会抛出AssertionError类型的异常。 assert(value[,message])assert.ok()... 查看详情
观察测试失败信息(代码片段)
我使用的是boosttest在自制的GUI中,并希望访问测试结果(例如测试失败时的失败信息和位置)。该unit_test::test_observer类提供了虚拟方法。voidassertion_result(boost::unit_test::assertion_result)然而,unit_test::assertion_result只是一个表示成功或... 查看详情
浅谈自动化测试之持续集成
...,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快地发现集成错误。许多团队发现这个过程可以大大减少集成的问题,让团队能够更快的开发内聚 查看详情
浅谈自动化测试
...,对提高软件质量起着重要作用。随着软件测试的发展,自动化测试技术也得到了很大提高。本文首先介绍了自动化测试的概念、分类和现状,并分别对不同端上的自动化测试实现原理进行了详细地分析和阐述,通过对目前主流... 查看详情
python断言方法:assert
...完测试用例后,最后一步是判断测试结果是pass还是fail,自动化测试脚本里面一般把这种生成测试结果的方法称为断言(assert)。用unittest组件测试用例的时候,断言的方法还是很多的,下面介绍几种常用的断言方法:assertEqual、... 查看详情
9个问题浅谈自动化测试与测试用例的编写
1、请问一般情况的安全测试都是从哪几个方面展开的? 安全测试主要针对以下漏洞类型进行测试,顺便罗列一些常用的测试工具、80%都是我们在用的。 (1)弱口令Nessus\\X-scan\\h-scan\\hydra (2)ACL访问控制列表暴露在外... 查看详情
rust编程语言入门之编写自动化测试(代码片段)
编写自动化测试一、编写和运行测试测试(函数)测试:函数验证非测试代码的功能是否和预期一致测试函数体(通常)执行的3个操作:准备数据/状态运行被测试的代码断言(Assert)结果解剖测试函数测试函数需要使用test属性... 查看详情
浅谈软件测试流程
【摘要】软件测试从哪里开始到哪里结束?中间要经过哪些环节以及各环节要注意哪些事项。本文就有关问题结合个人实际工作经验进行阐述,鉴于每个环节都可以做为一个专题来进行探讨,所以受篇幅和时间限制,本文对有关... 查看详情