Google C++单元测试框架GoogleTest---GMock的Che…
2018-06-17 23:32:16来源:未知 阅读 ()
CheatSheet文档中包含了GMock所有常用的东西,看了这个基本上就可以用它了,本文接上篇博文:Google C++单元测试框架GoogleTest---Google Mock简介--概念及基础语法 ,建议先看上一篇,再看本篇内容。
本文翻译自:https://github.com/google/googletest/blob/master/googlemock/docs/CheatSheet.md
一、定义一个模拟类
1. 模拟一个正常的类,就是接口类
给:
class Foo { ... virtual ~Foo(); virtual int GetSize() const = 0; virtual string Describe(const char* name) = 0; virtual string Describe(int type) = 0; virtual bool Process(Bar elem, int count) = 0; };
(note that ~Foo()
must be virtual) we can define its mock as,定义模拟类。
#include "gmock/gmock.h" class MockFoo : public Foo { MOCK_CONST_METHOD0(GetSize, int()); MOCK_METHOD1(Describe, string(const char* name)); MOCK_METHOD1(Describe, string(int type)); MOCK_METHOD2(Process, bool(Bar elem, int count)); };
创建一个“nice”模拟对象忽略所有无趣的调用,或一个“strict”模拟对象,将它们视为失败:
NiceMock<MockFoo> nice_foo; // The type is a subclass of MockFoo. StrictMock<MockFoo> strict_foo; // The type is a subclass of MockFoo.
2. 模拟一个类模板
To mock:
template <typename Elem> class StackInterface { public: ... virtual ~StackInterface(); virtual int GetSize() const = 0; virtual void Push(const Elem& x) = 0; };
(note that ~StackInterface()
must be virtual) just append _T
to the MOCK_*
macros:
template <typename Elem> class MockStack : public StackInterface<Elem> { public: ... MOCK_CONST_METHOD0_T(GetSize, int()); MOCK_METHOD1_T(Push, void(const Elem& x)); };
3. 指定模拟函数的调用约定
如果您的mock函数不使用默认调用约定,您可以通过将_WITH_CALLTYPE附加到前两个部分中描述的任何宏并指定调用约定作为宏的第一个参数来指定它。例如,
MOCK_METHOD_1_WITH_CALLTYPE(STDMETHODCALLTYPE,Foo,bool(int n)); MOCK_CONST_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE,Bar,int(double x,double y));?
其中STDMETHODCALLTYPE由Windows上的<objbase.h>定义。
二、在测试中使用模拟器
典型的流程是:
- 导入您需要使用的Google Mock名称。所有Google Mock名称都位于测试命名空间中,除非它们是宏或其他注释。
- 创建模拟对象。
- (可选)设置模拟对象的默认操作。
- 设置你对模拟对象的期望(他们怎么叫?他们做什么?)。
- 使用模拟对象的练习代码;如有必要,请使用Google Test断言检查结果。
- 当模拟对象被破坏时,Google Mock会自动验证所有对其的期望是否满足。
这里是一个例子:
using ::testing::Return; // #1 TEST(BarTest, DoesThis) { MockFoo foo; // #2 ON_CALL(foo, GetSize()) // #3 .WillByDefault(Return(1)); // ... other default actions ... EXPECT_CALL(foo, Describe(5)) // #4 .Times(3) .WillRepeatedly(Return("Category 5")); // ... other expectations ... EXPECT_EQ("good", MyProductionFunction(&foo)); // #5 } // #6
三、设置默认操作
Google Mock对任何返回void,bool,数值或指针的函数都有一个内置的默认动作。
要为全局返回类型T的函数自定义默认操作:
using ::testing::DefaultValue; // Sets the default value to be returned. T must be CopyConstructible. DefaultValue<T>::Set(value); // Sets a factory. Will be invoked on demand. T must be MoveConstructible. // T MakeT(); DefaultValue<T>::SetFactory(&MakeT); // ... use the mocks ... // Resets the default value. DefaultValue<T>::Clear();
要自定义特定方法的默认操作,请使用ON_CALL():
ON_CALL(mock_object, method(matchers)) .With(multi_argument_matcher) ? .WillByDefault(action);
四、设置期望
EXPECT_CALL()在模拟方法上设置期望(如何调用它?它会做什么?):
EXPECT_CALL(mock_object, method(matchers)) .With(multi_argument_matcher) ? .Times(cardinality) ? .InSequence(sequences) * .After(expectations) * .WillOnce(action) * .WillRepeatedly(action) ? .RetiresOnSaturation(); ?
如果省略Times(),则基数假定为:
- Times(1)当没有WillOnce()和WillRepeatedly();
- 当有n个WillOnce()但没有WillRepeatedly()时,Times(n),其中n> = 1; 要么
- 当有n个WillOnce()和WillRepeatedly(),其中n> = 0时,Times(AtLeast(n))。
没有EXPECT_CALL()的方法可以被任意调用多次,并且每次都将采取默认操作。
五、匹配
匹配器匹配单个参数。 您可以在ON_CALL()或EXPECT_CALL()中使用它,或使用它直接验证值:
EXPECT_THAT(value, matcher) | Asserts that value matches matcher . |
---|---|
ASSERT_THAT(value, matcher) |
The same as EXPECT_THAT(value, matcher) , except that it generates a fatal failure. |
内置的匹配(其中参数是函数参数)分为几类:
1. 通配符
_ | argument can be any value of the correct type可以代表任意类型. |
---|---|
A<type>() or An<type>() |
argument can be any value of type可以是type类型的任意值 . |
2. 一般比较
Eq(value) 或者 value | argument == value,method中的形参必须是value |
Ge(value) | argument >= value,method中的形参必须大于等于value |
Gt(value) | argument > value |
Le(value) | argument <= value |
Lt(value) | argument < value |
Ne(value) | argument != value |
IsNull() | method的形参必须是NULL指针 |
NotNull() | argument is a non-null pointer |
Ref(variable) | 形参是variable的引用 |
TypedEq<type>(value) | 形参的类型必须是type类型,而且值必须是value。当模拟函数被重载你可能需要它而不是Eq(vlaue) |
注意** 除了Ref()之外,这些匹配器会创建一个值的副本,以备日后修改或销毁。 如果编译器抱怨该值没有公共副本构造函数,请尝试将其包装在ByRef()中,例如。 Eq(ByRef(non_copyable_value))。 如果你这样做,请确保non_copyable_value之后不改变,否则你的匹配器的含义将被改变。
3. 浮点数的比较
DoubleEq(a_double) | 形参是一个double类型,比如值近似于a_double,两个NaN是不相等的 |
FloatEq(a_float) | 同上,只不过类型是float |
NanSensitiveDoubleEq(a_double) | 形参是一个double类型,比如值近似于a_double,两个NaN是相等的,这个是用户所希望的方式 |
NanSensitiveFloatEq(a_float) | 同上,只不过形参是float |
上述匹配器使用基于ULP的比较(与Google Test中使用的比较相同)。 它们根据期望值的绝对值自动选择合理的误差界限。 DoubleEq()和FloatEq()符合IEEE标准,这需要比较两个NaNs的相等性返回false。 NanSensitive *版本将两个NaNs相等,这通常是用户想要的。
DoubleNear(a_double, max_abs_error) | argument is a double value close to a_double (absolute error <= max_abs_error ), treating two NaNs as unequal. |
---|---|
FloatNear(a_float, max_abs_error) |
argument is a float value close to a_float (absolute error <= max_abs_error ), treating two NaNs as unequal. |
NanSensitiveDoubleNear(a_double, max_abs_error) |
argument is a double value close to a_double (absolute error <= max_abs_error ), treating two NaNs as equal. |
NanSensitiveFloatNear(a_float, max_abs_error) |
argument is a float value close to a_float (absolute error <= max_abs_error ), treating two NaNs as equal. |
4. String Matchers
这里的字符串即可以是C风格的字符串,也可以是C++风格的。
ContainsRegex(string) | 形参匹配给定的正则表达式 |
EndsWith(suffix) | 形参以suffix截尾 |
HasSubstr(string) | 形参有string这个子串 |
MatchesRegex(string) | 从第一个字符到最后一个字符都完全匹配给定的正则表达式. |
StartsWith(prefix) | 形参以prefix开始 |
StrCaseEq(string) | 参数等于string,并且忽略大小写 |
StrCaseNe(string) | 参数不是string,并且忽略大小写 |
StrEq(string) | 参数等于string |
StrNe(string) | 参数不等于string |
5. 容器的匹配
很多STL的容器的比较都支持==这样的操作,对于这样的容器可以使用上述的Eq(expected_container)来比较或者只是expect_container来完全匹配容器。但如果你想写得更为灵活,可以使用下面的这些容器匹配方法:
ContainerEq(container) | The same as Eq(container) except that the failure message also includes which elements are in one container but not the other. |
---|---|
Contains(e) |
argument contains an element that matches e , which can be either a value or a matcher. |
Each(e) |
argument is a container where every element matches e , which can be either a value or a matcher. |
ElementsAre(e0, e1, ..., en) |
argument has n + 1 elements, where the i-th element matches ei , which can be a value or a matcher. 0 to 10 arguments are allowed. |
ElementsAreArray({ e0, e1, ..., en }) , ElementsAreArray(array) , or ElementsAreArray(array, count) |
The same as ElementsAre() except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. |
IsEmpty() |
argument is an empty container (container.empty() ). |
Pointwise(m, container) |
argument contains the same number of elements as in container , and for all i, (the i-th element in argument , the i-th element in container ) match m , which is a matcher on 2-tuples. E.g. Pointwise(Le(), upper_bounds) verifies that each element in argument doesn't exceed the corresponding element in upper_bounds . See more detail below. |
SizeIs(m) |
argument is a container whose size matches m . E.g. SizeIs(2) or SizeIs(Lt(2)) . |
UnorderedElementsAre(e0, e1, ..., en) |
argument has n + 1 elements, and under some permutation each element matches an ei (for a different i ), which can be a value or a matcher. 0 to 10 arguments are allowed. |
UnorderedElementsAreArray({ e0, e1, ..., en }) , UnorderedElementsAreArray(array) , or UnorderedElementsAreArray(array, count) |
The same as UnorderedElementsAre() except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. |
WhenSorted(m) |
When argument is sorted using the < operator, it matches container matcher m . E.g. WhenSorted(UnorderedElementsAre(1, 2, 3)) verifies that argument contains elements 1 , 2 , and 3 , ignoring order. |
WhenSortedBy(comparator, m) |
The same as WhenSorted(m) , except that the given comparator instead of < is used to sort argument . E.g. WhenSortedBy(std::greater<int>(), ElementsAre(3, 2, 1)) . |
注意:
-
这些匹配器也可以匹配:
i. 通过引用传递的本地数组(例如在Foo(const int(&a)[5])中)和
ii. 作为指针和计数传递的数组(例如,在Bar(const T * buffer,int len) - 参见 Multi-argument Matchers)。
-
匹配的数组可以是多维的(即其元素可以是数组)。
-
在Pointwise(m,...)中的m应该是:: testing :: tuple <T,U>的匹配器,其中T和U分别是实际容器和预期容器的元素类型。 例如,要比较两个Foo容器,其中Foo不支持operator ==但是有一个Equals()方法,可以写:
using ::testing::get; MATCHER(FooEq, "") { return get<0>(arg).Equals(get<1>(arg)); } ... EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos));
6. 成员匹配器
Field(&class::field, m) | argument.field (or argument->field when argument is a plain pointer) matches matcher m , where argument is an object of type class. |
---|---|
Key(e) |
argument.first matches e , which can be either a value or a matcher. E.g. Contains(Key(Le(5))) can verify that a map contains a key <= 5 . |
Pair(m1, m2) |
argument is an std::pair whose first field matches m1 and second field matches m2 . |
Property(&class::property, m) |
argument.property() (or argument->property() when argument is a plain pointer) matches matcher m , where argument is an object of type class. |
7. 匹配函数或函数的结果
ResultOf(f, m) | f(argument) matches matcher m , where f is a function or functor. |
---|
8. 指针匹配
|
|
---|---|
|
when |
9. 多参数匹配器
从技术上讲,完全匹配器匹配单个值。 “多参数”匹配器只是匹配元组的匹配器。 以下匹配器可用于匹配元组(x,y):
Eq() | x == y |
---|---|
Ge() |
x >= y |
Gt() |
x > y |
Le() |
x <= y |
Lt() |
x < y |
Ne() |
x != y |
您可以使用以下选择器来选择参数的子集(或对其重新排序)以参与匹配:
AllArgs(m) | Equivalent to m . Useful as syntactic sugar in .With(AllArgs(m)) . |
---|---|
Args<N1, N2, ..., Nk>(m) |
The tuple of the k selected (using 0-based indices) arguments matches m , e.g. Args<1, 2>(Eq()) . |
10. 复合匹配
你可以从一个或多个其他匹配器做一个匹配器:
AllOf(m1, m2, ..., mn) | argument matches all of the matchers m1 to mn . |
---|---|
AnyOf(m1, m2, ..., mn) |
argument matches at least one of the matchers m1 to mn . |
Not(m) |
argument doesn't match matcher m . |
11. Adapters for Matchers
MatcherCast<T>(m) | casts matcher m to type Matcher<T> . |
---|---|
SafeMatcherCast<T>(m) |
safely casts matcher m to type Matcher<T> . |
Truly(predicate) |
predicate(argument) returns something considered by C++ to be true, where predicate is a function or functor. |
12 .匹配作为谓词(Matchers as Predicates)
Matches(m)(value) | evaluates to true if value matches m . You can use Matches(m) alone as a unary functor. |
---|---|
ExplainMatchResult(m, value, result_listener) |
evaluates to true if value matches m , explaining the result to result_listener . |
Value(value, m) |
evaluates to true if value matches m . |
13. 定义匹配
MATCHER(IsEven, "") { return (arg % 2) == 0; } | Defines a matcher IsEven() to match an even number. |
---|---|
MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; } |
Defines a macher IsDivisibleBy(n) to match a number divisible by n . |
MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; } |
Defines a matcher IsBetween(a, b) to match a value in the range [a , b ]. |
笔记:
- MATCHER *宏不能在函数或类中使用。
- 匹配器主体必须是纯功能性的(即它不能有任何副作用,并且结果必须不依赖于被匹配的值和匹配器参数之外的任何东西)。
- 您可以使用PrintToString(x)将任何类型的值x转换为字符串。
14. 匹配作为测试断言
ASSERT_THAT(expression, m) | Generates a fatal failure if the value of expression doesn't match matcher m . |
---|---|
EXPECT_THAT(expression, m) |
Generates a non-fatal failure if the value of expression doesn't match matcher m . |
六、动作Actions
操作指定了mock函数在调用时应该执行的操作。
1. 返回值
Return() | Return from a void mock function. |
---|---|
Return(value) |
Return value . If the type of value is different to the mock function's return type, value is converted to the latter type at the time the expectation is set, not when the action is executed. |
ReturnArg<N>() |
Return the N -th (0-based) argument. |
ReturnNew<T>(a1, ..., ak) |
Return new T(a1, ..., ak) ; a different object is created each time. |
ReturnNull() |
Return a null pointer. |
ReturnPointee(ptr) |
Return the value pointed to by ptr . |
ReturnRef(variable) |
Return a reference to variable . |
ReturnRefOfCopy(value) |
Return a reference to a copy of value ; the copy lives as long as the action. |
2. 副作用(Side Effects)
Assign(&variable, value) | Assign value to variable. |
---|---|
DeleteArg<N>() |
Delete the N -th (0-based) argument, which must be a pointer. |
SaveArg<N>(pointer) |
Save the N -th (0-based) argument to *pointer . |
SaveArgPointee<N>(pointer) |
Save the value pointed to by the N -th (0-based) argument to *pointer . |
SetArgReferee<N>(value) |
Assign value to the variable referenced by the N -th (0-based) argument. |
SetArgPointee<N>(value) |
Assign value to the variable pointed by the N -th (0-based) argument. |
SetArgumentPointee<N>(value) |
Same as SetArgPointee<N>(value) . Deprecated. Will be removed in v1.7.0. |
SetArrayArgument<N>(first, last) |
Copies the elements in source range [first , last ) to the array pointed to by the N -th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range. |
SetErrnoAndReturn(error, value) |
Set errno to error and return value . |
Throw(exception) |
Throws the given exception, which can be any copyable value. Available since v1.1.0. |
3. 使用函数或函子作为动作Using a Function or a Functor as an Action
Invoke(f) | Invoke f with the arguments passed to the mock function, where f can be a global/static function or a functor. |
---|---|
Invoke(object_pointer, &class::method) |
Invoke the {method on the object with the arguments passed to the mock function. |
InvokeWithoutArgs(f) |
Invoke f , which can be a global/static function or a functor. f must take no arguments. |
InvokeWithoutArgs(object_pointer, &class::method) |
Invoke the method on the object, which takes no arguments. |
InvokeArgument<N>(arg1, arg2, ..., argk) |
Invoke the mock function's N -th (0-based) argument, which must be a function or a functor, with the k arguments. |
被调用函数的返回值被用作动作的返回值。
定义要与Invoke *()一起使用的函数或函数时,可以将任何未使用的参数声明为未使用:
double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } ... EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
在Invoke Argument <N>(...)中,如果一个参数需要通过引用传递,则将其包装在ByRef()中。 例如,
InvokeArgument<2>(5, string("Hi"), ByRef(foo))
调用模拟函数#2参数,通过值传递给它5和字符串(“Hi”),并通过引用传递foo。
Default Action
DoDefault() | Do the default action (specified by ON_CALL() or the built-in one). |
---|
Note: due to technical reasons, DoDefault()
cannot be used inside a composite action - trying to do so will result in a run-time error.
Composite Actions
DoAll(a1, a2, ..., an) | Do all actions a1 to an and return the result of an in each invocation. The first n - 1 sub-actions must return void. |
---|---|
IgnoreResult(a) |
Perform action a and ignore its result. a must not return void. |
WithArg<N>(a) |
Pass the N -th (0-based) argument of the mock function to action a and perform it. |
WithArgs<N1, N2, ..., Nk>(a) |
Pass the selected (0-based) arguments of the mock function to action a and perform it. |
WithoutArgs(a) |
Perform action a without any arguments. |
Defining Actions
ACTION(Sum) { return arg0 + arg1; } | Defines an action Sum() to return the sum of the mock function's argument #0 and #1. |
---|---|
ACTION_P(Plus, n) { return arg0 + n; } |
Defines an action Plus(n) to return the sum of the mock function's argument #0 and n . |
ACTION_Pk(Foo, p1, ..., pk) { statements; } |
Defines a parameterized action Foo(p1, ..., pk) to execute the given statements . |
The ACTION*
macros cannot be used inside a function or class.
七、Cardinalities基数
这些在Times()中用于指定将调用模拟函数的次数:
AnyNumber() | The function can be called any number of times. |
---|---|
AtLeast(n) |
The call is expected at least n times. |
AtMost(n) |
The call is expected at most n times. |
Between(m, n) |
The call is expected between m and n (inclusive) times. |
Exactly(n) or n |
The call is expected exactly n times. In particular, the call should never happen when n is 0. |
八、期望顺序(Expectation Order)
默认情况下,期望可以按任何顺序匹配。如果一些或所有期望必须在给定的顺序中匹配,则有两种方式来指定它们。 它们可以单独使用或一起使用。
1.The After Clause
using ::testing::Expectation; ... Expectation init_x = EXPECT_CALL(foo, InitX()); Expectation init_y = EXPECT_CALL(foo, InitY()); EXPECT_CALL(foo, Bar()) .After(init_x, init_y);
上边说,只有在InitX()和InitY()被调用之后才能调用Bar()。
如果你不知道你写的期望有多少个前提条件,你可以使用ExpectationSet来收集它们:
using ::testing::ExpectationSet; ... ExpectationSet all_inits; for (int i = 0; i < element_count; i++) { all_inits += EXPECT_CALL(foo, InitElement(i)); } EXPECT_CALL(foo, Bar()) .After(all_inits);
上面说,只有在所有元素都被初始化之后才能调用Bar()。(但我们不关心哪些元素在其他元素之前被初始化)。
在 .After(all_inits)?中使用ExpectationSet之后再修改ExpectationSet不会影响.After()的含义。
2. 序列
当你有一个长链的顺序期望,使用序列指定顺序更容易,这不需要给链中的每个期望一个不同的名称。同一序列中的所有预期调用必须按其指定的顺序发生。
using ::testing::Sequence; Sequence s1, s2; ... EXPECT_CALL(foo, Reset()) .InSequence(s1, s2) .WillOnce(Return(true)); EXPECT_CALL(foo, GetSize()) .InSequence(s1) .WillOnce(Return(1)); EXPECT_CALL(foo, Describe(A<const char*>())) .InSequence(s2) .WillOnce(Return("dummy"));
上边说,Reset()必须在GetSize()和Describe()之前调用,后两个可以以任何顺序发生。
在一个序列中方便地提出许多期望:
using ::testing::InSequence; { InSequence dummy; EXPECT_CALL(...)...; EXPECT_CALL(...)...; ... EXPECT_CALL(...)...; }
上边说,在dummy范围内的所有预期调用必须以严格的顺序发生。 名称dummy是不相关的。)
九、验证和重置模拟
Google Mock会在模拟对象被破坏时验证对模拟对象的期望,或者您可以更早地执行:
using ::testing::Mock; ... // Verifies and removes the expectations on mock_obj; // returns true iff successful. Mock::VerifyAndClearExpectations(&mock_obj); ... // Verifies and removes the expectations on mock_obj; // also removes the default actions set by ON_CALL(); // returns true iff successful. Mock::VerifyAndClear(&mock_obj);
您还可以告诉Google Mock模拟对象可以泄漏,无需进行验证:
Mock::AllowLeak(&mock_obj);
十、模拟类
Google Mock定义了一个方便的模拟类模板
class MockFunction<R(A1, ..., An)> { public: MOCK_METHODn(Call, R(A1, ..., An)); };
---恢复内容结束---
CheatSheet文档中包含了GMock所有常用的东西,看了这个基本上就可以用它了,本文翻译自:https://github.com/google/googletest/blob/master/googlemock/docs/CheatSheet.md
一、定义一个模拟类
1. 模拟一个正常的类,就是接口类
给:
class Foo { ... virtual ~Foo(); virtual int GetSize() const = 0; virtual string Describe(const char* name) = 0; virtual string Describe(int type) = 0; virtual bool Process(Bar elem, int count) = 0; };
(note that ~Foo()
must be virtual) we can define its mock as,定义模拟类。
#include "gmock/gmock.h" class MockFoo : public Foo { MOCK_CONST_METHOD0(GetSize, int()); MOCK_METHOD1(Describe, string(const char* name)); MOCK_METHOD1(Describe, string(int type)); MOCK_METHOD2(Process, bool(Bar elem, int count)); };
创建一个“nice”模拟对象忽略所有无趣的调用,或一个“strict”模拟对象,将它们视为失败:
NiceMock<MockFoo> nice_foo; // The type is a subclass of MockFoo. StrictMock<MockFoo> strict_foo; // The type is a subclass of MockFoo.
2. 模拟一个类模板
To mock:
template <typename Elem> class StackInterface { public: ... virtual ~StackInterface(); virtual int GetSize() const = 0; virtual void Push(const Elem& x) = 0; };
(note that ~StackInterface()
must be virtual) just append _T
to the MOCK_*
macros:
template <typename Elem> class MockStack : public StackInterface<Elem> { public: ... MOCK_CONST_METHOD0_T(GetSize, int()); MOCK_METHOD1_T(Push, void(const Elem& x)); };
3. 指定模拟函数的调用约定
如果您的mock函数不使用默认调用约定,您可以通过将_WITH_CALLTYPE附加到前两个部分中描述的任何宏并指定调用约定作为宏的第一个参数来指定它。例如,
MOCK_METHOD_1_WITH_CALLTYPE(STDMETHODCALLTYPE,Foo,bool(int n)); MOCK_CONST_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE,Bar,int(double x,double y));?
其中STDMETHODCALLTYPE由Windows上的<objbase.h>定义。
二、在测试中使用模拟器
典型的流程是:
- 导入您需要使用的Google Mock名称。所有Google Mock名称都位于测试命名空间中,除非它们是宏或其他注释。
- 创建模拟对象。
- (可选)设置模拟对象的默认操作。
- 设置你对模拟对象的期望(他们怎么叫?他们做什么?)。
- 使用模拟对象的练习代码;如有必要,请使用Google Test断言检查结果。
- 当模拟对象被破坏时,Google Mock会自动验证所有对其的期望是否满足。
这里是一个例子:
using ::testing::Return; // #1 TEST(BarTest, DoesThis) { MockFoo foo; // #2 ON_CALL(foo, GetSize()) // #3 .WillByDefault(Return(1)); // ... other default actions ... EXPECT_CALL(foo, Describe(5)) // #4 .Times(3) .WillRepeatedly(Return("Category 5")); // ... other expectations ... EXPECT_EQ("good", MyProductionFunction(&foo)); // #5 } // #6
三、设置默认操作
Google Mock对任何返回void,bool,数值或指针的函数都有一个内置的默认动作。
要为全局返回类型T的函数自定义默认操作:
using ::testing::DefaultValue; // Sets the default value to be returned. T must be CopyConstructible. DefaultValue<T>::Set(value); // Sets a factory. Will be invoked on demand. T must be MoveConstructible. // T MakeT(); DefaultValue<T>::SetFactory(&MakeT); // ... use the mocks ... // Resets the default value. DefaultValue<T>::Clear();
要自定义特定方法的默认操作,请使用ON_CALL():
ON_CALL(mock_object, method(matchers)) .With(multi_argument_matcher) ? .WillByDefault(action);
四、设置期望
EXPECT_CALL()在模拟方法上设置期望(如何调用它?它会做什么?):
EXPECT_CALL(mock_object, method(matchers)) .With(multi_argument_matcher) ? .Times(cardinality) ? .InSequence(sequences) * .After(expectations) * .WillOnce(action) * .WillRepeatedly(action) ? .RetiresOnSaturation(); ?
如果省略Times(),则基数假定为:
- Times(1)当没有WillOnce()和WillRepeatedly();
- 当有n个WillOnce()但没有WillRepeatedly()时,Times(n),其中n> = 1; 要么
- 当有n个WillOnce()和WillRepeatedly(),其中n> = 0时,Times(AtLeast(n))。
没有EXPECT_CALL()的方法可以被任意调用多次,并且每次都将采取默认操作。
五、匹配
匹配器匹配单个参数。 您可以在ON_CALL()或EXPECT_CALL()中使用它,或使用它直接验证值:
EXPECT_THAT(value, matcher) | Asserts that value matches matcher . |
---|---|
ASSERT_THAT(value, matcher) |
The same as EXPECT_THAT(value, matcher) , except that it generates a fatal failure. |
内置的匹配(其中参数是函数参数)分为几类:
1. 通配符
_ | argument can be any value of the correct type可以代表任意类型. |
---|---|
A<type>() or An<type>() |
argument can be any value of type可以是type类型的任意值 . |
2. 一般比较
Eq(value) 或者 value | argument == value,method中的形参必须是value |
Ge(value) | argument >= value,method中的形参必须大于等于value |
Gt(value) | argument > value |
Le(value) | argument <= value |
Lt(value) | argument < value |
Ne(value) | argument != value |
IsNull() | method的形参必须是NULL指针 |
NotNull() | argument is a non-null pointer |
Ref(variable) | 形参是variable的引用 |
TypedEq<type>(value) | 形参的类型必须是type类型,而且值必须是value。当模拟函数被重载你可能需要它而不是Eq(vlaue) |
注意** 除了Ref()之外,这些匹配器会创建一个值的副本,以备日后修改或销毁。 如果编译器抱怨该值没有公共副本构造函数,请尝试将其包装在ByRef()中,例如。 Eq(ByRef(non_copyable_value))。 如果你这样做,请确保non_copyable_value之后不改变,否则你的匹配器的含义将被改变。
3. 浮点数的比较
DoubleEq(a_double) | 形参是一个double类型,比如值近似于a_double,两个NaN是不相等的 |
FloatEq(a_float) | 同上,只不过类型是float |
NanSensitiveDoubleEq(a_double) | 形参是一个double类型,比如值近似于a_double,两个NaN是相等的,这个是用户所希望的方式 |
NanSensitiveFloatEq(a_float) | 同上,只不过形参是float |
上述匹配器使用基于ULP的比较(与Google Test中使用的比较相同)。 它们根据期望值的绝对值自动选择合理的误差界限。 DoubleEq()和FloatEq()符合IEEE标准,这需要比较两个NaNs的相等性返回false。 NanSensitive *版本将两个NaNs相等,这通常是用户想要的。
DoubleNear(a_double, max_abs_error) | argument is a double value close to a_double (absolute error <= max_abs_error ), treating two NaNs as unequal. |
---|---|
FloatNear(a_float, max_abs_error) |
argument is a float value close to a_float (absolute error <= max_abs_error ), treating two NaNs as unequal. |
NanSensitiveDoubleNear(a_double, max_abs_error) |
argument is a double value close to a_double (absolute error <= max_abs_error ), treating two NaNs as equal. |
NanSensitiveFloatNear(a_float, max_abs_error) |
argument is a float value close to a_float (absolute error <= max_abs_error ), treating two NaNs as equal. |
4. String Matchers
这里的字符串即可以是C风格的字符串,也可以是C++风格的。
ContainsRegex(string) | 形参匹配给定的正则表达式 |
EndsWith(suffix) | 形参以suffix截尾 |
HasSubstr(string) | 形参有string这个子串 |
MatchesRegex(string) | 从第一个字符到最后一个字符都完全匹配给定的正则表达式. |
StartsWith(prefix) | 形参以prefix开始 |
StrCaseEq(string) | 参数等于string,并且忽略大小写 |
StrCaseNe(string) | 参数不是string,并且忽略大小写 |
StrEq(string) | 参数等于string |
StrNe(string) | 参数不等于string |
5. 容器的匹配
很多STL的容器的比较都支持==这样的操作,对于这样的容器可以使用上述的Eq(expected_container)来比较或者只是expect_container来完全匹配容器。但如果你想写得更为灵活,可以使用下面的这些容器匹配方法:
ContainerEq(container) | The same as Eq(container) except that the failure message also includes which elements are in one container but not the other. |
---|---|
Contains(e) |
argument contains an element that matches e , which can be either a value or a matcher. |
Each(e) |
argument is a container where every element matches e , which can be either a value or a matcher. |
ElementsAre(e0, e1, ..., en) |
argument has n + 1 elements, where the i-th element matches ei , which can be a value or a matcher. 0 to 10 arguments are allowed. |
ElementsAreArray({ e0, e1, ..., en }) , ElementsAreArray(array) , or ElementsAreArray(array, count) |
The same as ElementsAre() except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. |
IsEmpty() |
argument is an empty container (container.empty() ). |
Pointwise(m, container) |
argument contains the same number of elements as in container , and for all i, (the i-th element in argument , the i-th element in container ) match m , which is a matcher on 2-tuples. E.g. Pointwise(Le(), upper_bounds) verifies that each element in argument doesn't exceed the corresponding element in upper_bounds . See more detail below. |
SizeIs(m) |
argument is a container whose size matches m . E.g. SizeIs(2) or SizeIs(Lt(2)) . |
UnorderedElementsAre(e0, e1, ..., en) |
argument has n + 1 elements, and under some permutation each element matches an ei (for a different i ), which can be a value or a matcher. 0 to 10 arguments are allowed. |
UnorderedElementsAreArray({ e0, e1, ..., en }) , UnorderedElementsAreArray(array) , or UnorderedElementsAreArray(array, count) |
The same as UnorderedElementsAre() except that the expected element values/matchers come from an initializer list, STL-style container, or C-style array. |
WhenSorted(m) |
When argument is sorted using the < operator, it matches container matcher m . E.g. WhenSorted(UnorderedElementsAre(1, 2, 3)) verifies that argument contains elements 1 , 2 , and 3 , ignoring order. |
WhenSortedBy(comparator, m) |
The same as WhenSorted(m) , except that the given comparator instead of < is used to sort argument . E.g. WhenSortedBy(std::greater<int>(), ElementsAre(3, 2, 1)) . |
注意:
-
这些匹配器也可以匹配:
i. 通过引用传递的本地数组(例如在Foo(const int(&a)[5])中)和
ii. 作为指针和计数传递的数组(例如,在Bar(const T * buffer,int len) - 参见 Multi-argument Matchers)。
-
匹配的数组可以是多维的(即其元素可以是数组)。
-
在Pointwise(m,...)中的m应该是:: testing :: tuple <T,U>的匹配器,其中T和U分别是实际容器和预期容器的元素类型。 例如,要比较两个Foo容器,其中Foo不支持operator ==但是有一个Equals()方法,可以写:
using ::testing::get; MATCHER(FooEq, "") { return get<0>(arg).Equals(get<1>(arg)); } ... EXPECT_THAT(actual_foos, Pointwise(FooEq(), expected_foos));
6. 成员匹配器
Field(&class::field, m) | argument.field (or argument->field when argument is a plain pointer) matches matcher m , where argument is an object of type class. |
---|---|
Key(e) |
argument.first matches e , which can be either a value or a matcher. E.g. Contains(Key(Le(5))) can verify that a map contains a key <= 5 . |
Pair(m1, m2) |
argument is an std::pair whose first field matches m1 and second field matches m2 . |
Property(&class::property, m) |
argument.property() (or argument->property() when argument is a plain pointer) matches matcher m , where argument is an object of type class. |
7. 匹配函数或函数的结果
ResultOf(f, m) | f(argument) matches matcher m , where f is a function or functor. |
---|
8. 指针匹配
|
|
---|---|
|
when |
9. 多参数匹配器
从技术上讲,完全匹配器匹配单个值。 “多参数”匹配器只是匹配元组的匹配器。 以下匹配器可用于匹配元组(x,y):
Eq() | x == y |
---|---|
Ge() |
x >= y |
Gt() |
x > y |
Le() |
x <= y |
Lt() |
x < y |
Ne() |
x != y |
您可以使用以下选择器来选择参数的子集(或对其重新排序)以参与匹配:
AllArgs(m) | Equivalent to m . Useful as syntactic sugar in .With(AllArgs(m)) . |
---|---|
Args<N1, N2, ..., Nk>(m) |
The tuple of the k selected (using 0-based indices) arguments matches m , e.g. Args<1, 2>(Eq()) . |
10. 复合匹配
你可以从一个或多个其他匹配器做一个匹配器:
AllOf(m1, m2, ..., mn) | argument matches all of the matchers m1 to mn . |
---|---|
AnyOf(m1, m2, ..., mn) |
argument matches at least one of the matchers m1 to mn . |
Not(m) |
argument doesn't match matcher m . |
11. Adapters for Matchers
MatcherCast<T>(m) | casts matcher m to type Matcher<T> . |
---|---|
SafeMatcherCast<T>(m) |
safely casts matcher m to type Matcher<T> . |
Truly(predicate) |
predicate(argument) returns something considered by C++ to be true, where predicate is a function or functor. |
12 .匹配作为谓词(Matchers as Predicates)
Matches(m)(value) | evaluates to true if value matches m . You can use Matches(m) alone as a unary functor. |
---|---|
ExplainMatchResult(m, value, result_listener) |
evaluates to true if value matches m , explaining the result to result_listener . |
Value(value, m) |
evaluates to true if value matches m . |
13. 定义匹配
MATCHER(IsEven, "") { return (arg % 2) == 0; } | Defines a matcher IsEven() to match an even number. |
---|---|
MATCHER_P(IsDivisibleBy, n, "") { *result_listener << "where the remainder is " << (arg % n); return (arg % n) == 0; } |
Defines a macher IsDivisibleBy(n) to match a number divisible by n . |
MATCHER_P2(IsBetween, a, b, std::string(negation ? "isn't" : "is") + " between " + PrintToString(a) + " and " + PrintToString(b)) { return a <= arg && arg <= b; } |
Defines a matcher IsBetween(a, b) to match a value in the range [a , b ]. |
笔记:
- MATCHER *宏不能在函数或类中使用。
- 匹配器主体必须是纯功能性的(即它不能有任何副作用,并且结果必须不依赖于被匹配的值和匹配器参数之外的任何东西)。
- 您可以使用PrintToString(x)将任何类型的值x转换为字符串。
14. 匹配作为测试断言
ASSERT_THAT(expression, m) | Generates a fatal failure if the value of expression doesn't match matcher m . |
---|---|
EXPECT_THAT(expression, m) |
Generates a non-fatal failure if the value of expression doesn't match matcher m . |
六、动作Actions
操作指定了mock函数在调用时应该执行的操作。
1. 返回值
Return() | Return from a void mock function. |
---|---|
Return(value) |
Return value . If the type of value is different to the mock function's return type, value is converted to the latter type at the time the expectation is set, not when the action is executed. |
ReturnArg<N>() |
Return the N -th (0-based) argument. |
ReturnNew<T>(a1, ..., ak) |
Return new T(a1, ..., ak) ; a different object is created each time. |
ReturnNull() |
Return a null pointer. |
ReturnPointee(ptr) |
Return the value pointed to by ptr . |
ReturnRef(variable) |
Return a reference to variable . |
ReturnRefOfCopy(value) |
Return a reference to a copy of value ; the copy lives as long as the action. |
2. 副作用(Side Effects)
Assign(&variable, value) | Assign value to variable. |
---|---|
DeleteArg<N>() |
Delete the N -th (0-based) argument, which must be a pointer. |
SaveArg<N>(pointer) |
Save the N -th (0-based) argument to *pointer . |
SaveArgPointee<N>(pointer) |
Save the value pointed to by the N -th (0-based) argument to *pointer . |
SetArgReferee<N>(value) |
Assign value to the variable referenced by the N -th (0-based) argument. |
SetArgPointee<N>(value) |
Assign value to the variable pointed by the N -th (0-based) argument. |
SetArgumentPointee<N>(value) |
Same as SetArgPointee<N>(value) . Deprecated. Will be removed in v1.7.0. |
SetArrayArgument<N>(first, last) |
Copies the elements in source range [first , last ) to the array pointed to by the N -th (0-based) argument, which can be either a pointer or an iterator. The action does not take ownership of the elements in the source range. |
SetErrnoAndReturn(error, value) |
Set errno to error and return value . |
Throw(exception) |
Throws the given exception, which can be any copyable value. Available since v1.1.0. |
3. 使用函数或函子作为动作Using a Function or a Functor as an Action
Invoke(f) | Invoke f with the arguments passed to the mock function, where f can be a global/static function or a functor. |
---|---|
Invoke(object_pointer, &class::method) |
Invoke the {method on the object with the arguments passed to the mock function. |
InvokeWithoutArgs(f) |
Invoke f , which can be a global/static function or a functor. f must take no arguments. |
InvokeWithoutArgs(object_pointer, &class::method) |
Invoke the method on the object, which takes no arguments. |
InvokeArgument<N>(arg1, arg2, ..., argk) |
Invoke the mock function's N -th (0-based) argument, which must be a function or a functor, with the k arguments. |
被调用函数的返回值被用作动作的返回值。
定义要与Invoke *()一起使用的函数或函数时,可以将任何未使用的参数声明为未使用:
double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } ... EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
在Invoke Argument <N>(...)中,如果一个参数需要通过引用传递,则将其包装在ByRef()中。 例如,
InvokeArgument<2>(5, string("Hi"), ByRef(foo))
调用模拟函数#2参数,通过值传递给它5和字符串(“Hi”),并通过引用传递foo。
Default Action
DoDefault() | Do the default action (specified by ON_CALL() or the built-in one). |
---|
Note: due to technical reasons, DoDefault()
cannot be used inside a composite action - trying to do so will result in a run-time error.
Composite Actions
DoAll(a1, a2, ..., an) | Do all actions a1 to an and return the result of an in each invocation. The first n - 1 sub-actions must return void. |
---|---|
IgnoreResult(a) |
Perform action a and ignore its result. a must not return void. |
WithArg<N>(a) |
Pass the N -th (0-based) argument of the mock function to action a and perform it. |
WithArgs<N1, N2, ..., Nk>(a) |
Pass the selected (0-based) arguments of the mock function to action a and perform it. |
WithoutArgs(a) |
Perform action a without any arguments. |
Defining Actions
ACTION(Sum) { return arg0 + arg1; } | Defines an action Sum() to return the sum of the mock function's argument #0 and #1. |
---|---|
ACTION_P(Plus, n) { return arg0 + n; } |
Defines an action Plus(n) to return the sum of the mock function's argument #0 and n . |
ACTION_Pk(Foo, p1, ..., pk) { statements; } |
Defines a parameterized action Foo(p1, ..., pk) to execute the given statements . |
The ACTION*
macros cannot be used inside a function or class.
七、Cardinalities基数
这些在Times()中用于指定将调用模拟函数的次数:
AnyNumber() | The function can be called any number of times. |
---|---|
AtLeast(n) |
The call is expected at least n times. |
AtMost(n) |
The call is expected at most n times. |
Between(m, n) |
The call is expected between m and n (inclusive) times. |
Exactly(n) or n |
The call is expected exactly n times. In particular, the call should never happen when n is 0. |
八、期望顺序(Expectation Order)
默认情况下,期望可以按任何顺序匹配。如果一些或所有期望必须在给定的顺序中匹配,则有两种方式来指定它们。 它们可以单独使用或一起使用。
1.The After Clause
using ::testing::Expectation; ... Expectation init_x = EXPECT_CALL(foo, InitX()); Expectation init_y = EXPECT_CALL(foo, InitY()); EXPECT_CALL(foo, Bar()) .After(init_x, init_y);
上边说,只有在InitX()和InitY()被调用之后才能调用Bar()。
如果你不知道你写的期望有多少个前提条件,你可以使用ExpectationSet来收集它们:
using ::testing::ExpectationSet; ... ExpectationSet all_inits; for (int i = 0; i < element_count; i++) { all_inits += EXPECT_CALL(foo, InitElement(i)); } EXPECT_CALL(foo, Bar()) .After(all_inits);
上面说,只有在所有元素都被初始化之后才能调用Bar()。(但我们不关心哪些元素在其他元素之前被初始化)。
在 .After(all_inits)?中使用ExpectationSet之后再修改ExpectationSet不会影响.After()的含义。
2. 序列
当你有一个长链的顺序期望,使用序列指定顺序更容易,这不需要给链中的每个期望一个不同的名称。同一序列中的所有预期调用必须按其指定的顺序发生。
using ::testing::Sequence; Sequence s1, s2; ... EXPECT_CALL(foo, Reset()) .InSequence(s1, s2) .WillOnce(Return(true)); EXPECT_CALL(foo, GetSize()) .InSequence(s1) .WillOnce(Return(1)); EXPECT_CALL(foo, Describe(A<const char*>())) .InSequence(s2) .WillOnce(Return("dummy"));
上边说,Reset()必须在GetSize()和Describe()之前调用,后两个可以以任何顺序发生。
在一个序列中方便地提出许多期望:
using ::testing::InSequence; { InSequence dummy; EXPECT_CALL(...)...; EXPECT_CALL(...)...; ... EXPECT_CALL(...)...; }
上边说,在dummy范围内的所有预期调用必须以严格的顺序发生。 名称dummy是不相关的。)
九、验证和重置模拟
Google Mock会在模拟对象被破坏时验证对模拟对象的期望,或者您可以更早地执行:
using ::testing::Mock; ... // Verifies and removes the expectations on mock_obj; // returns true iff successful. Mock::VerifyAndClearExpectations(&mock_obj); ... // Verifies and removes the expectations on mock_obj; // also removes the default actions set by ON_CALL(); // returns true iff successful. Mock::VerifyAndClear(&mock_obj);
您还可以告诉Google Mock模拟对象可以泄漏,无需进行验证:
Mock::AllowLeak(&mock_obj);
十、模拟类
Google Mock定义了一个方便的模拟类模板
class MockFunction<R(A1, ..., An)> { public: MOCK_METHODn(Call, R(A1, ..., An)); };
See this recipe for one application of it.
十 一、Flags
--gmock_catch_leaked_mocks=0 | Don't report leaked mock objects as failures. |
---|---|
--gmock_verbose=LEVEL |
Sets the default verbosity level (info , warning , or error ) of Google Mock messages. |
十二、一个关于匹配器的例子
举一个测试重载函数的匹配器的例子吧,感觉这个挺麻烦的,另外google提供了很多例子,不知道怎么写的时候,可以去里边找。
//接口类 class Foo { public: virtual bool Transform() = 0; // Overloaded on the types and/or numbers of arguments. virtual int Add(char x) = 0; virtual int Add(int x,int y) = 0; virtual int Add(int times, char x) = 0; protected: virtual void Resume() = 0; private: virtual int GetTimeOut() = 0; };
#include "Foo.h" #include "gmock\gmock.h" //模拟类 //子类修改父类的访问权限 class MockFoo : public Foo { public: MOCK_METHOD0(Transform, bool()); // The following must be in the public section, even though the // methods are protected or private in the base class. MOCK_METHOD0(Resume, void()); MOCK_METHOD0(GetTimeOut, int()); //virtual int Add(char x); //virtual int Add(int times, char x); // virtual int Add(int x); MOCK_METHOD1(Add, int(char x)); //MOCK_METHOD1(Add, int(int x)); MOCK_METHOD2(Add, int(int times, char x)); // virtual int Add(int x,int y) = 0; MOCK_METHOD2(Add, int(int x, int y)); };
测试用例:
#include "stdafx.h" using namespace std; using ::testing::Return; using ::testing::_; using ::testing::An; using ::testing::Matcher; using ::testing::Lt; using ::testing::TypedEq; using ::testing::Matches; using ::testing::Le; using ::testing::Ne; using ::testing::AllOf; //测试模拟private、protected方法: TEST(TestMockPrivate, TestPrivate) { MockFoo foo; //GetTimeOut是private修饰的 EXPECT_CALL(foo, GetTimeOut()) .WillOnce(Return(1)); cout << "test private GetTimeOut:" << foo.GetTimeOut() << endl; //1 } //测试重载方法 TEST(TestMockOverload, TestOverload) { MockFoo foo; EXPECT_CALL(foo, Add(_)) .Times(1) .WillOnce(Return(1)); cout << "test TestOverload Add:" << foo.Add('c') << endl; //1 } //测试数量相同,类型不同的情况。 // virtual int Add(int x,int y) = 0; // virtual int Add(int times, char x) = 0; TEST(TestMockOverload, TestSameNumArg) { MockFoo foo; //两个都是int EXPECT_CALL(foo, Add(An<int>(), An<int>())) .Times(1) .WillOnce(Return(8)); int c = foo.Add(3, 5); cout << "test TestOverload Add:" <<c<< endl; //8 EXPECT_CALL(foo, Add(Matcher<int>(Lt(10)), TypedEq<char>('c'))) .Times(1) .WillOnce(Return(7)); c = foo.Add(2, 'c'); cout << "test TestOverload Add:" << c << endl; //7 } //测试数量相同,类型不同的情况。 TEST(TestMockOverload, TestSameNumArg2) { MockFoo foo; EXPECT_CALL(foo, Add(Matcher<int>(Lt(5)), An<int>())) .Times(1) .WillOnce(Return(7)); int c = foo.Add(2, 5); cout << "test TestOverload Add:" << c << endl; //7 //第一个参数小于5,,第二个参数是'd' EXPECT_CALL(foo, Add(Matcher<int>(Lt(5)), Matcher<char>('d'))) .Times(1) .WillOnce(Return(10)); c = foo.Add(2, 'd'); //10 cout << "test TestOverload Add:" << c << endl; //10 } int main(int argc, char** argv) { ::testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); }
运行结果:可以看到都成功啦
ok。。结束。。
转载请注明出处:http://www.cnblogs.com/jycboy/p/gmock_cheatsheet.html
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:enote笔记语言(1)
下一篇:C++学习笔记-内存管理与指针
- C++ 转换函数搭配友元函数 2020-06-10
- C++ 自动转换和强制类型转换(用户自定义类类型) 2020-06-10
- C++ rand函数 2020-06-10
- C++ 友元函数 2020-06-10
- C++ 运算符重载 2020-06-10
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash