Big Ben

一个半吊子的编码爱好者

0%

Edit

在之前学习Andrew NG的Coursera课程《Machine Learning》的时候,针对Neural Network做过一篇笔记:Machine Learning (2) - Neural Network。当时通过学习知道了如何计算2层(1个隐藏层)Neural Network的偏导数问题。但是理解只是局限于一堆公式,不够深刻。这次在学习Andrew NG的Deep Learning课程时,Andrew在介绍深层网络之前也介绍了浅层网络的基本原理,包括导数公式的推导。我现在终于理解了这些公式是如何来的了。对比了之前Machine Learning的课件和笔记,我认为此次课程中使用的符号标记方式更容易理解和推导。
在这一篇笔记中,我会主要记录浅层神经网络的forward propagation & backward propagation计算方法。

Forward Propagation

Backward Propagation

在神经网络的学习中,我们往往能听到这个词汇。听起来很玄乎,其实他并不神秘。
Backward Propagation,中文翻译为反向传播。它是用来计算导数的。下图为Logistic Regression的计算图:

上式也是我们在求导数时最常用到的链式法则(chain rule)。若



则有下图:

纠错

  • 上图应为
  • 应为

先看Andrew给出的计算结果:

sigmoid函数的导数为:
而其他的隐藏层采用为其激活函数,即有可能是tanh或者ReLU,也有可能是sigmoid。

分步求导

损失函数

其导数为:
代入:

代入,可得:

代入,可得:

这个就很简单了

至此全部得证。

矩阵求导

在计算的时候,会发现这里有一个矩阵求导的问题。是列向量,型矩阵,是列向量。
网上搜到的矩阵求导方法的帖子有很多,但都没有很好的解释为什么
根据我自己的理解,可以看作个列向量。关于的导数就是对所有列向量的导数的stack。
因为

所以
stack到一块儿就是
所以得证。

Vectorization

Forward propagation




Backward propagation






np.sum(A, axis=1, keepdims=True) 求取矩阵A各行的和
axis: =1 按行求和,=0 按列求和,没有axis则全矩阵求和
keepdims: =True 求和出来后仍然保存矩阵结构,即按行求和,则得到列向量,按列求和得到行向量,全矩阵求和得到一个1x1矩阵。

分析

先看single training example:

训练样本按照下面的方法stack,也类似可得如下:

为什么?
最直观的b应该是,因为
那为什么要按照行求和再按m求均值呢?因为为模型参数,与样本无关。针对每个样本b, db应当相同。所以要按上面这么算。

总结

在浅层神经网络中,正向传播求节点值,反向传播利用链式法则求导数。在向量化的时候,针对所有的样本,向量按照列组合成矩阵,即矩阵的一列对应一个样本。

%23%20Deep%20Learning%20%281%29%20-%20Neural%20Network%20with%201%20Hidden%20Layer%0A@%28myblog%29%5Bdeep%20learning%2C%20neural%20network%5D%0A%0A%u5728%u4E4B%u524D%u5B66%u4E60Andrew%20NG%u7684Coursera%u8BFE%u7A0B%u300AMachine%20Learning%u300B%u7684%u65F6%u5019%uFF0C%u9488%u5BF9Neural%20Network%u505A%u8FC7%u4E00%u7BC7%u7B14%u8BB0%uFF1A%5BMachine%20Learning%20%282%29%20-%20Neural%20Network%5D%28https%3A//zhougy0717.github.io/2017/04/03/Machine%2520Learning%2520%282%29%2520-%2520Neural%2520Network/%29%u3002%u5F53%u65F6%u901A%u8FC7%u5B66%u4E60%u77E5%u9053%u4E86%u5982%u4F55%u8BA1%u7B972%u5C42%uFF081%u4E2A%u9690%u85CF%u5C42%uFF09Neural%20Network%u7684%u504F%u5BFC%u6570%u95EE%u9898%u3002%u4F46%u662F%u7406%u89E3%u53EA%u662F%u5C40%u9650%u4E8E%u4E00%u5806%u516C%u5F0F%uFF0C%u4E0D%u591F%u6DF1%u523B%u3002%u8FD9%u6B21%u5728%u5B66%u4E60Andrew%20NG%u7684Deep%20Learning%u8BFE%u7A0B%u65F6%uFF0CAndrew%u5728%u4ECB%u7ECD%u6DF1%u5C42%u7F51%u7EDC%u4E4B%u524D%u4E5F%u4ECB%u7ECD%u4E86%u6D45%u5C42%u7F51%u7EDC%u7684%u57FA%u672C%u539F%u7406%uFF0C%u5305%u62EC%u5BFC%u6570%u516C%u5F0F%u7684%u63A8%u5BFC%u3002%u6211%u73B0%u5728%u7EC8%u4E8E%u7406%u89E3%u4E86%u8FD9%u4E9B%u516C%u5F0F%u662F%u5982%u4F55%u6765%u7684%u4E86%u3002%u5BF9%u6BD4%u4E86%u4E4B%u524DMachine%20Learning%u7684%u8BFE%u4EF6%u548C%u7B14%u8BB0%uFF0C%u6211%u8BA4%u4E3A%u6B64%u6B21%u8BFE%u7A0B%u4E2D%u4F7F%u7528%u7684%u7B26%u53F7%u6807%u8BB0%u65B9%u5F0F%u66F4%u5BB9%u6613%u7406%u89E3%u548C%u63A8%u5BFC%u3002%0A%u5728%u8FD9%u4E00%u7BC7%u7B14%u8BB0%u4E2D%uFF0C%u6211%u4F1A%u4E3B%u8981%u8BB0%u5F55%u6D45%u5C42%u795E%u7ECF%u7F51%u7EDC%u7684forward%20propagation%20%26%20backward%20propagation%u8BA1%u7B97%u65B9%u6CD5%u3002%0A%0A%23%23%20Forward%20Propagation%0A%21%5BAlt%20text%5D%28./1513332445189.png%29%0A%u524D%u5411%u4F20%u64AD%u8BA1%u7B97%u6BCF%u4E2A%u8282%u70B9%u7684%u503C%uFF0C%u540E%u9762%u7684%u53CD%u5411%u4F20%u64AD%u4E2D%u4F1A%u7528%u5230%u3002%0A%21%5BAlt%20text%5D%28./1513574653750.png%29%0A%23%23%20Backward%20Propagation%0A%u5728%u795E%u7ECF%u7F51%u7EDC%u7684%u5B66%u4E60%u4E2D%uFF0C%u6211%u4EEC%u5F80%u5F80%u80FD%u542C%u5230%u8FD9%u4E2A%u8BCD%u6C47%u3002%u542C%u8D77%u6765%u5F88%u7384%u4E4E%uFF0C%u5176%u5B9E%u4ED6%u5E76%u4E0D%u795E%u79D8%u3002%0ABackward%20Propagation%uFF0C%u4E2D%u6587%u7FFB%u8BD1%u4E3A%u53CD%u5411%u4F20%u64AD%u3002%u5B83%u662F%u7528%u6765%u8BA1%u7B97%u5BFC%u6570%u7684%u3002%u4E0B%u56FE%u4E3ALogistic%20Regression%u7684%u8BA1%u7B97%u56FE%uFF1A%0A%21%5BAlt%20text%7C500x0%5D%28./1513330613563.png%29%0A%u6309%u7167%u6C42%u5BFC%u516C%u5F0F%u53EF%u77E5%uFF1A%0A%24%24%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20%5Comega%7D%20%3D%20%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20a%7D%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20a%7D%7B%5Cpartial%20z%7D%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20z%7D%7B%5Cpartial%20%5Comega%7D%24%24%0A%u4E0A%u5F0F%u4E5F%u662F%u6211%u4EEC%u5728%u6C42%u5BFC%u6570%u65F6%u6700%u5E38%u7528%u5230%u7684%u94FE%u5F0F%u6CD5%u5219%28chain%20rule%29%u3002%u82E5%0A%24da%20%3D%20%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20a%7D%24%0A%24dz%20%3D%20%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20z%7D%20%3D%20da%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20a%7D%7B%5Cpartial%20z%7D%24%0A%24d%20%5Comega%20%3D%20%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20%5Comega%7D%20%3D%20dz%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20a%7D%7B%5Cpartial%20%5Comega%7D%24%0A%u5219%u6709%u4E0B%u56FE%uFF1A%0A%21%5BAlt%20text%7C550x0%5D%28./1513331905347.png%29%0A%u8FD9%u5C31%u662F%u6240%u8C13%u7684%u53CD%u5411%u4F20%u64AD%28Backward%20Propagation%29%u3002%u524D%u4E00%u5C42%u4E3A%u540E%u4E00%u5C42%u63D0%u4F9B%u8FDB%u4E00%u6B65%u8BA1%u7B97%u5BFC%u6570%u7684%u4FE1%u606F%u3002%0A%u63A8%u8FDB%u52302%u5C42%u795E%u7ECF%u7F51%u7EDC%uFF0C%u5C31%u8981%u591A%u7B97%u4E00%u5C42%uFF1A%0A%21%5BAlt%20text%5D%28./1513332172423.png%29%0A%0A%3E**%u7EA0%u9519**%0A-%20%u4E0A%u56FE%24z%5E%7B%5B2%5D%7D%24%u5E94%u4E3A%24%5Comega%5E%7B%5B2%5D%7Da%5E%7B%5B1%5D%7D%20+%20b%5E%7B%5B2%5D%7D%24%0A-%20%24a%5E%7B%5B1%5D%7D%24%u5E94%u4E3A%24g%5E%7B%5B1%5D%7D%28z%5E%7B%5B1%5D%7D%29%24%0A%0A%u5148%u770BAndrew%u7ED9%u51FA%u7684%u8BA1%u7B97%u7ED3%u679C%uFF1A%0A%21%5BAlt%20text%7C280x0%5D%28./1513332236320.png%29%0A%u5982%u679C%u76F4%u63A5%u81EA%u5DF1%u53BB%u7B97%u4F1A%u6709%u4E00%u4E9B%u7591%u95EE%u3002%u8981%u7B97%u51FA%u8FD9%u4E2A%u7ED3%u679C%uFF0C%u5176%u5B9E%u662F%u6709%u4E00%u4E9B%u524D%u63D0%u7684%u3002%0A%21%5BAlt%20text%5D%28./1513332445189.png%29%0A%u5728%u8F93%u51FA%u5C42%uFF0C%u5373%24a%5E%7B%5B2%5D%7D%24%u5C42%uFF0C%u91C7%u7528sigmoid%u6FC0%u6D3B%u51FD%u6570%uFF0C%u5373%0A%24%24a%5E%7B%5B2%5D%7D%20%3D%20%5Csigma%28z%5E%7B%5B2%5D%7D%29%20%3D%20%5Cdfrac%20%7B1%7D%7B1+e%5E%7B-z%7D%7D%24%24%0Asigmoid%u51FD%u6570%u7684%u5BFC%u6570%u4E3A%3A%0A%24%24%5Cdfrac%20%7Bda%7D%7Bdz%7D%20%3D%20%5Cdfrac%20%7Be%5E%7B-z%7D%7D%7B%281+e%5E%7B-z%7D%29%5E2%7D%20%3D%20%5Cdfrac%20%7B1%7D%7B1+e%5E%7B-z%7D%7D%20-%20%5Cdfrac%20%7B1%7D%7B%281+e%5E%7B-z%7D%29%5E2%7D%20%3D%20a%281-a%29%24%24%0A%u800C%u5176%u4ED6%u7684%u9690%u85CF%u5C42%u91C7%u7528%24g%5E%7B%5Bi%5D%7D%28%29%24%u4E3A%u5176%u6FC0%u6D3B%u51FD%u6570%uFF0C%u5373%u6709%u53EF%u80FD%u662Ftanh%u6216%u8005ReLU%uFF0C%u4E5F%u6709%u53EF%u80FD%u662Fsigmoid%u3002%0A%0A%23%23%23%20%u5206%u6B65%u6C42%u5BFC%0A%23%23%23%20%24dz%5E%7B%5B2%5D%7D%24%0A%u635F%u5931%u51FD%u6570%0A%24%24L%20%3D%20-%28ylog%20a%5E%7B%5B2%5D%7D%20+%20%281-y%29log%20%281-a%5E%7B%5B2%5D%7D%29%29%24%24%0A%u5176%u5BFC%u6570%u4E3A%uFF1A%0A%24%24%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20a%7D%20%3D%20-%5Cdfrac%20%7By%7D%7Ba%5E%7B%5B2%5D%7D%7D%20-%20%5Cdfrac%20%7B1-y%7D%7B1-a%5E%7B%5B2%5D%7D%7D%24%24%0A%u4EE3%u5165%uFF1A%0A%24%24dz%5E%7B%5B2%5D%7D%20%3D%20%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20a%7D%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20a%7D%7B%5Cpartial%20z%7D%20%3D%20a%5E%7B%5B2%5D%7D%281-a%5E%7B%5B2%5D%7D%29%28-%5Cdfrac%20%7By%7D%7Ba%5E%7B%5B2%5D%7D%7D%20-%20%5Cdfrac%20%7B1-y%7D%7B1-a%5E%7B%5B2%5D%7D%7D%29%20%3D%20a%5E%7B%5B2%5D%7D%20-%20y%24%24%0A%0A%0A%0A%23%23%23%23%20%24d%5Comega%5E%7B%5B2%5D%7D%20%2C%20b%5E%7B%5B2%5D%7D%24%0A%24%24d%5Comega%5E%7B%5B2%5D%7D%20%3D%20%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20z%5E%7B%5B2%5D%7D%7D%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20z%5E%7B%5B2%5D%7D%7D%7B%5Cpartial%20%5Comega%5E%7B%5B2%5D%7D%7D%20%3D%20dz%5E%7B%5B2%5D%7Da%5E%7B%5B1%5DT%7D%24%24%0A%24%24db%5E%7B%5B2%5D%7D%20%3D%20%5Cdfrac%20%7B%5Cpartial%20L%7D%7B%5Cpartial%20z%5E%7B%5B2%5D%7D%7D%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20z%5E%7B%5B2%5D%7D%7D%7B%5Cpartial%20b%5E%7B%5B2%5D%7D%7D%20%3D%20dz%5E%7B%5B2%5D%7D%24%24%0A%0A%23%23%23%23%20%24da%5E%7B%5B1%5D%7D%24%0A%24%24da%5E%7B%5B1%5D%7D%20%3D%20dz%5E%7B%5B2%5D%7D%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20z%5E%7B%5B2%5D%7D%7D%7B%5Cpartial%20a%5E%7B%5B1%5D%7D%7D%24%24%0A%u4EE3%u5165%24%5Cdfrac%20%7B%5Cpartial%20z%5E%7B%5B2%5D%7D%7D%7B%5Cpartial%20a%5E%7B%5B1%5D%7D%7D%20%3D%20%5Comega%5E%7B%5B2%5DT%7D%24%uFF0C%u53EF%u5F97%uFF1A%0A%24%24da%5E%7B%5B1%5D%7D%20%3D%20%5Comega%5E%7B%5B2%5DT%7D%20dz%5E%7B%5B2%5D%7D%24%24%0A%0A%0A%23%23%23%23%20%24dz%5E%7B%5B1%5D%7D%24%0A%24%24dz%5E%7B%5B1%5D%7D%20%3D%20da%5E%7B%5B1%5D%7D%20%5Ccenterdot%20%5Cdfrac%20%7B%5Cpartial%20a%7D%7B%5Cpartial%20z%5E%7B%5B1%5D%7D%7D%24%24%0A%u4EE3%u5165%24%5Cdfrac%20%7B%5Cpartial%20a%7D%7B%5Cpartial%20z%5E%7B%5B1%5D%7D%7D%20%3D%20g%5E%7B%5B1%5D%7D%5Cprime%20%28z%5E%7B%5B1%5D%7D%29%24%uFF0C%u53EF%u5F97%uFF1A%0A%24%24dz%5E%7B%5B1%5D%7D%20%3D%20%5Comega%5E%7B%5B2%5DT%7D%20dz%5E%7B%5B2%5D%7D%20*g%5E%7B%5B1%5D%7D%5Cprime%20%28z%5E%7B%5B1%5D%7D%29%24%24%0A%0A%23%23%23%23%20%24d%5Comega%5E%7B%5B1%5D%7D%20%2C%20db%5E%7B%5B1%5D%7D%24%0A%u8FD9%u4E2A%u5C31%u5F88%u7B80%u5355%u4E86%0A%24%24d%5Comega%5E%7B%5B1%5D%7D%20%3D%20dz%5E%7B%5B1%5D%7Dx%24%24%0A%24%24db%5E%7B%5B1%5D%7D%20%3D%20dz%5E%7B%5B1%5D%7D%24%24%0A%0A%u81F3%u6B64%u5168%u90E8%u5F97%u8BC1%u3002%0A%0A%23%23%23%20%u77E9%u9635%u6C42%u5BFC%0A%u5728%u8BA1%u7B97%24da%5E%7B%5B1%5D%7D%24%u7684%u65F6%u5019%uFF0C%u4F1A%u53D1%u73B0%u8FD9%u91CC%u6709%u4E00%u4E2A%u77E9%u9635%u6C42%u5BFC%u7684%u95EE%u9898%u3002%24z%5E%7B%5B2%5D%7D%24%u662F%u5217%u5411%u91CF%uFF0C%24%5Comega%20%5E%7B%5B2%5D%7D%24%u662F%24n%5E%7B%5B2%5D%7D%5Ctimes%20n%5E%7B%5B1%5D%7D%24%u578B%u77E9%u9635%uFF0C%24a%5E%7B%5B1%5D%7D%24%u662F%u5217%u5411%u91CF%u3002%0A%u7F51%u4E0A%u641C%u5230%u7684%u77E9%u9635%u6C42%u5BFC%u65B9%u6CD5%u7684%u5E16%u5B50%u6709%u5F88%u591A%uFF0C%u4F46%u90FD%u6CA1%u6709%u5F88%u597D%u7684%u89E3%u91CA%u4E3A%u4EC0%u4E48%24%5Cdfrac%20%7B%5Cpartial%20z%5E%7B%5B2%5D%7D%7D%7B%5Cpartial%20%5Comega%5E%7B%5B2%5D%7D%7D%20%3D%20a%5E%7B%5B1%5DT%7D%24%u3002%0A%u6839%u636E%u6211%u81EA%u5DF1%u7684%u7406%u89E3%uFF0C%24%5Comega%20%5E%7B%5B2%5D%7D%24%u53EF%u4EE5%u770B%u4F5C%24n%5E%7B%5B1%5D%7D%24%u4E2A%u5217%u5411%u91CF%u3002%24z%5E%7B%5B2%5D%7D%24%u5173%u4E8E%24%5Comega%5E%7B%5B2%5D%7D%24%u7684%u5BFC%u6570%u5C31%u662F%u5BF9%u6240%u6709%u5217%u5411%u91CF%u7684%u5BFC%u6570%u7684stack%u3002%0A%u56E0%u4E3A%0A%24%24z_%7B%28i%29%7D%20%3D%20%5CSigma_%7Bj%3D0%7D%5E%7Bn%5E%7B%5B1%5D%7D-1%7D%20%5Comega_%7B%28i%29%28j%29%7Da_%7B%28j%29%7D%24%24%0A%u6240%u4EE5%0A%24%24%5Cdfrac%20%7B%5Cpartial%20z%5E%7B%5B2%5D%7D%7D%7B%5Cpartial%20%5Comega_%7B%28j%29%7D%7D%20%3D%20a_%7B%28j%29%7D%24%24%0Astack%u5230%u4E00%u5757%u513F%u5C31%u662F%24%5Ba_%7B%280%29%7D%2C%20a_%7B%281%29%7D...a_%7B%28n-1%29%7D%5D%20%3D%20a%5ET%24%0A%u6240%u4EE5%u5F97%u8BC1%u3002%20%0A%0A%23%23%23%20Vectorization%0A%0A%0A%0A%0A%23%23%23%23%20Forward%20propagation%0A%24Z%5E%7B%5B1%5D%7D%20%3D%20W%5E%7B%5B1%5D%7DX%20+%20b%5E%7B%5B1%5D%7D%24%0A%24A%5E%7B%5B1%5D%7D%20%3D%20g%5E%7B%5B1%5D%7D%28Z%5E%7B%5B1%5D%7D%29%24%0A%24Z%5E%7B%5B2%5D%7D%20%3D%20W%5E%7B%5B2%5D%7DA%5E%7B%5B1%5D%7D%20+%20b%5E%7B%5B2%5D%7D%24%0A%24A%5E%7B%5B2%5D%7D%20%3D%20g%5E%7B%5B2%5D%7D%28Z%5E%7B%5B2%5D%7D%29%20%3D%20%5Csigma%28Z%5E%7B%5B2%5D%7D%29%24%0A%23%23%23%23%20Backward%20propagation%0A%24dZ%5E%7B%5B2%5D%7D%20%3D%20A%5E%7B%5B2%5D%7D%20-%20Y%24%0A%24dW%5E%7B%5B2%5D%7D%20%3D%20%5Cdfrac%20%7B1%7D%7Bm%7D%20dZ%5E%7B%5B2%5D%7DA%5E%7B%5B1%5DT%7D%24%0A%24db%5E%7B%5B2%5D%7D%20%3D%20%5Cdfrac%20%7B1%7D%7Bm%7D%20np.sum%28dZ%5E%7B%5B2%5D%7D%2C%20axis%3D1%2C%20keepdims%3DTrue%29%24%0A%24dZ%5E%7B%5B1%5D%7D%20%3D%20W%5E%7B%5B2%5DT%7DdZ%5E%7B%5B2%5D%7D%20*%20g%5E%7B%5B1%5D%5Cprime%7D%28Z%5E%7B%5B1%5D%7D%29%24%0A%24dW%5E%7B%5B1%5D%7D%20%3D%20%5Cdfrac%20%7B1%7D%7Bm%7D%20dZ%5E%7B%5B1%5D%7DX%5ET%24%0A%24db%5E%7B%5B1%5D%7D%20%3D%20%5Cdfrac%20%7B1%7D%7Bm%7D%20np.sum%28dZ%5E%7B%5B1%5D%7D%2C%20axis%3D1%2C%20keepdims%3DTrue%29%24%0A%0A%3E%20**np.sum**%28A%2C%20axis%3D1%2C%20keepdims%3DTrue%29%20%u6C42%u53D6%u77E9%u9635A%u5404%u884C%u7684%u548C%0A%3E%20**axis**%3A%20%3D1%20%u6309%u884C%u6C42%u548C%uFF0C%3D0%20%u6309%u5217%u6C42%u548C%uFF0C%u6CA1%u6709axis%u5219%u5168%u77E9%u9635%u6C42%u548C%0A%3E%20**keepdims**%3A%20%3DTrue%20%u6C42%u548C%u51FA%u6765%u540E%u4ECD%u7136%u4FDD%u5B58%u77E9%u9635%u7ED3%u6784%uFF0C%u5373%u6309%u884C%u6C42%u548C%uFF0C%u5219%u5F97%u5230%u5217%u5411%u91CF%uFF0C%u6309%u5217%u6C42%u548C%u5F97%u5230%u884C%u5411%u91CF%uFF0C%u5168%u77E9%u9635%u6C42%u548C%u5F97%u5230%u4E00%u4E2A1x1%u77E9%u9635%u3002%0A%0A%23%23%23%23%20%u5206%u6790%0A%u5148%u770Bsingle%20training%20example%3A%20%0A%21%5BAlt%20text%5D%28./1513575221043.png%29%0A%u518D%u770Bvectorized%u7248%u672C%uFF1A%0A%21%5BAlt%20text%5D%28./1513577070931.png%29%0A%0A%u8BAD%u7EC3%u6837%u672C%u6309%u7167%u4E0B%u9762%u7684%u65B9%u6CD5stack%uFF0C%24A%5E%7B%5B1%5D%7D%24%u4E5F%u7C7B%u4F3C%u53EF%u5F97%u5982%u4E0B%uFF1A%0A%21%5BAlt%20text%7C250x0%5D%28./1513576811182.png%29%0A%u5176%u4F59%u77E9%u9635%24Z%5E%7B%5Bi%5D%7D%2C%20A%5E%7B%5Bi%5D%7D%2C%20W%5E%7B%5Bi%5D%7D%2C%20dZ%5E%7B%5Bi%5D%7D%2C%20dW%5E%7B%5Bi%5D%7D%24%u4E5F%u53EF%u7C7B%u4F3C%u5F97%u5230%u3002%0A%u7A0D%u7A0D%u9700%u8981%u6CE8%u610F%u7684%u662F%u5411%u91CF%24b%5E%7B%5Bi%5D%7D%24%uFF0CAndrew%u5728%u8BFE%u7A0B%u4E2D%u4E5F%u6CA1%u6709%u8FC7%u591A%u7684%u63D0%u5230%u3002%u8FD9%u91CC%u5199%u51FA%u6211%u7684%u7406%u89E3%u3002%0A%u9996%u5148%uFF1A%0A%24%24Z%5E%7B%5Bi%5D%7D%20%3D%20W%5E%7B%5Bi%5D%7DA%5E%7B%5Bi-1%5D%7D%20+%20b%5E%7B%5Bi%5D%7D%24%24%0A%u4E3A%u4EC0%u4E48%24b%5E%7B%5Bi%5D%7D%24%u662F%24db%5E%7B%5Bi%5D%7D%20%3D%20%5Cdfrac%20%7B1%7D%7Bm%7D%20np.sum%28dZ%5E%7B%5Bi%5D%7D%2C%20axis%3D1%2C%20keepdims%3DTrue%29%24%3F%0A%u6700%u76F4%u89C2%u7684b%u5E94%u8BE5%u662F%24b%5E%7B%5Bi%5D%7D%2C%20db%5E%7B%5Bi%5D%7D%20%5Cin%20%28n%5E%7B%5Bi-1%5D%7D%2C%20m%29%24%uFF0C%u56E0%u4E3A%24A%5E%7B%5Bi-1%5D%7D%20%5Cin%20%28n%5E%7B%5Bi-1%5D%7D%2C%20m%29%uFF0C%20W%5E%7B%5Bi%5D%7D%20%5Cin%20%28n%5E%7B%5Bi%5D%7D%2C%20n%5E%7B%5Bi-1%5D%7D%29%2C%20Z%5E%7B%5Bi%5D%7D%20%5Cin%20%28n%5E%7B%5Bi%5D%7D%2Cm%29%24%0A%u90A3%u4E3A%u4EC0%u4E48%u8981%u6309%u7167%u884C%u6C42%u548C%u518D%u6309m%u6C42%u5747%u503C%u5462%uFF1F%u56E0%u4E3A%24W%5E%7B%5Bi%5D%7D%2C%20b%5E%7B%5Bi%5D%7D%24%u4E3A%u6A21%u578B%u53C2%u6570%uFF0C%u4E0E%u6837%u672C%u65E0%u5173%u3002%u9488%u5BF9%u6BCF%u4E2A%u6837%u672Cb%2C%20db%u5E94%u5F53%u76F8%u540C%u3002%u6240%u4EE5%u8981%u6309%u4E0A%u9762%u8FD9%u4E48%u7B97%u3002%0A%0A%23%23%20%u603B%u7ED3%0A%u5728%u6D45%u5C42%u795E%u7ECF%u7F51%u7EDC%u4E2D%uFF0C%u6B63%u5411%u4F20%u64AD%u6C42%u8282%u70B9%u503C%uFF0C%u53CD%u5411%u4F20%u64AD%u5229%u7528%u94FE%u5F0F%u6CD5%u5219%u6C42%u5BFC%u6570%u3002%u5728%u5411%u91CF%u5316%u7684%u65F6%u5019%uFF0C%u9488%u5BF9%u6240%u6709%u7684%u6837%u672C%uFF0C%u5411%u91CF%u6309%u7167%u5217%u7EC4%u5408%u6210%u77E9%u9635%uFF0C%u5373%u77E9%u9635%u7684%u4E00%u5217%u5BF9%u5E94%u4E00%u4E2A%u6837%u672C%u3002%0A%0A%0A%0A%0A%0A

Edit

service

在AngularJS中service是单例的,所以通常用service在controller之间,甚至模块之间来共享数据。

angular.module('app', [])
.controller('ctrl', Ctrl)
.service('CustomService', CustomService);
function CustomService(){
const service = this
service.a = "test"
}
Ctrl.$inject = ['CustomService']
function Ctrl(CustomService){
console.log(CustomService.a)
}

前面一个字符串是这个service的名字,后面一个是这个service的构造函数。可以如上在controller中使用。

factory

工厂函数,可以用来生成任意想要的东西,当其返回一个service实例时,和.service作用是相同的,如下

ShoppingListController2.$inject = ['ShoppingListFactory'];
function ShoppingListController2(ShoppingListFactory) {
var list2 = this;
// Use factory to create new shopping list service
var shoppingList = ShoppingListFactory(3);
...
}
function ShoppingListFactory() {
var factory = function (maxItems) {
return new ShoppingListService(maxItems);
};
return factory;
}

provider

factory其实是一种特殊的provider。看Angular JS的实现:

function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
}

factory函数都是用provider来实现的。
一个provider函数通常如下定义:

function ServiceProvider() {
var provider = this;
provider.config = {…};
provider.$get = function () {
var service =
new Service(provider.config.prop);
return service;
};
}

provider.$get来提供factory功能。它还能提供其他的配置项。然后provider还有一个好处是,在module.config函数中,只能inject provider。.config是在module定义以后,先于其他服务,立即运行的。如下:

angular.module('app', [])
.controller('ctrl', Ctrl)
.provider('Service', ServiceProvider)
.config(Config);
Config.$inject = ['ServiceProvider'];
function Config(ServiceProvider) {
ServiceProvider.config.prop = 'value';
};

这样的话,在$get实例化的service就能根据模块的不同来定制不同的功能。

%23%20Angular%20JS%20-%20service%2C%20factory%20%26%20provider%0A@%28myblog%29%5Bangular%5D%0A%0A%0A%23%23%20service%0A%u5728AngularJS%u4E2Dservice%u662F%u5355%u4F8B%u7684%uFF0C%u6240%u4EE5%u901A%u5E38%u7528service%u5728controller%u4E4B%u95F4%uFF0C%u751A%u81F3%u6A21%u5757%u4E4B%u95F4%u6765%u5171%u4EAB%u6570%u636E%u3002%0A%60%60%60%0Aangular.module%28%27app%27%2C%20%5B%5D%29%0A%20%20.controller%28%27ctrl%27%2C%20Ctrl%29%0A%20%20.service%28%27CustomService%27%2C%20CustomService%29%3B%0A%0Afunction%20CustomService%28%29%7B%0A%09const%20service%20%3D%20this%0A%09service.a%20%3D%20%22test%22%0A%7D%0A%0ACtrl.%24inject%20%3D%20%5B%27CustomService%27%5D%0Afunction%20Ctrl%28CustomService%29%7B%0A%09console.log%28CustomService.a%29%0A%7D%0A%60%60%60%0A%u524D%u9762%u4E00%u4E2A%u5B57%u7B26%u4E32%u662F%u8FD9%u4E2Aservice%u7684%u540D%u5B57%uFF0C%u540E%u9762%u4E00%u4E2A%u662F%u8FD9%u4E2Aservice%u7684%u6784%u9020%u51FD%u6570%u3002%u53EF%u4EE5%u5982%u4E0A%u5728controller%u4E2D%u4F7F%u7528%u3002%0A%0A%23%23%20factory%0A%u5DE5%u5382%u51FD%u6570%uFF0C%u53EF%u4EE5%u7528%u6765%u751F%u6210%u4EFB%u610F%u60F3%u8981%u7684%u4E1C%u897F%uFF0C%u5F53%u5176%u8FD4%u56DE%u4E00%u4E2Aservice%u5B9E%u4F8B%u65F6%uFF0C%u548C.service%u4F5C%u7528%u662F%u76F8%u540C%u7684%uFF0C%u5982%u4E0B%0A%60%60%60%0AShoppingListController2.%24inject%20%3D%20%5B%27ShoppingListFactory%27%5D%3B%0Afunction%20ShoppingListController2%28ShoppingListFactory%29%20%7B%0A%20%20var%20list2%20%3D%20this%3B%0A%0A%20%20//%20Use%20factory%20to%20create%20new%20shopping%20list%20service%0A%20%20var%20shoppingList%20%3D%20ShoppingListFactory%283%29%3B%0A%20%20...%0A%7D%0A%0Afunction%20ShoppingListFactory%28%29%20%7B%0A%20%20var%20factory%20%3D%20function%20%28maxItems%29%20%7B%0A%20%20%20%20return%20new%20ShoppingListService%28maxItems%29%3B%0A%20%20%7D%3B%0A%0A%20%20return%20factory%3B%0A%7D%0A%60%60%60%0A%0A%23%23%20provider%0Afactory%u5176%u5B9E%u662F%u4E00%u79CD%u7279%u6B8A%u7684provider%u3002%u770BAngular%20JS%u7684%u5B9E%u73B0%uFF1A%0A%60%60%60%0Afunction%20factory%28name%2C%20factoryFn%2C%20enforce%29%20%7B%0A%20%20return%20provider%28name%2C%20%7B%0A%20%20%20%20%24get%3A%20enforce%20%21%3D%3D%20false%20%3F%20enforceReturnValue%28name%2C%20factoryFn%29%20%3A%20factoryFn%0A%20%20%7D%29%3B%0A%7D%0A%60%60%60%0Afactory%u51FD%u6570%u90FD%u662F%u7528provider%u6765%u5B9E%u73B0%u7684%u3002%0A%u4E00%u4E2Aprovider%u51FD%u6570%u901A%u5E38%u5982%u4E0B%u5B9A%u4E49%uFF1A%0A%60%60%60%0Afunction%20ServiceProvider%28%29%20%7B%0A%20%20var%20provider%20%3D%20this%3B%0A%20%20provider.config%20%3D%20%7B%u2026%7D%3B%0A%20%20provider.%24get%20%3D%20function%20%28%29%20%7B%0A%20%20%20%20var%20service%20%3D%0A%20%20%20%20%20%20new%20Service%28provider.config.prop%29%3B%0A%20%20%20%20return%20service%3B%0A%20%20%7D%3B%0A%7D%0A%60%60%60%0Aprovider.%5C%24get%u6765%u63D0%u4F9Bfactory%u529F%u80FD%u3002%u5B83%u8FD8%u80FD%u63D0%u4F9B%u5176%u4ED6%u7684%u914D%u7F6E%u9879%u3002%u7136%u540Eprovider%u8FD8%u6709%u4E00%u4E2A%u597D%u5904%u662F%uFF0C%u5728module.config%u51FD%u6570%u4E2D%uFF0C%u53EA%u80FDinject%20provider%u3002.config%u662F%u5728module%u5B9A%u4E49%u4EE5%u540E%uFF0C%u5148%u4E8E%u5176%u4ED6%u670D%u52A1%uFF0C%u7ACB%u5373%u8FD0%u884C%u7684%u3002%u5982%u4E0B%uFF1A%0A%60%60%60javascript%0Aangular.module%28%27app%27%2C%20%5B%5D%29%0A%20%20.controller%28%27ctrl%27%2C%20Ctrl%29%0A%20%20.provider%28%27Service%27%2C%20ServiceProvider%29%0A%20%20.config%28Config%29%3B%0AConfig.%24inject%20%3D%20%5B%27ServiceProvider%27%5D%3B%0Afunction%20Config%28ServiceProvider%29%20%7B%0A%20%20ServiceProvider.config.prop%20%3D%20%27value%27%3B%0A%7D%3B%0A%60%60%60%0A%u8FD9%u6837%u7684%u8BDD%uFF0C%u5728%5C%24get%u5B9E%u4F8B%u5316%u7684service%u5C31%u80FD%u6839%u636E%u6A21%u5757%u7684%u4E0D%u540C%u6765%u5B9A%u5236%u4E0D%u540C%u7684%u529F%u80FD%u3002

Edit


前端代码一直没有做unit testing,也不知道怎么做。在Yaakov Chaikin的Coursera课程中,第五章正好介绍了相关内容。所以就顺便在自己的项目中实践了一把。无数次的实践证明,看得头头是道,写起来就到处是坑。小小的内容,着实花了不少时间。

针对前端的代码,网上采用的测试框架大多是karma + jasmine + chai。因为我之前在后端代码的测试中多采用mocha + should + sinon。所以本次实践,仍然爱采用mocha框架,来搭配karma使用。

karma是一个自动化测试框架。因其丰富的配置选项,良好的扩展性能,被广泛应用,尤其是在前端。因为前端要针对各种版本的浏览器,各种框架,如果全部靠手动去适配,基本不太可能。所以大家都用karma了。

karma setup

基本配置参考前端单元测试之Karma环境搭建

配置文件简单如下:

module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: [...],
// list of files / patterns to load in the browser
files: [
'...',
{pattern: '...'}
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['...'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity,
// Which plugins to enable
plugins: []
})
}

简单的解释目前用到的选项:

  • frameworks: 使用到的框架,所有使用到的,要在下面plugins中列明。比如:frameworks中包含mocha,则plugins下就要包含karma-mocha
  • basePath, files, exclude: 这三个指明了karma要加载的JS文件
    • basePath:基础目录,会直接插入到files,exclude指定的路径前面
    • files:加载文件,支持通配符和正则式,通过大括号还可以添加配置选项。通配符’**’表示所有目录,’*’表示所有文件,’**/*’表示其下所有目录所有文件。
    • exclude: 可以剔除files中不想要的文件
  • browsers:使用的浏览器,如果想要headless测试则可用PhantomJS

karma with ES6

因为PhantomJS不支持ES6语法(网上说,PhantomJS甚至对ES5的支持都不是很好),所以karma要测试包含ES6语法的代码,还需要一番配置。网上相关的帖子很多,列举如下:

总的来说,有这么几种解决方案:

  • karma-es6-shim
  • karma-babel-preprocessor
  • webpack
  • SlimerJS
    前三种有点复杂,目前的我的技术栈还无法理解。最终选择最简单的SlimerJS。在Linux上可以通过Xvfb做到headless测试,Windows上就这样吧,跳个窗口也不是什么大事。

Debug karma test using Chrome

这个在初学的时候还是很重要的,如果没有这个工具,恐怕我也跳不出自己给自己挖的大坑。
参考链接:Debugging Karma Unit Tests
按照上面的步骤打开调试页面和Chrome Dev Tool页面,刷新页面即可重新加载代码并触发断点。
将命令行写入package.json,以后调用npm就可以方便的打开该调试工具。

{
...
"scripts": {
"start": "node ./bin/www",
"karma-debug": "cd public/js && karma start --browsers=Chrome --single-run=false --debug"
},
...
}

ngMock

ngMock中两个最重要的关键词就是:module, inject。可以参考这个人的博客:

ngMock通过module加载已经定义了的模块,或者定义匿名module并提供新的service,或者复写已存在的service。module只是将名字,以及对应位置存入一个数组。inject将真正的加载所有module提供的service,并解决依赖关系,还可以将相应的service导出到test case中使用。其实ngMock也就是做了这两件事情。
下面简单介绍一些有助于理解的知识点。

angular.mock.module

module函数的原型是angular.mock.module(alias, obj, func)
module接受三种参数类型,每一种都可以单独使用,用来定义或加载对应模块

  • alias: 是module名字字符串,可以是在待测代码中定义的,也可以是在测试文件中定义的
  • obj: 一个普通的字典结构,他会生成一个匿名module,其中的成员会被定义成$provide.value()
  • function: 同样是定义一个匿名module,不同的是,function可以用$provide作为参数,来定义各种各样的service。

function() vs Object

The examples we used were similar, so what’s the key difference between the function() and Object form? As the Object type gets added as a $provider.value, it is therefore restricted to the capabilities of the value service, most importantly that a value service cannot be injected with other services.

关于$provide,可以参考Angular官方文档

The $provide service has a number of methods for registering components with the $injector. Many of these functions are also exposed on angular.Module.

它提供了下面的这些service

provider(name, provider) - registers a service provider with the $injector
constant(name, obj) - registers a value/object that can be accessed by providers and services.
value(name, obj) - registers a value/object that can only be accessed by services, not providers.
factory(name, fn) - registers a service factory function that will be wrapped in a service provider object, whose $get property will contain the given factory function.
service(name, Fn) - registers a constructor function that will be wrapped in a service provider object, whose $get property will instantiate a new object using the given constructor function.
decorator(name, decorFn) - registers a decorator function that will be able to modify or replace the implementation of another service.

angular.mock.inject

$inject是ngMock的核心,看看官方的定义

The inject function wraps a function into an injectable function. The inject() creates new instance of $injector per test, which is then used for resolving references.

inject就是定义了一个injector,那injector又是什么

$injector is used to retrieve object instances as defined by provider, instantiate types, invoke methods, and load modules.

$injector也就是AngularJS的核心。它提供了所谓的“DI” (Dependency Injection)模式。Angular的核心代码通过$injector来获取对应service的reference。
我们通常将inject放在beforeEach里,这样inject就为每个测试定义一个injector,为这个injector准备好所有的service,并能通过参数将任意已有的service导出。

Test controllers

了解了上面的两个重要组件之后,针对不同的测试组件就容易理解了。
直接上代码:

describe('main controller', function() {
var $controller
var mainCtrl
beforeEach('set up module', function() {
module(function ($provide) {
$provide.service('MockDataService', function () {
const service = this
service.data = 'test data'
})
})
module('MainModule')
});
beforeEach('set up controller', inject(function (_$controller_, MockDataService) {
$controller = _$controller_
mainCtrl = $controller('MainController', {
DataService: MockDataService
})
}))
it('should assemble print page URL correctly', function() {
mainCtrl.getData().should.equal('test data')
});
});

这里只有两个值得注意的地方,其实前文也都有提及。

  1. 第一个module中通过$provide提供了一个假的service,这个service可以通过其名字被inject。
  2. inject中通过_$controller_来导出controller的构造函数。通过这个构造函数来构造相应的待测controller,并传入假的service达到mock的目的。

Test services

仍然直接上代码:

angular.module('PrinterData', [])
.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true);
}]);
describe('printer data service', function() {
var $httpBackend
var PrinterDataService
beforeEach(function(){
module(function ($provide) {
$provide.provider('$location', function () {
const provider = this
const location = {}
location.search = function (){
return {
id: 'test id',
}
}
provider.html5Mode = sinon.stub()
provider.$get = sinon.stub().returns(location)
})
})
});
beforeEach(function() {
module('Data')
inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend')
DataService = $injector.get('DataService')
})
});
it('should update if succeeds', function(done) {
$httpBackend.whenGET(/.*/)
.respond({
status: 'ready'
})
DataService.getStatus().then(function (data) {
data.should.deepEqual({
status: 'ready'
})
done()
}).catch(function (err) {
should.not.exist(err)
})
$httpBackend.flush();
});
})

下面罗列一些实际使用中碰到的一些问题,以及自己的一些理解。

使用$injector

我们说过inject函数就是用来创建$injector,所以$injector也是可以直接被导出使用的。这时需要什么service,就可以直接调用$injector.get。为什么不直接通过参数来inject呢?当然是因为参数太长了,不好看。。。

mock $http

为了测试稳定性,我们在unit test中并不真正的访问服务器,而通常采用假的服务器响应来mock。这就需要用到$httpBackend service。那么$http和$httpBackend之间的关系是什么?

HTTP backend used by the service that delegates to XMLHttpRequest object or JSONP and deals with browser incompatibilities.
You should never need to use this service directly, instead use the higher-level abstractions: $http or $resource.
During testing this implementation is swapped with mock $httpBackend which can be trained with responses.

具体做法,上面的代码中已有,节录如下:

beforeEach(function() {
module('Data')
inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend')
DataService = $injector.get('DataService')
})
});
it('should update if succeeds', function(done) {
$httpBackend.whenGET(/.*/)
.respond({
status: 'ready'
})
DataService.getStatus().then(function (data) {
data.should.deepEqual({
status: 'ready'
})
done()
}).catch(function (err) {
should.not.exist(err)
})
$httpBackend.flush();
});

通过$httpBackend.wheGET(...).respond(...)来设置mock response。whenGET还支持正则式。
调用完请求函数后要记得$httpBackend.flush()将请求真正的发出。否则请求并不会发出,直到调用flush()函数,或者测试超时失败。

如何mock $location & $locationProvider

很惭愧的说,在这个问题上我花了很久很久的时间,搜索了大量的google网页,最后还是依靠chrome追踪angular核心代码才发现原来是自己给自己挖的坑,对service, factory和provider理解不透彻。
首先来就事论事,说一下到底怎么完成我的这个测试。

因为我要测试DataService,而DataService定义在Data moduleDataService调用了$location service。需要将之mock掉。最简单的想法就是通过inject来获得$location service reference。并通过sinon.stub来设置需要的返回值。

inject(function ($injector, $location) {
sinon.stub($location, 'search').returns({
id: 'test id'
})
DataService = $injector.get('DataService')
})

但是我倒霉催的,竟然是用$provide做了:

module(function ($provide) {
$provide.service('$location', function () {
const location = this
location.search = sinon.stub().returns({
device_id: 'test id',
})
})
})

这样就会有问题了,当ngMock加载这个模块的时候,会先定义一个$locationProvider,这就会覆盖其自带的$locationProvider。而如下的module.config方法中对$locationProvider的操作就会出现问题:

angular.module('PrinterData', [])
.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true);
}]);

因为新定义的$locationProvider中没有html5Mode成员。于是想了各种办法来mock $locationProvider。结果因为对service,factory,provider的理解不正确,都失败了。放一段错误的代码:

beforeEach(function(){
module(function ($provide) {
$provide.service('$locationProvider', function () {
const location = this
location.html5Mode = function(){}
})
$provide.factory('$location', function () {
const locationProvider = this
locationProvider.html5Mode = function(){}
})
})

第一个用$provide.service会定义一个$locationProviderProvider
第二个用$provide.factory定义了一个$locationProvider,但是没有返回,所以$location service不会被实例化,也就没有$location service了。
如果想通过mock $locationProvider来mock $location service的话,正确的做法应当是:

$provide.provider('$location', function () {
const provider = this
const location = {}
location.search = function (){
return {
device_id: 'test id'
}
}
provider.html5Mode = sinon.stub()
provider.$get = sinon.stub().returns(location)
})

mock掉provider.$get函数,提供假的location服务。其实还有很多办法,例如获得$locationProvider再对$get函数进行stub,也可以。没有再一一尝试了。理解了错误和正确的做法,想再去实现就得心应手了。

测试.config/.run块

Data module有一个.config块,因为模块加载的时候会先运行其.config块,然后是.run块。因其特殊性,所以在对其中使用到的服务进行mock要注意顺序。可以参考这个链接:Testing config and run blocks in AngularJS
How to easily test an often neglected part of your application

如何mock $interval

在单元测试中,我们通常要采用mock的手段,而不真正的使用时间服务。在sinon中,可以使用fake timer来取代JS中标准的setTimeout和setInterval。不过这个在Angular JS中并不起作用,如果我们使用$interval服务的话。
因为在对Angular JS做单元测试的时候,$interval并不会调用setInterval。原因是这里的$interval其实是ngMock中的$interval。此时可以使用$interval.flush(ms)来将时钟推进若干时间。当然如果愿意,也可以用sinon来mock从$injector中get出来的$interval service。$timeout service也类似,有同样的方法对付单元测试。

Promise not resolve

在Angular JS中,我们往往调用$q service来使用Promise,而$q.defer().promise只在$scope的digest cycle才会判定resolve还是reject,所以要触发$q产生的promise,一定要调用对应的$scope.$apply()或者$scope.$digest()。stack overflow中也有大把类似问题的解答。具体也可以参考这篇博客,讲得很透彻:

在Angular JS中,也可以使用标准的Promise,例如: var promise = new Promise(resolve, reject)。但是貌似,标准的Promise和\q兼容的并不很好,下面的code并不会工作:

var promise = $q.all([promise1, promise2])
$scope.$apply()

promise并不会在promise1和promise2返回之后返回,实际上是他根本就不会返回,直到测试超时失败。

“Unexpected request: GET …/.html” on Karma tests

这是Yaakov在视频中提到的需要使用$templateCache的情形。Yaakov提到用karma是解决该问题最好的办法,但是因为视频并不涉及介绍karma,所以并没有说应该怎么做。
当我们的karma test加载了route.js,并在其中定义了component,则测试在实例化该Angular component的时候回去用$http service来GET该template html文件,于是产生了该unexpected request错误。Yaakov在教程中的解决办法是,通过一次真正的AJAX从服务器上请求该html,并将之存入$templateCache中,之后就不会再有真正的GET AJAX产生了。其实这一段可以通过配置karma来实现。具体做法如下:

  • 安装ng-html2js,karma-ng-html2js-preprocessor
  • 使用ng-html2js将对应的template html转成js模块,并在测试代码中引入。

看下面具体代码
karma.conf.js

// list of files / patterns to load in the browser
files: [
'../html/templates/*.html',
...
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'../html/templates/home.template.html': ['ng-html2js']
},
ngHtml2JsPreprocessor: {
moduleName: 'templates',
stripPrefix: '.*/',
prependPrefix: '/html/templates/'
},

test.js

describe(function(){
beforeEach(module('templates'))
})

特别要注意的是对ng-html2js的配置

ngHtml2JsPreprocessor: {
moduleName: 'templates',
stripPrefix: '.*/',
prependPrefix: '/html/templates/'
},

当没有stripPrefix选项时,通过Chrome debug可以看到,所有的template都被转化成了js模块,但是其模块名是类似c:\path\to\template\template.html的结构,而我在route中的指定路径是web路径templateUrl: '/html/templates/home.template.html'。就对不上号了。做法是通过stripPrefix将所有的前缀全部剥去,然后用prependPrefix加上需要的前缀。这样就可以工作了。网上有很多帖子讨论这个问题。列两个有启发性的:

%23%20Angular%20JS%20-%20unit%20test%0A@%28myblog%29%5Bangular%2C%20unittest%5D%0A%u524D%u7AEF%u4EE3%u7801%u4E00%u76F4%u6CA1%u6709%u505Aunit%20testing%uFF0C%u4E5F%u4E0D%u77E5%u9053%u600E%u4E48%u505A%u3002%u5728Yaakov%20Chaikin%u7684%5BCoursera%u8BFE%u7A0B%5D%28https%3A//www.coursera.org/learn/single-page-web-apps-with-angularjs/home/week/5%29%u4E2D%uFF0C%u7B2C%u4E94%u7AE0%u6B63%u597D%u4ECB%u7ECD%u4E86%u76F8%u5173%u5185%u5BB9%u3002%u6240%u4EE5%u5C31%u987A%u4FBF%u5728%u81EA%u5DF1%u7684%u9879%u76EE%u4E2D%u5B9E%u8DF5%u4E86%u4E00%u628A%u3002%u65E0%u6570%u6B21%u7684%u5B9E%u8DF5%u8BC1%u660E%uFF0C%u770B%u5F97%u5934%u5934%u662F%u9053%uFF0C%u5199%u8D77%u6765%u5C31%u5230%u5904%u662F%u5751%u3002%u5C0F%u5C0F%u7684%u5185%u5BB9%uFF0C%u7740%u5B9E%u82B1%u4E86%u4E0D%u5C11%u65F6%u95F4%u3002%0A%0A%u9488%u5BF9%u524D%u7AEF%u7684%u4EE3%u7801%uFF0C%u7F51%u4E0A%u91C7%u7528%u7684%u6D4B%u8BD5%u6846%u67B6%u5927%u591A%u662Fkarma%20+%20jasmine%20+%20chai%u3002%u56E0%u4E3A%u6211%u4E4B%u524D%u5728%u540E%u7AEF%u4EE3%u7801%u7684%u6D4B%u8BD5%u4E2D%u591A%u91C7%u7528mocha%20+%20should%20+%20sinon%u3002%u6240%u4EE5%u672C%u6B21%u5B9E%u8DF5%uFF0C%u4ECD%u7136%u7231%u91C7%u7528mocha%u6846%u67B6%uFF0C%u6765%u642D%u914Dkarma%u4F7F%u7528%u3002%0A%0Akarma%u662F%u4E00%u4E2A%u81EA%u52A8%u5316%u6D4B%u8BD5%u6846%u67B6%u3002%u56E0%u5176%u4E30%u5BCC%u7684%u914D%u7F6E%u9009%u9879%uFF0C%u826F%u597D%u7684%u6269%u5C55%u6027%u80FD%uFF0C%u88AB%u5E7F%u6CDB%u5E94%u7528%uFF0C%u5C24%u5176%u662F%u5728%u524D%u7AEF%u3002%u56E0%u4E3A%u524D%u7AEF%u8981%u9488%u5BF9%u5404%u79CD%u7248%u672C%u7684%u6D4F%u89C8%u5668%uFF0C%u5404%u79CD%u6846%u67B6%uFF0C%u5982%u679C%u5168%u90E8%u9760%u624B%u52A8%u53BB%u9002%u914D%uFF0C%u57FA%u672C%u4E0D%u592A%u53EF%u80FD%u3002%u6240%u4EE5%u5927%u5BB6%u90FD%u7528karma%u4E86%u3002%0A%23%23%20karma%20setup%0A%u57FA%u672C%u914D%u7F6E%u53C2%u8003%5B%u524D%u7AEF%u5355%u5143%u6D4B%u8BD5%u4E4BKarma%u73AF%u5883%u642D%u5EFA%5D%28https%3A//segmentfault.com/a/1190000006895064%29%0A%0A%u914D%u7F6E%u6587%u4EF6%u7B80%u5355%u5982%u4E0B%uFF1A%0A%60%60%60javascript%0Amodule.exports%20%3D%20function%28config%29%20%7B%0A%20%20config.set%28%7B%0A%0A%20%20%20%20//%20base%20path%20that%20will%20be%20used%20to%20resolve%20all%20patterns%20%28eg.%20files%2C%20exclude%29%0A%20%20%20%20basePath%3A%20%27%27%2C%0A%0A%0A%20%20%20%20//%20frameworks%20to%20use%0A%20%20%20%20//%20available%20frameworks%3A%20https%3A//npmjs.org/browse/keyword/karma-adapter%0A%20%20%20%20frameworks%3A%20%5B...%5D%2C%0A%0A%0A%20%20%20%20//%20list%20of%20files%20/%20patterns%20to%20load%20in%20the%20browser%0A%20%20%20%20files%3A%20%5B%0A%20%20%20%20%20%20%27...%27%2C%0A%20%20%20%20%20%20%7Bpattern%3A%20%27...%27%7D%0A%20%20%20%20%5D%2C%0A%0A%0A%20%20%20%20//%20list%20of%20files%20to%20exclude%0A%20%20%20%20exclude%3A%20%5B%0A%20%20%20%20%5D%2C%0A%0A%0A%20%20%20%20//%20preprocess%20matching%20files%20before%20serving%20them%20to%20the%20browser%0A%20%20%20%20//%20available%20preprocessors%3A%20https%3A//npmjs.org/browse/keyword/karma-preprocessor%0A%20%20%20%20preprocessors%3A%20%7B%0A%20%20%20%20%7D%2C%0A%0A%0A%20%20%20%20//%20test%20results%20reporter%20to%20use%0A%20%20%20%20//%20possible%20values%3A%20%27dots%27%2C%20%27progress%27%0A%20%20%20%20//%20available%20reporters%3A%20https%3A//npmjs.org/browse/keyword/karma-reporter%0A%20%20%20%20reporters%3A%20%5B%27progress%27%5D%2C%0A%0A%0A%20%20%20%20//%20web%20server%20port%0A%20%20%20%20port%3A%209876%2C%0A%0A%0A%20%20%20%20//%20enable%20/%20disable%20colors%20in%20the%20output%20%28reporters%20and%20logs%29%0A%20%20%20%20colors%3A%20true%2C%0A%0A%0A%20%20%20%20//%20level%20of%20logging%0A%20%20%20%20//%20possible%20values%3A%20config.LOG_DISABLE%20%7C%7C%20config.LOG_ERROR%20%7C%7C%20config.LOG_WARN%20%7C%7C%20config.LOG_INFO%20%7C%7C%20config.LOG_DEBUG%0A%20%20%20%20logLevel%3A%20config.LOG_INFO%2C%0A%0A%0A%20%20%20%20//%20enable%20/%20disable%20watching%20file%20and%20executing%20tests%20whenever%20any%20file%20changes%0A%20%20%20%20autoWatch%3A%20true%2C%0A%0A%0A%20%20%20%20//%20start%20these%20browsers%0A%20%20%20%20//%20available%20browser%20launchers%3A%20https%3A//npmjs.org/browse/keyword/karma-launcher%0A%20%20%20%20browsers%3A%20%5B%27...%27%5D%2C%0A%0A%0A%20%20%20%20//%20Continuous%20Integration%20mode%0A%20%20%20%20//%20if%20true%2C%20Karma%20captures%20browsers%2C%20runs%20the%20tests%20and%20exits%0A%20%20%20%20singleRun%3A%20false%2C%0A%0A%20%20%20%20//%20Concurrency%20level%0A%20%20%20%20//%20how%20many%20browser%20should%20be%20started%20simultaneous%0A%20%20%20%20concurrency%3A%20Infinity%2C%0A%0A%20%20%20%20//%20Which%20plugins%20to%20enable%0A%20%20%20%20plugins%3A%20%5B%5D%0A%20%20%7D%29%0A%7D%20%0A%60%60%60%0A%0A%u7B80%u5355%u7684%u89E3%u91CA%u76EE%u524D%u7528%u5230%u7684%u9009%u9879%uFF1A%0A-%20frameworks%3A%20%u4F7F%u7528%u5230%u7684%u6846%u67B6%uFF0C%u6240%u6709%u4F7F%u7528%u5230%u7684%uFF0C%u8981%u5728%u4E0B%u9762plugins%u4E2D%u5217%u660E%u3002%u6BD4%u5982%uFF1Aframeworks%u4E2D%u5305%u542Bmocha%uFF0C%u5219plugins%u4E0B%u5C31%u8981%u5305%u542Bkarma-mocha%0A-%20basePath%2C%20files%2C%20exclude%3A%20%u8FD9%u4E09%u4E2A%u6307%u660E%u4E86karma%u8981%u52A0%u8F7D%u7684JS%u6587%u4EF6%0A%09-%20basePath%uFF1A%u57FA%u7840%u76EE%u5F55%uFF0C%u4F1A%u76F4%u63A5%u63D2%u5165%u5230files%uFF0Cexclude%u6307%u5B9A%u7684%u8DEF%u5F84%u524D%u9762%0A%09-%20files%uFF1A%u52A0%u8F7D%u6587%u4EF6%uFF0C%u652F%u6301%u901A%u914D%u7B26%u548C%u6B63%u5219%u5F0F%uFF0C%u901A%u8FC7%u5927%u62EC%u53F7%u8FD8%u53EF%u4EE5%u6DFB%u52A0%u914D%u7F6E%u9009%u9879%u3002%u901A%u914D%u7B26%27%5C*%5C*%27%u8868%u793A%u6240%u6709%u76EE%u5F55%uFF0C%27%5C*%27%u8868%u793A%u6240%u6709%u6587%u4EF6%uFF0C%27%5C*%5C*/*%27%u8868%u793A%u5176%u4E0B%u6240%u6709%u76EE%u5F55%u6240%u6709%u6587%u4EF6%u3002%0A%09-%20exclude%3A%20%u53EF%u4EE5%u5254%u9664files%u4E2D%u4E0D%u60F3%u8981%u7684%u6587%u4EF6%0A-%20browsers%uFF1A%u4F7F%u7528%u7684%u6D4F%u89C8%u5668%uFF0C%u5982%u679C%u60F3%u8981headless%u6D4B%u8BD5%u5219%u53EF%u7528PhantomJS%0A%0A%23%23%23%20karma%20with%20ES6%0A%u56E0%u4E3APhantomJS%u4E0D%u652F%u6301ES6%u8BED%u6CD5%28%u7F51%u4E0A%u8BF4%uFF0CPhantomJS%u751A%u81F3%u5BF9ES5%u7684%u652F%u6301%u90FD%u4E0D%u662F%u5F88%u597D%29%uFF0C%u6240%u4EE5karma%u8981%u6D4B%u8BD5%u5305%u542BES6%u8BED%u6CD5%u7684%u4EE3%u7801%uFF0C%u8FD8%u9700%u8981%u4E00%u756A%u914D%u7F6E%u3002%u7F51%u4E0A%u76F8%u5173%u7684%u5E16%u5B50%u5F88%u591A%uFF0C%u5217%u4E3E%u5982%u4E0B%uFF1A%0A-%20%5BTesting%20ES6%20with%20Karma%2C%20RequireJS%2C%20and%20Angular%5D%28http%3A//radify.io/blog/announcing-karma-es6-shim/%29%20%20-%20karma-es6-shim%20%0A-%20%5BHow%20to%20Start%20Writing%20Your%20AngularJS%20Tests%20in%20ES6%5D%28https%3A//www.barbarianmeetscoding.com/blog/2015/11/16/how-to-start-writing-your-angular-js-tests-in-es6/%29%20-%20karma-babel-preprocessor%0A-%20%5BWriting%20Jasmine%20Unit%20Tests%20In%20ES6%5D%28http%3A//www.syntaxsuccess.com/viewarticle/writing-jasmine-unit-tests-in-es6%29%20-%20webpack%0A%0A%u603B%u7684%u6765%u8BF4%uFF0C%u6709%u8FD9%u4E48%u51E0%u79CD%u89E3%u51B3%u65B9%u6848%uFF1A%0A-%20karma-es6-shim%0A-%20karma-babel-preprocessor%0A-%20webpack%0A-%20SlimerJS%0A%u524D%u4E09%u79CD%u6709%u70B9%u590D%u6742%uFF0C%u76EE%u524D%u7684%u6211%u7684%u6280%u672F%u6808%u8FD8%u65E0%u6CD5%u7406%u89E3%u3002%u6700%u7EC8%u9009%u62E9%u6700%u7B80%u5355%u7684SlimerJS%u3002%u5728Linux%u4E0A%u53EF%u4EE5%u901A%u8FC7Xvfb%u505A%u5230headless%u6D4B%u8BD5%uFF0CWindows%u4E0A%u5C31%u8FD9%u6837%u5427%uFF0C%u8DF3%u4E2A%u7A97%u53E3%u4E5F%u4E0D%u662F%u4EC0%u4E48%u5927%u4E8B%u3002%0A%0A%23%23%23%20Debug%20karma%20test%20using%20Chrome%0A%u8FD9%u4E2A%u5728%u521D%u5B66%u7684%u65F6%u5019%u8FD8%u662F%u5F88%u91CD%u8981%u7684%uFF0C%u5982%u679C%u6CA1%u6709%u8FD9%u4E2A%u5DE5%u5177%uFF0C%u6050%u6015%u6211%u4E5F%u8DF3%u4E0D%u51FA%u81EA%u5DF1%u7ED9%u81EA%u5DF1%u6316%u7684%u5927%u5751%u3002%0A%u53C2%u8003%u94FE%u63A5%uFF1A%5BDebugging%20Karma%20Unit%20Tests%5D%28https%3A//glebbahmutov.com/blog/debugging-karma-unit-tests/index.html%29%0A%u6309%u7167%u4E0A%u9762%u7684%u6B65%u9AA4%u6253%u5F00%u8C03%u8BD5%u9875%u9762%u548CChrome%20Dev%20Tool%u9875%u9762%uFF0C%u5237%u65B0%u9875%u9762%u5373%u53EF%u91CD%u65B0%u52A0%u8F7D%u4EE3%u7801%u5E76%u89E6%u53D1%u65AD%u70B9%u3002%0A%u5C06%u547D%u4EE4%u884C%u5199%u5165package.json%uFF0C%u4EE5%u540E%u8C03%u7528npm%u5C31%u53EF%u4EE5%u65B9%u4FBF%u7684%u6253%u5F00%u8BE5%u8C03%u8BD5%u5DE5%u5177%u3002%0A%60%60%60%0A%7B%0A...%0A%20%20%22scripts%22%3A%20%7B%0A%20%20%20%20%22start%22%3A%20%22node%20./bin/www%22%2C%0A%20%20%20%20%22karma-debug%22%3A%20%22cd%20public/js%20%26%26%20karma%20start%20--browsers%3DChrome%20--single-run%3Dfalse%20--debug%22%0A%20%20%7D%2C%20%0A%20%20...%0A%7D%0A%60%60%60%0A%23%23%20ngMock%0AngMock%u4E2D%u4E24%u4E2A%u6700%u91CD%u8981%u7684%u5173%u952E%u8BCD%u5C31%u662F%uFF1Amodule%2C%20inject%u3002%u53EF%u4EE5%u53C2%u8003%u8FD9%u4E2A%u4EBA%u7684%u535A%u5BA2%uFF1A%0A-%20%5BngMock%20Fundamentals%20for%20AngularJS%20-%20Understanding%20Inject%5D%28http%3A//www.bradoncode.com/blog/2015/05/27/ngmock-fundamentals-angularjs-testing-inject/%29%0A-%20%5BngMock%20Fundamentals%20for%20AngularJS%20Unit%20Testing%20-%20Understanding%20Module%5D%28http%3A//www.bradoncode.com/blog/2015/05/24/ngmock-fundamentals-angularjs-unit-testing/%29%0A%0AngMock%u901A%u8FC7module%u52A0%u8F7D%u5DF2%u7ECF%u5B9A%u4E49%u4E86%u7684%u6A21%u5757%uFF0C%u6216%u8005%u5B9A%u4E49%u533F%u540Dmodule%u5E76%u63D0%u4F9B%u65B0%u7684service%uFF0C%u6216%u8005%u590D%u5199%u5DF2%u5B58%u5728%u7684service%u3002module%u53EA%u662F%u5C06%u540D%u5B57%uFF0C%u4EE5%u53CA%u5BF9%u5E94%u4F4D%u7F6E%u5B58%u5165%u4E00%u4E2A%u6570%u7EC4%u3002inject%u5C06%u771F%u6B63%u7684%u52A0%u8F7D%u6240%u6709module%u63D0%u4F9B%u7684service%uFF0C%u5E76%u89E3%u51B3%u4F9D%u8D56%u5173%u7CFB%uFF0C%u8FD8%u53EF%u4EE5%u5C06%u76F8%u5E94%u7684service%u5BFC%u51FA%u5230test%20case%u4E2D%u4F7F%u7528%u3002%u5176%u5B9EngMock%u4E5F%u5C31%u662F%u505A%u4E86%u8FD9%u4E24%u4EF6%u4E8B%u60C5%u3002%0A%u4E0B%u9762%u7B80%u5355%u4ECB%u7ECD%u4E00%u4E9B%u6709%u52A9%u4E8E%u7406%u89E3%u7684%u77E5%u8BC6%u70B9%u3002%0A%23%23%23%20angular.mock.module%0Amodule%u51FD%u6570%u7684%u539F%u578B%u662F%60angular.mock.module%28alias%2C%20obj%2C%20func%29%60%0Amodule%u63A5%u53D7%u4E09%u79CD%u53C2%u6570%u7C7B%u578B%uFF0C%u6BCF%u4E00%u79CD%u90FD%u53EF%u4EE5%u5355%u72EC%u4F7F%u7528%uFF0C%u7528%u6765%u5B9A%u4E49%u6216%u52A0%u8F7D%u5BF9%u5E94%u6A21%u5757%0A-%20alias%3A%20%u662Fmodule%u540D%u5B57%u5B57%u7B26%u4E32%uFF0C%u53EF%u4EE5%u662F%u5728%u5F85%u6D4B%u4EE3%u7801%u4E2D%u5B9A%u4E49%u7684%uFF0C%u4E5F%u53EF%u4EE5%u662F%u5728%u6D4B%u8BD5%u6587%u4EF6%u4E2D%u5B9A%u4E49%u7684%0A-%20obj%3A%20%u4E00%u4E2A%u666E%u901A%u7684%u5B57%u5178%u7ED3%u6784%uFF0C%u4ED6%u4F1A%u751F%u6210%u4E00%u4E2A%u533F%u540Dmodule%uFF0C%u5176%u4E2D%u7684%u6210%u5458%u4F1A%u88AB%u5B9A%u4E49%u6210%60%24provide.value%28%29%60%0A-%20function%3A%20%u540C%u6837%u662F%u5B9A%u4E49%u4E00%u4E2A%u533F%u540Dmodule%uFF0C%u4E0D%u540C%u7684%u662F%uFF0Cfunction%u53EF%u4EE5%u7528%24provide%u4F5C%u4E3A%u53C2%u6570%uFF0C%u6765%u5B9A%u4E49%u5404%u79CD%u5404%u6837%u7684service%u3002%0A%0A%3E**function%28%29%20vs%20Object**%0A%0A%3EThe%20examples%20we%20used%20were%20similar%2C%20so%20what%u2019s%20the%20key%20difference%20between%20the%20function%28%29%20and%20Object%20form%3F%20As%20the%20Object%20type%20gets%20added%20as%20a%20%24provider.value%2C%20it%20is%20therefore%20restricted%20to%20the%20capabilities%20of%20the%20value%20service%2C%20most%20importantly%20that%20a%20value%20service%20cannot%20be%20injected%20with%20other%20services.%0A%0A%u5173%u4E8E%24provide%uFF0C%u53EF%u4EE5%u53C2%u8003%5BAngular%u5B98%u65B9%u6587%u6863%5D%28https%3A//docs.angularjs.org/api/auto/service/%5C%24provide%29%0A%3EThe%20**%5C%24provide**%20service%20has%20a%20number%20of%20methods%20for%20registering%20components%20with%20the%20**%5C%24injector**.%20Many%20of%20these%20functions%20are%20also%20exposed%20on%20angular.Module.%0A%0A%u5B83%u63D0%u4F9B%u4E86%u4E0B%u9762%u7684%u8FD9%u4E9Bservice%0A%0A%3E**provider**%28name%2C%20provider%29%20-%20registers%20a%20service%20provider%20with%20the%20%5C%24injector%0A**constant**%28name%2C%20obj%29%20-%20registers%20a%20value/object%20that%20can%20be%20accessed%20by%20providers%20and%20services.%0A**value**%28name%2C%20obj%29%20-%20registers%20a%20value/object%20that%20can%20only%20be%20accessed%20by%20services%2C%20not%20providers.%0A**factory**%28name%2C%20fn%29%20-%20registers%20a%20service%20factory%20function%20that%20will%20be%20wrapped%20in%20a%20service%20provider%20object%2C%20whose%20%5C%24get%20property%20will%20contain%20the%20given%20factory%20function.%0A**service**%28name%2C%20Fn%29%20-%20registers%20a%20constructor%20function%20that%20will%20be%20wrapped%20in%20a%20service%20provider%20object%2C%20whose%20%5C%24get%20property%20will%20instantiate%20a%20new%20object%20using%20the%20given%20constructor%20function.%0A**decorator**%28name%2C%20decorFn%29%20-%20registers%20a%20decorator%20function%20that%20will%20be%20able%20to%20modify%20or%20replace%20the%20implementation%20of%20another%20service.%0A%0A%23%23%23%20angular.mock.inject%0A%5C%24inject%u662FngMock%u7684%u6838%u5FC3%uFF0C%u770B%u770B%u5B98%u65B9%u7684%u5B9A%u4E49%0A%3EThe%20inject%20function%20wraps%20a%20function%20into%20an%20injectable%20function.%20The%20inject%28%29%20creates%20new%20instance%20of%20%24injector%20per%20test%2C%20which%20is%20then%20used%20for%20resolving%20references.%0A%0Ainject%u5C31%u662F%u5B9A%u4E49%u4E86%u4E00%u4E2Ainjector%uFF0C%u90A3injector%u53C8%u662F%u4EC0%u4E48%0A%3E%5C%24injector%20is%20used%20to%20retrieve%20object%20instances%20as%20defined%20by%20provider%2C%20instantiate%20types%2C%20invoke%20methods%2C%20and%20load%20modules.%0A%0A%5C%24injector%u4E5F%u5C31%u662FAngularJS%u7684%u6838%u5FC3%u3002%u5B83%u63D0%u4F9B%u4E86%u6240%u8C13%u7684%u201CDI%22%20%28Dependency%20Injection%29%u6A21%u5F0F%u3002Angular%u7684%u6838%u5FC3%u4EE3%u7801%u901A%u8FC7%5C%24injector%u6765%u83B7%u53D6%u5BF9%u5E94service%u7684reference%u3002%0A%u6211%u4EEC%u901A%u5E38%u5C06inject%u653E%u5728beforeEach%u91CC%uFF0C%u8FD9%u6837inject%u5C31%u4E3A%u6BCF%u4E2A%u6D4B%u8BD5%u5B9A%u4E49%u4E00%u4E2Ainjector%uFF0C%u4E3A%u8FD9%u4E2Ainjector%u51C6%u5907%u597D%u6240%u6709%u7684service%uFF0C%u5E76%u80FD%u901A%u8FC7%u53C2%u6570%u5C06%u4EFB%u610F%u5DF2%u6709%u7684service%u5BFC%u51FA%u3002%0A%0A%23%23%23%20Test%20controllers%0A%u4E86%u89E3%u4E86%u4E0A%u9762%u7684%u4E24%u4E2A%u91CD%u8981%u7EC4%u4EF6%u4E4B%u540E%uFF0C%u9488%u5BF9%u4E0D%u540C%u7684%u6D4B%u8BD5%u7EC4%u4EF6%u5C31%u5BB9%u6613%u7406%u89E3%u4E86%u3002%0A%u76F4%u63A5%u4E0A%u4EE3%u7801%uFF1A%0A%60%60%60javascript%0Adescribe%28%27main%20controller%27%2C%20function%28%29%20%7B%0A%20%20var%20%24controller%0A%20%20var%20mainCtrl%0A%20%20beforeEach%28%27set%20up%20module%27%2C%20function%28%29%20%7B%0A%20%20%20%20module%28function%20%28%24provide%29%20%7B%0A%20%20%20%20%20%20%24provide.service%28%27MockDataService%27%2C%20function%20%28%29%20%7B%0A%20%20%20%20%20%20%20%20const%20service%20%3D%20this%0A%20%20%20%20%20%20%20%20service.data%20%3D%20%27test%20data%27%0A%20%20%20%20%20%20%7D%29%0A%20%20%20%20%7D%29%0A%0A%20%20%20%20module%28%27MainModule%27%29%0A%20%20%7D%29%3B%0A%0A%20%20beforeEach%28%27set%20up%20controller%27%2C%20inject%28function%20%28_%24controller_%2C%20MockDataService%29%20%7B%0A%20%20%20%20%24controller%20%3D%20_%24controller_%0A%20%20%20%20mainCtrl%20%3D%20%24controller%28%27MainController%27%2C%20%7B%0A%20%20%20%20%20%20%20%20%20%20DataService%3A%20MockDataService%0A%20%20%20%20%7D%29%0A%20%20%7D%29%29%20%0A%0A%20%20it%28%27should%20assemble%20print%20page%20URL%20correctly%27%2C%20function%28%29%20%7B%0A%20%20%20%20mainCtrl.getData%28%29.should.equal%28%27test%20data%27%29%0A%20%20%7D%29%3B%0A%7D%29%3B%0A%60%60%60%0A%u8FD9%u91CC%u53EA%u6709%u4E24%u4E2A%u503C%u5F97%u6CE8%u610F%u7684%u5730%u65B9%uFF0C%u5176%u5B9E%u524D%u6587%u4E5F%u90FD%u6709%u63D0%u53CA%u3002%0A1.%20%u7B2C%u4E00%u4E2Amodule%u4E2D%u901A%u8FC7%5C%24provide%u63D0%u4F9B%u4E86%u4E00%u4E2A%u5047%u7684service%uFF0C%u8FD9%u4E2Aservice%u53EF%u4EE5%u901A%u8FC7%u5176%u540D%u5B57%u88ABinject%u3002%0A2.%20inject%u4E2D%u901A%u8FC7%5C_%5C%24controller%5C_%u6765%u5BFC%u51FAcontroller%u7684%u6784%u9020%u51FD%u6570%u3002%u901A%u8FC7%u8FD9%u4E2A%u6784%u9020%u51FD%u6570%u6765%u6784%u9020%u76F8%u5E94%u7684%u5F85%u6D4Bcontroller%uFF0C%u5E76%u4F20%u5165%u5047%u7684service%u8FBE%u5230mock%u7684%u76EE%u7684%u3002%0A%0A%23%23%23%20Test%20services%0A%u4ECD%u7136%u76F4%u63A5%u4E0A%u4EE3%u7801%uFF1A%0A%60%60%60%20javascript%0Aangular.module%28%27PrinterData%27%2C%20%5B%5D%29%0A.config%28%5B%27%24locationProvider%27%2C%20function%28%24locationProvider%29%20%7B%0A%20%20%24locationProvider.html5Mode%28true%29%3B%0A%7D%5D%29%3B%0A%60%60%60%0A%60%60%60javascript%0Adescribe%28%27printer%20data%20service%27%2C%20function%28%29%20%7B%0A%20%20var%20%24httpBackend%0A%20%20var%20PrinterDataService%0A%20%20beforeEach%28function%28%29%7B%0A%20%20%20%20module%28function%20%28%24provide%29%20%7B%0A%20%20%20%20%20%20%24provide.provider%28%27%24location%27%2C%20function%20%28%29%20%7B%0A%20%20%20%20%20%20%20%20const%20provider%20%3D%20this%0A%20%20%20%20%20%20%20%20const%20location%20%3D%20%7B%7D%0A%20%20%20%20%20%20%20%20location.search%20%3D%20function%20%28%29%7B%0A%20%20%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20id%3A%20%27test%20id%27%2C%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20provider.html5Mode%20%3D%20sinon.stub%28%29%0A%20%20%20%20%20%20%20%20provider.%24get%20%3D%20sinon.stub%28%29.returns%28location%29%0A%20%20%20%20%20%20%7D%29%0A%20%20%20%20%7D%29%0A%20%20%7D%29%3B%0A%20%20beforeEach%28function%28%29%20%7B%0A%20%20%20%20module%28%27Data%27%29%0A%20%20%20%20inject%28function%20%28%24injector%29%20%7B%0A%20%20%20%20%20%20%24httpBackend%20%3D%20%24injector.get%28%27%24httpBackend%27%29%0A%20%20%20%20%20%20DataService%20%3D%20%24injector.get%28%27DataService%27%29%0A%20%20%20%20%7D%29%0A%20%20%7D%29%3B%0A%0A%20%20it%28%27should%20update%20if%20succeeds%27%2C%20function%28done%29%20%7B%0A%20%20%20%20%24httpBackend.whenGET%28/.*/%29%0A%20%20%20%20%20%20.respond%28%7B%0A%20%20%20%20%20%20%20%20status%3A%20%27ready%27%0A%20%20%20%20%20%20%7D%29%0A%20%20%20%20DataService.getStatus%28%29.then%28function%20%28data%29%20%7B%0A%20%20%20%20%20%20data.should.deepEqual%28%7B%0A%20%20%20%20%20%20%20%20status%3A%20%27ready%27%0A%20%20%20%20%20%20%7D%29%0A%20%20%20%20%20%20done%28%29%0A%20%20%20%20%7D%29.catch%28function%20%28err%29%20%7B%0A%20%20%20%20%20%20should.not.exist%28err%29%0A%20%20%20%20%7D%29%0A%20%20%20%20%0A%20%20%20%20%24httpBackend.flush%28%29%3B%0A%20%20%7D%29%3B%0A%7D%29%0A%60%60%60%0A%u4E0B%u9762%u7F57%u5217%u4E00%u4E9B%u5B9E%u9645%u4F7F%u7528%u4E2D%u78B0%u5230%u7684%u4E00%u4E9B%u95EE%u9898%uFF0C%u4EE5%u53CA%u81EA%u5DF1%u7684%u4E00%u4E9B%u7406%u89E3%u3002%0A%0A%23%23%23%23%20%u4F7F%u7528%5C%24injector%0A%u6211%u4EEC%u8BF4%u8FC7inject%u51FD%u6570%u5C31%u662F%u7528%u6765%u521B%u5EFA%5C%24injector%uFF0C%u6240%u4EE5%5C%24injector%u4E5F%u662F%u53EF%u4EE5%u76F4%u63A5%u88AB%u5BFC%u51FA%u4F7F%u7528%u7684%u3002%u8FD9%u65F6%u9700%u8981%u4EC0%u4E48service%uFF0C%u5C31%u53EF%u4EE5%u76F4%u63A5%u8C03%u7528%5C%24injector.get%u3002%u4E3A%u4EC0%u4E48%u4E0D%u76F4%u63A5%u901A%u8FC7%u53C2%u6570%u6765inject%u5462%uFF1F%u5F53%u7136%u662F%u56E0%u4E3A%u53C2%u6570%u592A%u957F%u4E86%uFF0C%u4E0D%u597D%u770B%u3002%u3002%u3002%0A%23%23%23%23%20mock%20%5C%24http%0A%u4E3A%u4E86%u6D4B%u8BD5%u7A33%u5B9A%u6027%uFF0C%u6211%u4EEC%u5728unit%20test%u4E2D%u5E76%u4E0D%u771F%u6B63%u7684%u8BBF%u95EE%u670D%u52A1%u5668%uFF0C%u800C%u901A%u5E38%u91C7%u7528%u5047%u7684%u670D%u52A1%u5668%u54CD%u5E94%u6765mock%u3002%u8FD9%u5C31%u9700%u8981%u7528%u5230%5C%24httpBackend%20service%u3002%u90A3%u4E48%5C%24http%u548C%5C%24httpBackend%u4E4B%u95F4%u7684%u5173%u7CFB%u662F%u4EC0%u4E48%uFF1F%0A%3EHTTP%20backend%20used%20by%20the%20service%20that%20delegates%20to%20XMLHttpRequest%20object%20or%20JSONP%20and%20deals%20with%20browser%20incompatibilities.%0AYou%20should%20never%20need%20to%20use%20this%20service%20directly%2C%20instead%20use%20the%20higher-level%20abstractions%3A%20%5C%24http%20or%20%5C%24resource.%0ADuring%20testing%20this%20implementation%20is%20swapped%20with%20mock%20%24httpBackend%20which%20can%20be%20trained%20with%20responses.%0A%0A%u5177%u4F53%u505A%u6CD5%uFF0C%u4E0A%u9762%u7684%u4EE3%u7801%u4E2D%u5DF2%u6709%uFF0C%u8282%u5F55%u5982%u4E0B%uFF1A%0A%60%60%60javascript%0AbeforeEach%28function%28%29%20%7B%0A%20%20%20%20module%28%27Data%27%29%0A%20%20%20%20inject%28function%20%28%24injector%29%20%7B%0A%20%20%20%20%20%20%24httpBackend%20%3D%20%24injector.get%28%27%24httpBackend%27%29%0A%20%20%20%20%20%20DataService%20%3D%20%24injector.get%28%27DataService%27%29%0A%20%20%20%20%7D%29%0A%20%20%7D%29%3B%0A%0A%20%20it%28%27should%20update%20if%20succeeds%27%2C%20function%28done%29%20%7B%0A%20%20%20%20%24httpBackend.whenGET%28/.*/%29%0A%20%20%20%20%20%20.respond%28%7B%0A%20%20%20%20%20%20%20%20status%3A%20%27ready%27%0A%20%20%20%20%20%20%7D%29%0A%20%20%20%20DataService.getStatus%28%29.then%28function%20%28data%29%20%7B%0A%20%20%20%20%20%20data.should.deepEqual%28%7B%0A%20%20%20%20%20%20%20%20status%3A%20%27ready%27%0A%20%20%20%20%20%20%7D%29%0A%20%20%20%20%20%20done%28%29%0A%20%20%20%20%7D%29.catch%28function%20%28err%29%20%7B%0A%20%20%20%20%20%20should.not.exist%28err%29%0A%20%20%20%20%7D%29%0A%20%20%20%20%0A%20%20%20%20%24httpBackend.flush%28%29%3B%0A%20%20%7D%29%3B%0A%60%60%60%0A%u901A%u8FC7%60%24httpBackend.wheGET%28...%29.respond%28...%29%60%u6765%u8BBE%u7F6Emock%20response%u3002%60whenGET%60%u8FD8%u652F%u6301%u6B63%u5219%u5F0F%u3002%0A%u8C03%u7528%u5B8C%u8BF7%u6C42%u51FD%u6570%u540E%u8981%u8BB0%u5F97%60%24httpBackend.flush%28%29%60%u5C06%u8BF7%u6C42%u771F%u6B63%u7684%u53D1%u51FA%u3002%u5426%u5219%u8BF7%u6C42%u5E76%u4E0D%u4F1A%u53D1%u51FA%uFF0C%u76F4%u5230%u8C03%u7528%60flush%28%29%60%u51FD%u6570%uFF0C%u6216%u8005%u6D4B%u8BD5%u8D85%u65F6%u5931%u8D25%u3002%0A%0A%23%23%23%23%20%u5982%u4F55mock%20%5C%24location%20%26%20%5C%24locationProvider%0A%u5F88%u60ED%u6127%u7684%u8BF4%uFF0C%u5728%u8FD9%u4E2A%u95EE%u9898%u4E0A%u6211%u82B1%u4E86%u5F88%u4E45%u5F88%u4E45%u7684%u65F6%u95F4%uFF0C%u641C%u7D22%u4E86%u5927%u91CF%u7684google%u7F51%u9875%uFF0C%u6700%u540E%u8FD8%u662F%u4F9D%u9760chrome%u8FFD%u8E2Aangular%u6838%u5FC3%u4EE3%u7801%u624D%u53D1%u73B0%u539F%u6765%u662F%u81EA%u5DF1%u7ED9%u81EA%u5DF1%u6316%u7684%u5751%uFF0C%u5BF9service%2C%20factory%u548Cprovider%u7406%u89E3%u4E0D%u900F%u5F7B%u3002%0A%u9996%u5148%u6765%u5C31%u4E8B%u8BBA%u4E8B%uFF0C%u8BF4%u4E00%u4E0B%u5230%u5E95%u600E%u4E48%u5B8C%u6210%u6211%u7684%u8FD9%u4E2A%u6D4B%u8BD5%u3002%0A%0A%u56E0%u4E3A%u6211%u8981%u6D4B%u8BD5DataService%uFF0C%u800CDataService%u5B9A%u4E49%u5728Data%20moduleDataService%u8C03%u7528%u4E86%5C%24location%20service%u3002%u9700%u8981%u5C06%u4E4Bmock%u6389%u3002%u6700%u7B80%u5355%u7684%u60F3%u6CD5%u5C31%u662F%u901A%u8FC7inject%u6765%u83B7%u5F97%5C%24location%20service%20reference%u3002%u5E76%u901A%u8FC7sinon.stub%u6765%u8BBE%u7F6E%u9700%u8981%u7684%u8FD4%u56DE%u503C%u3002%0A%0A%60%60%60%0Ainject%28function%20%28%24injector%2C%20%24location%29%20%7B%0A%20%20sinon.stub%28%24location%2C%20%27search%27%29.returns%28%7B%0A%20%20%20%20id%3A%20%27test%20id%27%0A%20%20%7D%29%0A%20%20DataService%20%3D%20%24injector.get%28%27DataService%27%29%0A%7D%29%0A%60%60%60%0A%0A%u4F46%u662F%u6211%u5012%u9709%u50AC%u7684%uFF0C%u7ADF%u7136%u662F%u7528%5C%24provide%u505A%u4E86%uFF1A%0A%60%60%60%0Amodule%28function%20%28%24provide%29%20%7B%0A%20%20%24provide.service%28%27%24location%27%2C%20function%20%28%29%20%7B%0A%20%20%20%20const%20location%20%3D%20this%0A%20%20%20%20location.search%20%3D%20sinon.stub%28%29.returns%28%7B%0A%20%20%20%20%20%20device_id%3A%20%27test%20id%27%2C%0A%20%20%20%20%7D%29%0A%20%20%7D%29%0A%7D%29%0A%60%60%60%0A%u8FD9%u6837%u5C31%u4F1A%u6709%u95EE%u9898%u4E86%uFF0C%u5F53ngMock%u52A0%u8F7D%u8FD9%u4E2A%u6A21%u5757%u7684%u65F6%u5019%uFF0C%u4F1A%u5148%u5B9A%u4E49%u4E00%u4E2A%5C%24locationProvider%uFF0C%u8FD9%u5C31%u4F1A%u8986%u76D6%u5176%u81EA%u5E26%u7684%5C%24locationProvider%u3002%u800C%u5982%u4E0B%u7684module.config%u65B9%u6CD5%u4E2D%u5BF9%5C%24locationProvider%u7684%u64CD%u4F5C%u5C31%u4F1A%u51FA%u73B0%u95EE%u9898%uFF1A%0A%60%60%60%0Aangular.module%28%27PrinterData%27%2C%20%5B%5D%29%0A.config%28%5B%27%24locationProvider%27%2C%20function%28%24locationProvider%29%20%7B%0A%20%20%24locationProvider.html5Mode%28true%29%3B%0A%7D%5D%29%3B%0A%60%60%60%0A%u56E0%u4E3A%u65B0%u5B9A%u4E49%u7684%5C%24locationProvider%u4E2D%u6CA1%u6709html5Mode%u6210%u5458%u3002%u4E8E%u662F%u60F3%u4E86%u5404%u79CD%u529E%u6CD5%u6765mock%20%5C%24locationProvider%u3002%u7ED3%u679C%u56E0%u4E3A%u5BF9service%uFF0Cfactory%uFF0Cprovider%u7684%u7406%u89E3%u4E0D%u6B63%u786E%uFF0C%u90FD%u5931%u8D25%u4E86%u3002%u653E%u4E00%u6BB5%u9519%u8BEF%u7684%u4EE3%u7801%uFF1A%0A%60%60%60%0AbeforeEach%28function%28%29%7B%0A%20%20module%28function%20%28%24provide%29%20%7B%0A%20%20%20%20%24provide.service%28%27%24locationProvider%27%2C%20function%20%28%29%20%7B%0A%20%20%20%20%20%20const%20location%20%3D%20this%0A%20%20%20%20%20%20location.html5Mode%20%3D%20function%28%29%7B%7D%0A%20%20%20%20%7D%29%0A%20%20%20%20%24provide.factory%28%27%24location%27%2C%20function%20%28%29%20%7B%0A%20%20%20%20%20%20const%20locationProvider%20%3D%20this%0A%20%20%20%20%20%20locationProvider.html5Mode%20%3D%20function%28%29%7B%7D%0A%20%20%20%20%7D%29%0A%7D%29%0A%60%60%60%0A%u7B2C%u4E00%u4E2A%u7528%5C%24provide.service%u4F1A%u5B9A%u4E49%u4E00%u4E2A%5C%24locationProviderProvider%0A%u7B2C%u4E8C%u4E2A%u7528%5C%24provide.factory%u5B9A%u4E49%u4E86%u4E00%u4E2A%5C%24locationProvider%uFF0C%u4F46%u662F%u6CA1%u6709%u8FD4%u56DE%uFF0C%u6240%u4EE5%5C%24location%20service%u4E0D%u4F1A%u88AB%u5B9E%u4F8B%u5316%uFF0C%u4E5F%u5C31%u6CA1%u6709%5C%24location%20service%u4E86%u3002%0A%u5982%u679C%u60F3%u901A%u8FC7mock%20%5C%24locationProvider%u6765mock%20%5C%24location%20service%u7684%u8BDD%uFF0C%u6B63%u786E%u7684%u505A%u6CD5%u5E94%u5F53%u662F%uFF1A%0A%60%60%60%0A%24provide.provider%28%27%24location%27%2C%20function%20%28%29%20%7B%0A%20%20const%20provider%20%3D%20this%0A%20%20const%20location%20%3D%20%7B%7D%0A%20%20location.search%20%3D%20function%20%28%29%7B%0A%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20device_id%3A%20%27test%20id%27%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20provider.html5Mode%20%3D%20sinon.stub%28%29%0A%20%20provider.%24get%20%3D%20sinon.stub%28%29.returns%28location%29%0A%7D%29%0A%60%60%60%0Amock%u6389provider.%5C%24get%u51FD%u6570%uFF0C%u63D0%u4F9B%u5047%u7684location%u670D%u52A1%u3002%u5176%u5B9E%u8FD8%u6709%u5F88%u591A%u529E%u6CD5%uFF0C%u4F8B%u5982%u83B7%u5F97%5C%24locationProvider%u518D%u5BF9%5C%24get%u51FD%u6570%u8FDB%u884Cstub%uFF0C%u4E5F%u53EF%u4EE5%u3002%u6CA1%u6709%u518D%u4E00%u4E00%u5C1D%u8BD5%u4E86%u3002%u7406%u89E3%u4E86%u9519%u8BEF%u548C%u6B63%u786E%u7684%u505A%u6CD5%uFF0C%u60F3%u518D%u53BB%u5B9E%u73B0%u5C31%u5F97%u5FC3%u5E94%u624B%u4E86%u3002%0A%0A%23%23%23%23%20%u6D4B%u8BD5.config/.run%u5757%0AData%20module%u6709%u4E00%u4E2A.config%u5757%uFF0C%u56E0%u4E3A%u6A21%u5757%u52A0%u8F7D%u7684%u65F6%u5019%u4F1A%u5148%u8FD0%u884C%u5176.config%u5757%uFF0C%u7136%u540E%u662F.run%u5757%u3002%u56E0%u5176%u7279%u6B8A%u6027%uFF0C%u6240%u4EE5%u5728%u5BF9%u5176%u4E2D%u4F7F%u7528%u5230%u7684%u670D%u52A1%u8FDB%u884Cmock%u8981%u6CE8%u610F%u987A%u5E8F%u3002%u53EF%u4EE5%u53C2%u8003%u8FD9%u4E2A%u94FE%u63A5%uFF1A%5BTesting%20config%20and%20run%20blocks%20in%20AngularJS%0AHow%20to%20easily%20test%20an%20often%20neglected%20part%20of%20your%20application%5D%28https%3A//medium.com/@a_eife/testing-config-and-run-blocks-in-angularjs-1809bd52977e%29%0A%0A%23%23%23%23%20%u5982%u4F55mock%20%24interval%0A%u5728%u5355%u5143%u6D4B%u8BD5%u4E2D%uFF0C%u6211%u4EEC%u901A%u5E38%u8981%u91C7%u7528mock%u7684%u624B%u6BB5%uFF0C%u800C%u4E0D%u771F%u6B63%u7684%u4F7F%u7528%u65F6%u95F4%u670D%u52A1%u3002%u5728sinon%u4E2D%uFF0C%u53EF%u4EE5%u4F7F%u7528fake%20timer%u6765%u53D6%u4EE3JS%u4E2D%u6807%u51C6%u7684setTimeout%u548CsetInterval%u3002%u4E0D%u8FC7%u8FD9%u4E2A%u5728Angular%20JS%u4E2D%u5E76%u4E0D%u8D77%u4F5C%u7528%uFF0C%u5982%u679C%u6211%u4EEC%u4F7F%u7528%5C%24interval%u670D%u52A1%u7684%u8BDD%u3002%0A%u56E0%u4E3A%u5728%u5BF9Angular%20JS%u505A%u5355%u5143%u6D4B%u8BD5%u7684%u65F6%u5019%uFF0C%5C%24interval%u5E76%u4E0D%u4F1A%u8C03%u7528setInterval%u3002%u539F%u56E0%u662F%u8FD9%u91CC%u7684%5C%24interval%u5176%u5B9E%u662FngMock%u4E2D%u7684%5C%24interval%u3002%u6B64%u65F6%u53EF%u4EE5%u4F7F%u7528%60%24interval.flush%28ms%29%60%u6765%u5C06%u65F6%u949F%u63A8%u8FDB%u82E5%u5E72%u65F6%u95F4%u3002%u5F53%u7136%u5982%u679C%u613F%u610F%uFF0C%u4E5F%u53EF%u4EE5%u7528sinon%u6765mock%u4ECE%5C%24injector%u4E2Dget%u51FA%u6765%u7684%5C%24interval%20service%u3002%5C%24timeout%20service%u4E5F%u7C7B%u4F3C%uFF0C%u6709%u540C%u6837%u7684%u65B9%u6CD5%u5BF9%u4ED8%u5355%u5143%u6D4B%u8BD5%u3002%0A%0A%23%23%23%23%20Promise%20not%20resolve%0A%u5728Angular%20JS%u4E2D%uFF0C%u6211%u4EEC%u5F80%u5F80%u8C03%u7528%5C%24q%20service%u6765%u4F7F%u7528Promise%uFF0C%u800C%5C%24q.defer%28%29.promise%u53EA%u5728%5C%24scope%u7684digest%20cycle%u624D%u4F1A%u5224%u5B9Aresolve%u8FD8%u662Freject%uFF0C%u6240%u4EE5%u8981%u89E6%u53D1%5C%24q%u4EA7%u751F%u7684promise%uFF0C%u4E00%u5B9A%u8981%u8C03%u7528%u5BF9%u5E94%u7684%5C%24scope.%5C%24apply%28%29%u6216%u8005%5C%24scope.%5C%24digest%28%29%u3002stack%20overflow%u4E2D%u4E5F%u6709%u5927%u628A%u7C7B%u4F3C%u95EE%u9898%u7684%u89E3%u7B54%u3002%u5177%u4F53%u4E5F%u53EF%u4EE5%u53C2%u8003%u8FD9%u7BC7%u535A%u5BA2%uFF0C%u8BB2%u5F97%u5F88%u900F%u5F7B%uFF1A%0A-%20%5BUnit%20Testing%20with%20%24q%20Promises%20in%20AngularJS%5D%28http%3A//www.bradoncode.com/blog/2015/07/13/unit-test-promises-angualrjs-q/%29%0A%0A%u5728Angular%20JS%u4E2D%uFF0C%u4E5F%u53EF%u4EE5%u4F7F%u7528%u6807%u51C6%u7684Promise%uFF0C%u4F8B%u5982%3A%20%60var%20promise%20%3D%20new%20Promise%28resolve%2C%20reject%29%60%u3002%u4F46%u662F%u8C8C%u4F3C%uFF0C%u6807%u51C6%u7684Promise%u548C%5Cq%u517C%u5BB9%u7684%u5E76%u4E0D%u5F88%u597D%uFF0C%u4E0B%u9762%u7684code%u5E76%u4E0D%u4F1A%u5DE5%u4F5C%uFF1A%0A%60%60%60%0Avar%20promise%20%3D%20%24q.all%28%5Bpromise1%2C%20promise2%5D%29%0A%24scope.%24apply%28%29%0A%60%60%60%0Apromise%u5E76%u4E0D%u4F1A%u5728promise1%u548Cpromise2%u8FD4%u56DE%u4E4B%u540E%u8FD4%u56DE%uFF0C%u5B9E%u9645%u4E0A%u662F%u4ED6%u6839%u672C%u5C31%u4E0D%u4F1A%u8FD4%u56DE%uFF0C%u76F4%u5230%u6D4B%u8BD5%u8D85%u65F6%u5931%u8D25%u3002%0A%0A%23%23%23%23%20%22Unexpected%20request%3A%20GET%20.../.html%22%20on%20Karma%20tests%0A%u8FD9%u662FYaakov%u5728%u89C6%u9891%u4E2D%u63D0%u5230%u7684%u9700%u8981%u4F7F%u7528%5C%24templateCache%u7684%u60C5%u5F62%u3002Yaakov%u63D0%u5230%u7528karma%u662F%u89E3%u51B3%u8BE5%u95EE%u9898%u6700%u597D%u7684%u529E%u6CD5%uFF0C%u4F46%u662F%u56E0%u4E3A%u89C6%u9891%u5E76%u4E0D%u6D89%u53CA%u4ECB%u7ECDkarma%uFF0C%u6240%u4EE5%u5E76%u6CA1%u6709%u8BF4%u5E94%u8BE5%u600E%u4E48%u505A%u3002%0A%u5F53%u6211%u4EEC%u7684karma%20test%u52A0%u8F7D%u4E86route.js%uFF0C%u5E76%u5728%u5176%u4E2D%u5B9A%u4E49%u4E86component%uFF0C%u5219%u6D4B%u8BD5%u5728%u5B9E%u4F8B%u5316%u8BE5Angular%20component%u7684%u65F6%u5019%u56DE%u53BB%u7528%5C%24http%20service%u6765GET%u8BE5template%20html%u6587%u4EF6%uFF0C%u4E8E%u662F%u4EA7%u751F%u4E86%u8BE5unexpected%20request%u9519%u8BEF%u3002Yaakov%u5728%u6559%u7A0B%u4E2D%u7684%u89E3%u51B3%u529E%u6CD5%u662F%uFF0C%u901A%u8FC7%u4E00%u6B21%u771F%u6B63%u7684AJAX%u4ECE%u670D%u52A1%u5668%u4E0A%u8BF7%u6C42%u8BE5html%uFF0C%u5E76%u5C06%u4E4B%u5B58%u5165%5C%24templateCache%u4E2D%uFF0C%u4E4B%u540E%u5C31%u4E0D%u4F1A%u518D%u6709%u771F%u6B63%u7684GET%20AJAX%u4EA7%u751F%u4E86%u3002%u5176%u5B9E%u8FD9%u4E00%u6BB5%u53EF%u4EE5%u901A%u8FC7%u914D%u7F6Ekarma%u6765%u5B9E%u73B0%u3002%u5177%u4F53%u505A%u6CD5%u5982%u4E0B%uFF1A%0A-%20%u5B89%u88C5ng-html2js%uFF0Ckarma-ng-html2js-preprocessor%0A-%20%u4F7F%u7528ng-html2js%u5C06%u5BF9%u5E94%u7684template%20html%u8F6C%u6210js%u6A21%u5757%uFF0C%u5E76%u5728%u6D4B%u8BD5%u4EE3%u7801%u4E2D%u5F15%u5165%u3002%0A%0A%u770B%u4E0B%u9762%u5177%u4F53%u4EE3%u7801%0Akarma.conf.js%0A%60%60%60%0A//%20list%20of%20files%20/%20patterns%20to%20load%20in%20the%20browser%0Afiles%3A%20%5B%0A%20%20%27../html/templates/*.html%27%2C%0A%20%20...%0A%5D%2C%0A%0A//%20preprocess%20matching%20files%20before%20serving%20them%20to%20the%20browser%0A//%20available%20preprocessors%3A%20https%3A//npmjs.org/browse/keyword/karma-preprocessor%0Apreprocessors%3A%20%7B%0A%20%20%27../html/templates/home.template.html%27%3A%20%5B%27ng-html2js%27%5D%0A%7D%2C%0A%0AngHtml2JsPreprocessor%3A%20%7B%0A%20%20moduleName%3A%20%27templates%27%2C%0A%20%20stripPrefix%3A%20%27.*/%27%2C%0A%20%20prependPrefix%3A%20%27/html/templates/%27%0A%7D%2C%20%0A%60%60%60%0Atest.js%0A%60%60%60%0Adescribe%28function%28%29%7B%0A%20%20beforeEach%28module%28%27templates%27%29%29%0A%7D%29%0A%60%60%60%0A%u7279%u522B%u8981%u6CE8%u610F%u7684%u662F%u5BF9ng-html2js%u7684%u914D%u7F6E%0A%60%60%60%0AngHtml2JsPreprocessor%3A%20%7B%0A%20%20moduleName%3A%20%27templates%27%2C%0A%20%20stripPrefix%3A%20%27.*/%27%2C%0A%20%20prependPrefix%3A%20%27/html/templates/%27%0A%7D%2C%20%0A%60%60%60%0A%u5F53%u6CA1%u6709%60stripPrefix%60%u9009%u9879%u65F6%uFF0C%u901A%u8FC7Chrome%20debug%u53EF%u4EE5%u770B%u5230%uFF0C%u6240%u6709%u7684template%u90FD%u88AB%u8F6C%u5316%u6210%u4E86js%u6A21%u5757%uFF0C%u4F46%u662F%u5176%u6A21%u5757%u540D%u662F%u7C7B%u4F3C%60c%3A%5Cpath%5Cto%5Ctemplate%5Ctemplate.html%60%u7684%u7ED3%u6784%uFF0C%u800C%u6211%u5728route%u4E2D%u7684%u6307%u5B9A%u8DEF%u5F84%u662Fweb%u8DEF%u5F84%60templateUrl%3A%20%27/html/templates/home.template.html%27%60%u3002%u5C31%u5BF9%u4E0D%u4E0A%u53F7%u4E86%u3002%u505A%u6CD5%u662F%u901A%u8FC7%60stripPrefix%60%u5C06%u6240%u6709%u7684%u524D%u7F00%u5168%u90E8%u5265%u53BB%uFF0C%u7136%u540E%u7528%60prependPrefix%60%u52A0%u4E0A%u9700%u8981%u7684%u524D%u7F00%u3002%u8FD9%u6837%u5C31%u53EF%u4EE5%u5DE5%u4F5C%u4E86%u3002%u7F51%u4E0A%u6709%u5F88%u591A%u5E16%u5B50%u8BA8%u8BBA%u8FD9%u4E2A%u95EE%u9898%u3002%u5217%u4E24%u4E2A%u6709%u542F%u53D1%u6027%u7684%uFF1A%0A-%20%5BUnit%20Testing%20AngularJS%20directive%20with%20templateUrl%5D%28https%3A//stackoverflow.com/questions/15214760/unit-testing-angularjs-directive-with-templateurl%29%0A-%20%5BKarma%20%27Unexpected%20Request%27%20when%20testing%20angular%20directive%2C%20even%20with%20ng-html2js%5D%28https%3A//stackoverflow.com/questions/22869668/karma-unexpected-request-when-testing-angular-directive-even-with-ng-html2js%29%0A

Edit

修改root密码

synouser --setpw root xxx

How to “unmount /volume1”

  1. 查看占用volume1的进程:lsof /volumes
  2. 关闭对应的进程
find /usr/syno/etc.defaults/rc.sysv/ | grep -i <service name>
synoservicecfg --status | grep enable | grep -i <service name>

参考how-to-fsck-on-synology-dsm6

扫描File System

fsck.ext4 -v -n -f /dev/vg1000/lv #Check the filesystem, with NO changes being written
fsck.ext4 -v -f -y /dev/vg1000/lv #Check & fix your filesystem

查看所有挂载点

cat /etc/mtab
%23%20Synology%0A@%28myblog%29%5Bnas%2C%20synology%5D%0A%0A%23%23%20%u4FEE%u6539root%u5BC6%u7801%0A%60%60%60%0Asynouser%20--setpw%20root%20xxx%0A%60%60%60%0A%0A%23%23%20How%20to%20%22unmount%20/volume1%22%0A%0A1.%20%u67E5%u770B%u5360%u7528volume1%u7684%u8FDB%u7A0B%uFF1A%60lsof%20/volumes%60%0A2.%20%u5173%u95ED%u5BF9%u5E94%u7684%u8FDB%u7A0B%0A%60%60%60%0Afind%20/usr/syno/etc.defaults/rc.sysv/%20%7C%20grep%20-i%20%3Cservice%20name%3E%0Asynoservicecfg%20--status%20%7C%20grep%20enable%20%7C%20grep%20-i%20%3Cservice%20name%3E%0A%60%60%60%0A%u53C2%u8003%5Bhow-to-fsck-on-synology-dsm6%5D%28https%3A//github.com/OliPelz/how-to-fsck-on-synology-dsm6%29%0A%0A%23%23%20%u626B%u63CFFile%20System%0A%60%60%60bash%0Afsck.ext4%20-v%20-n%20-f%20/dev/vg1000/lv%20%23Check%20the%20filesystem%2C%20with%20NO%20changes%20being%20written%0Afsck.ext4%20-v%20-f%20-y%20/dev/vg1000/lv%20%23Check%20%26%20fix%20your%20filesystem%0A%60%60%60%0A%0A%23%23%20%u67E5%u770B%u6240%u6709%u6302%u8F7D%u70B9%0A%60%60%60bash%0Acat%20/etc/mtab%0A%60%60%60

Edit

所谓的Single Page Application (SPA)的核心就是用ui-router来切换不同的UI state,从而实现页面功能。但是相关库并不在AngularJS中,有两个库用的比较多

  • ngRouter from Google and community
  • ui-router from community

本文基于后者。

How to use it?

<script src="lib/angular.min.js"></script>
<script src="lib/angular-ui-router.min.js"></script>
<ui-view></ui-view>
angular.module('App',['ui.router']);

ui-router会用template html来替换ui-view标签<ui-view></ui-view>
进入核心配置:

angular.module('App')
.config(RoutesConfig);
RoutesConfig.$inject =
['$stateProvider', '$urlRouterProvider'];
function RoutesConfig($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/view1');
$stateProvider
.state('view1', {
url: '/view1',
template: '<div>…</div>'
})
.state('view2', {...});
}

$urlRouterProvider.otherwise设置了默认的URL。$stateProvider设置了各个状态。

$stateProvider
.state('itemDetail', {
url: '',
templateUrl: '',
controller: 'ItemListController as itemList',
resolve: {
}

每个状态都可以有template和controller。

controller

controller成员有这么几种写法:

  • controller: ‘CtrlName as label’
  • controller: CtrlName
  • controllerAs: label

resolve

The resolve property is a map object. The map object contains key/value pairs of:

  • key – {string}: a name of a dependency to be injected into the controller.
  • factory - {string|function}:
    • If string, then it is an alias for a service.
    • Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before the controller is instantiated and its value is injected into the controller.。

Yaakov的课程此处可能有错讹。并不是什么都能往resolve里放,放数字会报错,具体可参看GitHub - angular-ui/ui-router

也就是factory只有两种可能,string或者function,如果直接放数字,会出错。resolve成员会在controller实例化之前被确定。如果promise被reject,则会取消状态切换,然后错误会被送到$stateChangeError。resolve的成员,通过inject可以被controller使用,如下:

View1Ctrl.$inject = ['myData'];
function View1Ctrl(myData) {
var view1 = this;
view1.myData = myData;
}

URL Parameters

.state('view1', {
url: '/view1/{param1}',
templateUrl: 'view1.html',
controller: 'View1Ctrl as view1',
resolve: {
myData: ['$stateParams',
function ($stateParams) {
return getDataBasedOn($stateParams.param1);
}]
}
});

这是最正常的写法。在HTML中如下使用:

<a ui-sref="view1({itemId:someVal})">
Link to view with data
</a>

或者

$state.go('contacts', {param1: value1})

正则参数

Examples:

  • /user/{id:[^/]*} - Same as ‘/user/{id}’ from the previous example.
  • /user/{id:[0-9a-fA-F]{1,8}} - Similar to the previous example, but only matches if the id parameter consists of 1 to 8 hex digits.
  • /files/{path:.*} - Matches any URL starting with ‘/files/’ and captures the rest of the path into the parameter ‘path’.
  • /files/*path - Ditto. Special syntax for catch all.

目前,尚未实践过,猜测是对URL进行捕获,如果不符合该正则表达式,则该参数不存在

多于1个参数
使用URL参数写法:url: "/contacts?myParam1&myParam2"

Using Parameters without Specifying Them in State URLs

.state('contacts', {
url: "/contacts",
params: {
param1: null
},
templateUrl: 'contacts.html'
})

Router State Transition Events

stateChangeSuccess - fired once the state transition is complete
$stateChangeError - fires when an error occurs during transition
event.preventDefault() - to prevent the transition from occurring

State Inheritance

状态也可以继承,继承的唯一目的就是使用嵌套的ui-view。

.state('view1', {
resolve: {
myData: 'someVal';
}
})
.state('view1.child', {
url: '/detail/{param1}',
templateUrl: 'view1Detail.html',
controller: "ChildCtrl as child"
});
ChildCtrl.$inject = ['myData'];
function ChildCtrl (myData) { … }

状态继承的时候,resolve成员也可以继承,如上。

%23%20ui-router%0A@%28myblog%29%5Bangular%5D%0A%0A%u6240%u8C13%u7684Single%20Page%20Application%20%28SPA%29%u7684%u6838%u5FC3%u5C31%u662F%u7528ui-router%u6765%u5207%u6362%u4E0D%u540C%u7684UI%20state%uFF0C%u4ECE%u800C%u5B9E%u73B0%u9875%u9762%u529F%u80FD%u3002%u4F46%u662F%u76F8%u5173%u5E93%u5E76%u4E0D%u5728AngularJS%u4E2D%uFF0C%u6709%u4E24%u4E2A%u5E93%u7528%u7684%u6BD4%u8F83%u591A%0A-%20ngRouter%20from%20Google%20and%20community%0A-%20ui-router%20from%20community%0A%0A%u672C%u6587%u57FA%u4E8E%u540E%u8005%u3002%0A%0A%23%23%20How%20to%20use%20it%3F%0A%60%60%60%0A%3Cscript%20src%3D%22lib/angular.min.js%22%3E%3C/script%3E%0A%3Cscript%20src%3D%22lib/angular-ui-router.min.js%22%3E%3C/script%3E%0A%0A%3Cui-view%3E%3C/ui-view%3E%0A%0Aangular.module%28%27App%27%2C%5B%27ui.router%27%5D%29%3B%0A%60%60%60%0Aui-router%u4F1A%u7528template%20html%u6765%u66FF%u6362ui-view%u6807%u7B7E%60%3Cui-view%3E%3C/ui-view%3E%60%u3002%0A%u8FDB%u5165%u6838%u5FC3%u914D%u7F6E%uFF1A%0A%60%60%60%0Aangular.module%28%27App%27%29%0A.config%28RoutesConfig%29%3B%0ARoutesConfig.%24inject%20%3D%0A%5B%27%24stateProvider%27%2C%20%27%24urlRouterProvider%27%5D%3B%0Afunction%20RoutesConfig%28%24stateProvider%2C%20%24urlRouterProvider%29%20%7B%0A%09%24urlRouterProvider.otherwise%28%27/view1%27%29%3B%0A%09%0A%09%24stateProvider%0A%09.state%28%27view1%27%2C%20%7B%0A%09%09url%3A%20%27/view1%27%2C%0A%09%09template%3A%20%27%3Cdiv%3E%u2026%3C/div%3E%27%0A%09%7D%29%0A%09.state%28%27view2%27%2C%20%7B...%7D%29%3B%0A%7D%0A%60%60%60%0A%60%24urlRouterProvider.otherwise%60%u8BBE%u7F6E%u4E86%u9ED8%u8BA4%u7684URL%u3002%60%24stateProvider%60%u8BBE%u7F6E%u4E86%u5404%u4E2A%u72B6%u6001%u3002%0A%0A%60%60%60%0A%24stateProvider%0A.state%28%27itemDetail%27%2C%20%7B%0A%20%20%20%20url%3A%20%27%27%2C%0A%20%20%20%20templateUrl%3A%20%27%27%2C%0A%20%20%20%20controller%3A%20%27ItemListController%20as%20itemList%27%2C%0A%20%20%20%20resolve%3A%20%7B%0A%20%20%20%20%7D%0A%60%60%60%0A%u6BCF%u4E2A%u72B6%u6001%u90FD%u53EF%u4EE5%u6709template%u548Ccontroller%u3002%0A%0A%23%23%23%20controller%0Acontroller%u6210%u5458%u6709%u8FD9%u4E48%u51E0%u79CD%u5199%u6CD5%uFF1A%0A-%20controller%3A%20%u2018CtrlName%20as%20label%u2019%0A-%20controller%3A%20CtrlName%0A-%20controllerAs%3A%20label%0A%0A%23%23%23%20resolve%0AThe%20resolve%20property%20is%20a%20map%20object.%20The%20map%20object%20contains%20key/value%20pairs%20of%3A%0A%0A-%20key%20%u2013%20%7Bstring%7D%3A%20a%20name%20of%20a%20dependency%20to%20be%20injected%20into%20the%20controller.%0A-%20factory%20-%20%7Bstring%7Cfunction%7D%3A%0A%09-%20If%20string%2C%20then%20it%20is%20an%20alias%20for%20a%20service.%0A%09-%20Otherwise%20if%20function%2C%20then%20it%20is%20injected%20and%20the%20return%20value%20is%20treated%20as%20the%20dependency.%20If%20the%20result%20is%20a%20promise%2C%20it%20is%20resolved%20before%20the%20controller%20is%20instantiated%20and%20its%20value%20is%20injected%20into%20the%20controller.%u3002%0A%0A%0A*Yaakov%u7684%u8BFE%u7A0B%u6B64%u5904%u53EF%u80FD%u6709%u9519%u8BB9%u3002%u5E76%u4E0D%u662F%u4EC0%u4E48%u90FD%u80FD%u5F80resolve%u91CC%u653E%uFF0C%u653E%u6570%u5B57%u4F1A%u62A5%u9519%uFF0C%u5177%u4F53%u53EF%u53C2%u770B%5BGitHub%20-%20angular-ui/ui-router%5D%28https%3A//github.com/angular-ui/ui-router/wiki%29*%0A%0A%u4E5F%u5C31%u662Ffactory%u53EA%u6709%u4E24%u79CD%u53EF%u80FD%uFF0Cstring%u6216%u8005function%uFF0C%u5982%u679C%u76F4%u63A5%u653E%u6570%u5B57%uFF0C%u4F1A%u51FA%u9519%u3002resolve%u6210%u5458%u4F1A%u5728controller%u5B9E%u4F8B%u5316%u4E4B%u524D%u88AB%u786E%u5B9A%u3002%u5982%u679Cpromise%u88ABreject%uFF0C%u5219%u4F1A%u53D6%u6D88%u72B6%u6001%u5207%u6362%uFF0C%u7136%u540E%u9519%u8BEF%u4F1A%u88AB%u9001%u5230%24stateChangeError%u3002resolve%u7684%u6210%u5458%uFF0C%u901A%u8FC7inject%u53EF%u4EE5%u88ABcontroller%u4F7F%u7528%uFF0C%u5982%u4E0B%3A%0A%60%60%60%0AView1Ctrl.%24inject%20%3D%20%5B%27myData%27%5D%3B%0Afunction%20View1Ctrl%28myData%29%20%7B%0A%09var%20view1%20%3D%20this%3B%0A%09view1.myData%20%3D%20myData%3B%0A%7D%20%0A%60%60%60%0A%0A%23%23%23%20URL%20Parameters%0A%60%60%60%0A.state%28%27view1%27%2C%20%7B%0A%09url%3A%20%27/view1/%7Bparam1%7D%27%2C%0A%09templateUrl%3A%20%27view1.html%27%2C%0A%09controller%3A%20%27View1Ctrl%20as%20view1%27%2C%0A%09resolve%3A%20%7B%0A%09%09myData%3A%20%5B%27%24stateParams%27%2C%0A%09%09%09function%20%28%24stateParams%29%20%7B%0A%09%09%09return%20getDataBasedOn%28%24stateParams.param1%29%3B%0A%09%09%7D%5D%0A%09%7D%0A%7D%29%3B%0A%60%60%60%0A%u8FD9%u662F%u6700%u6B63%u5E38%u7684%u5199%u6CD5%u3002%u5728HTML%u4E2D%u5982%u4E0B%u4F7F%u7528%uFF1A%0A%60%60%60%0A%3Ca%20ui-sref%3D%22view1%28%7BitemId%3AsomeVal%7D%29%22%3E%0A%09Link%20to%20view%20with%20data%0A%3C/a%3E%0A%60%60%60%0A%u6216%u8005%0A%60%60%60%0A%24state.go%28%27contacts%27%2C%20%7Bparam1%3A%20value1%7D%29%0A%60%60%60%0A%0A**%u6B63%u5219%u53C2%u6570**%0A%3EExamples%3A%0A%0A%3E-%20%60/user/%7Bid%3A%5B%5E/%5D*%7D%60%20-%20Same%20as%20%27/user/%7Bid%7D%27%20from%20the%20previous%20example.%0A%3E-%20%60/user/%7Bid%3A%5B0-9a-fA-F%5D%7B1%2C8%7D%7D%60%20-%20Similar%20to%20the%20previous%20example%2C%20but%20only%20matches%20if%20the%20id%20parameter%20consists%20of%201%20to%208%20hex%20digits.%0A%3E-%20%60/files/%7Bpath%3A.*%7D%60%20-%20Matches%20any%20URL%20starting%20with%20%27/files/%27%20and%20captures%20the%20rest%20of%20the%20path%20into%20the%20parameter%20%27path%27.%0A%3E-%20%60/files/*path%60%20-%20Ditto.%20Special%20syntax%20for%20catch%20all.%0A%0A*%u76EE%u524D%uFF0C%u5C1A%u672A%u5B9E%u8DF5%u8FC7%uFF0C%u731C%u6D4B%u662F%u5BF9URL%u8FDB%u884C%u6355%u83B7%uFF0C%u5982%u679C%u4E0D%u7B26%u5408%u8BE5%u6B63%u5219%u8868%u8FBE%u5F0F%uFF0C%u5219%u8BE5%u53C2%u6570%u4E0D%u5B58%u5728*%0A%0A**%u591A%u4E8E1%u4E2A%u53C2%u6570**%0A%u4F7F%u7528URL%u53C2%u6570%u5199%u6CD5%uFF1A%60url%3A%20%22/contacts%3FmyParam1%26myParam2%22%60%0A%0A**Using%20Parameters%20without%20Specifying%20Them%20in%20State%20URLs**%0A%60%60%60%0A.state%28%27contacts%27%2C%20%7B%0A%09url%3A%20%22/contacts%22%2C%0A%20%20%20%20params%3A%20%7B%0A%20%20%20%20%20%20%20%20param1%3A%20null%0A%20%20%20%20%7D%2C%0A%20%20%20%20templateUrl%3A%20%27contacts.html%27%0A%7D%29%0A%60%60%60%0A%0A%23%23%20Router%20State%20Transition%20Events%0A%24stateChangeStart%20-%20fires%20when%20state%20change%20transition%20begins%0A%24stateChangeSuccess%20-%20fired%20once%20the%20state%20transition%20is%20complete%0A%24stateChangeError%20-%20fires%20when%20an%20error%20occurs%20during%20transition%0Aevent.preventDefault%28%29%20-%20to%20prevent%20the%20transition%20from%20occurring%0A%0A%23%23%20State%20Inheritance%0A%u72B6%u6001%u4E5F%u53EF%u4EE5%u7EE7%u627F%uFF0C%u7EE7%u627F%u7684%u552F%u4E00%u76EE%u7684%u5C31%u662F%u4F7F%u7528%u5D4C%u5957%u7684ui-view%u3002%0A%60%60%60%0A.state%28%27view1%27%2C%20%7B%0A%09resolve%3A%20%7B%0A%09%09myData%3A%20%27someVal%27%3B%0A%09%7D%0A%09%u2026%0A%7D%29%0A.state%28%27view1.child%27%2C%20%7B%0A%09url%3A%20%27/detail/%7Bparam1%7D%27%2C%0A%09templateUrl%3A%20%27view1Detail.html%27%2C%0A%09controller%3A%20%22ChildCtrl%20as%20child%22%0A%09%u2026%0A%7D%29%3B%0AChildCtrl.%24inject%20%3D%20%5B%27myData%27%5D%3B%0Afunction%20ChildCtrl%20%28myData%29%20%7B%20%u2026%20%7D%0A%60%60%60%0A%u72B6%u6001%u7EE7%u627F%u7684%u65F6%u5019%uFF0Cresolve%u6210%u5458%u4E5F%u53EF%u4EE5%u7EE7%u627F%uFF0C%u5982%u4E0A%u3002%0A

Edit

Google Test,Google Mock以下简称gtest,gmock。
在接触gtest,gmock之前,测试C/C++ code使用UnitTest++。这是一个很简洁的框架,上手很快。参看另一篇博文UnitTest++简介。测试相关的功能够用,但是没有mock库。这带来的问题是:

  1. 测试遗留代码的时候,需要自行fake相关代码。这个在遗留系统很庞大时,要颇费心力。而且过多涉及细节,导致测试极不稳定,系统代码任意的演进,都会导致大堆的测试失败,甚至测试无法进行。
  2. 测试case之间无法很好的解耦。结果同样是测试不够稳定。术语是测试代码很“脆弱”。

Java,Python,JavaScript都有自己的mock库。Python的Mock类,Java的Mockito/PowerMock,JavaScript的Sinon。于是,在网上搜索了一下C/C++的Mock库,于是看到了gtest,gmock。然后就有了这一篇。

简介

不用去网上费心找教程,两个项目的文档都非常棒。入口统一在gtest GitHub项目主页上。而且该文档不仅很好的介绍了gtest,gmock的用法,其中还涉及了很多TDD或者UnitTest的真知灼见,很值得读一读。
要使用gtest非常简单:编译出gtest,gmock,再链入你的测试程序。

编译gtest/gmock

gtest,gmock均用cmake来管理跨平台,先用cmake来生成Makefile。用命令cmake -G "Unix Makefiles" /path/to/CMakeList.txt

Makefile

然后按照下面编写Makefile。注意gmock_main是一个main函数来调用所有的test case,省得自己写main函数了。

CC = gcc
CPP = g++
LINK = g++
CFLAGS = -g -Wall -Werror -Wextra -std=gnu99
CPPFLAGS = -g -Wall -Werror -Wextra
LIBS = -L./lib -lgtest -lgmock -lgmock_main -lpthread
C__SOURCES = $(wildcard *.c)
CPPSOURCES = $(wildcard *.cpp)
OBJECTS = $(patsubst %.c, %.o, $(C__SOURCES)) $(patsubst %.cpp, %.o, $(CPPSOURCES))
TARGET = test_exe
first: all
%.o: %.c
$(CC) $(INCLUDES) -c $(CFLAGS) -o $@ $<
%.o: %.cpp
$(CPP) $(INCLUDES) -c $(CPPFLAGS) -o $@ $<
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(LINK) $(CPPFLAGS) $(LIBS) -o $(TARGET) $(OBJECTS)
.PHONY : clean
clean:
rm -f $(TARGET) $(OBJECTS)

Terms

MeaningGoogle Test TermISTQB Term
Exercise a particular program path with specific input values and verify the resultsTEST()Test Case
A set of several tests related to one componentTest CaseTest Suite

Test

#include "gtest/gtest.h"
#include "gmock/gmock.h"
using ::testing::Return;
using ::testing::Test;
using ::testing::_;
using ::testing::AtLeast;
TEST(TestCaseName, should_this_test_do)
{
...
EXPECT_STREQ("{}", str);
}

中间的那堆namespace都是gtest/gmock库里定义的matcher宏或者各种有用的宏。

Test Fixture

在测试有重复的时候,就要用到Test Fixture了,也就是setUp / tearDown。

class QueueTest : public ::testing::Test {
protected:
virtual void SetUp() {
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// virtual void TearDown() {}
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
TEST_F(QueueTest, IsEmptyInitially) {
EXPECT_EQ(0, q0_.size());
}
TEST_F(QueueTest, DequeueWorks) {
int* n = q0_.Dequeue();
EXPECT_EQ(NULL, n);
n = q1_.Dequeue();
ASSERT_TRUE(n != NULL);
EXPECT_EQ(1, *n);
EXPECT_EQ(0, q1_.size());
delete n;
n = q2_.Dequeue();
ASSERT_TRUE(n != NULL);
EXPECT_EQ(2, *n);
EXPECT_EQ(1, q2_.size());
delete n;
}

constructor/destructor vs. SetUp/TearDown

When you need to write per-test set-up and tear-down logic, you have the choice between using the test fixture constructor/destructor or SetUp()/TearDown(). The former is usually preferred, as it has the following benefits:

  • By initializing a member variable in the constructor, we have the option to make it const, which helps prevent accidental changes to its value and makes the tests more obviously correct.
  • In case we need to subclass the test fixture class, the subclass’ constructor is guaranteed to call the base class’ constructor first, and the subclass’ destructor is guaranteed to call the base class’ destructor afterward. With SetUp()/TearDown(), a subclass may make the mistake of forgetting to call the base class’ SetUp()/TearDown() or call them at the wrong moment.

Benefit for using SetUp/TearDown:

  • If the tear-down operation could throw an exception, you must use TearDown() as opposed to the destructor, as throwing in a destructor leads to undefined behavior and usually will kill your program right away. Note that many standard libraries (like STL) may throw when exceptions are enabled in the compiler. Therefore you should prefer TearDown() if you want to write portable tests that work with or without exceptions.
  • The assertion macros throw an exception when flag –gtest_throw_on_failure is specified. Therefore, you shouldn’t use Google Test assertions in a destructor if you plan to run your tests with this flag.
  • In a constructor or destructor, you cannot make a virtual function call on this object. (You can call a method declared as virtual, but it will be statically bound.) Therefore, if you need to call a method that will be overriden in a derived class, you have to use SetUp()/TearDown().

简言之,在逻辑上,这两组的作用相同,都是每个测试之前之后会做一些处理工作。Constructor/Destructor的好处是提供了继承。setUp/tearDown的好处是可以处理exception,这是不能放在析构函数里的。

SetUpTestCase / TearDownTestCase

Test Case级别的SetUp/TearDown

class FooTest : public ::testing::Test {
protected:
// Per-test-case set-up.
// Called before the first test in this test case.
// Can be omitted if not needed.
static void SetUpTestCase() {
shared_resource_ = new ...;
}
// Per-test-case tear-down.
// Called after the last test in this test case.
// Can be omitted if not needed.
static void TearDownTestCase() {
delete shared_resource_;
shared_resource_ = NULL;
}
// You can define per-test set-up and tear-down logic as usual.
virtual void SetUp() { ... }
virtual void TearDown() { ... }
// Some expensive resource shared by all tests.
static T* shared_resource_;
};

SetUp/TearDown Environment

  • First, you subclass the ::testing::Environment class to define a test environment, which knows how to set-up and tear-down:
  • Then, you register an instance of your environment class with Google Test by calling the ::testing::AddGlobalTestEnvironment() function:
    Now, when RUN_ALL_TESTS() is called, it first calls the SetUp() method of the environment object, then runs the tests if there was no fatal failures, and finally calls TearDown() of the environment object.
  • It’s OK to register multiple environment objects. In this case, their SetUp() will be called in the order they are registered, and their TearDown() will be called in the reverse order.
  • Note that Google Test takes ownership of the registered environment objects. Therefore do not delete them by yourself.
class Environment {
public:
virtual ~Environment() {}
// Override this to define how to set up the environment.
virtual void SetUp() {}
// Override this to define how to tear down the environment.
virtual void TearDown() {}
};
Environment* AddGlobalTestEnvironment(Environment* env);

断言

有两种断言EXPECT_xxx和ASSERT_xxx。前者会让测试终止,后者不会,只会让测试fail。

gmock

之所以要切到gtest,唯一的原因就是gmock,所以要专开一章重点介绍一下。所有内容均来自于官方文档。内容深度由浅入深,依次如下:

最后还有参考手册:

简介

Google C++ Mocking Framework (or Google Mock for short) is a library (sometimes we also call it a “framework” to make it sound cool) for creating mock classes and using them. It does to C++ what jMock and EasyMock do to Java.

何为Mock?

Mocks are objects pre-programmed with expectations, which form a specification of the calls they are expected to receive.

相应的还有Fake和Stub

Fake objects have working implementations, but usually take some shortcut (perhaps to make the operations less expensive), which makes them not suitable for production. An in-memory file system would be an example of a fake.

gmock的文档里只提到了Fake,从Martin Fowler的文章Mocks Aren’t Stubs中摘录如下:

Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test.

三者作用相同,都是模拟系统其他部分的功能,达到代码隔离的效果,方便测试。但是Mock的特点是更OO化,也符合TDD或者BDD的思想——针对一个object设置期待,再对齐verify。

Mocks vs. Stubs - from Martin Fowler

In order to use state verification on the stub, I need to make some extra methods on the stub to help with verification. As a result the stub implements MailService but adds extra test methods.

Mock objects always use behavior verification, a stub can go either way. Meszaros refers to stubs that use behavior verification as a Test Spy. The difference is in how exactly the double runs and verifies and I’ll leave that for you to explore on your own.

Getting Started

Class to Mock

class Turtle {
...
virtual ~Turtle() {}
virtual void PenUp() = 0;
virtual void PenDown() = 0;
virtual void Forward(int distance) = 0;
};

Mock class

#include "gmock/gmock.h" // Brings in Google Mock.
class MockTurtle : public Turtle {
public:
...
MOCK_METHOD0(PenUp, void());
MOCK_METHOD0(PenDown, void());
MOCK_METHOD1(Forward, void(int distance));
};

针对待Mock的Turtle class要注意的是:

Note that the destructor of Turtle must be virtual, as is the case for all classes you intend to inherit from - otherwise the destructor of the derived class will not be called when you delete an object through a base pointer, and you’ll get corrupted program states like memory leaks.

Use it

#include "path/to/mock-turtle.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::AtLeast; // #1
TEST(PainterTest, CanDrawSomething) {
MockTurtle turtle; // #2
EXPECT_CALL(turtle, PenDown()) // #3
.Times(AtLeast(1));
Painter painter(&turtle); // #4
EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
} // #5
int main(int argc, char** argv) {
// The following line must be executed to initialize Google Mock
// (and Google Test) before running the tests.
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}

如果你愿意的话,可以自己写main,如果你想偷懒,记得在Makefile里link gmock_main。
按Google的说法,gmock也可以和其他测试框架兼容,但总感觉挺悬的。

一些有用的工具

Marcher
EXPECT_CALL(turtle, Forward(_));
     以任意参数调用Forward
EXPECT_CALL(turtle, Forward(Ge(100)));
     以大于100的整数调用Forward

Cardinalities: How Many Times Will It Be Called?

  • If neither WillOnce() nor WillRepeatedly() is in the EXPECT_CALL(), the inferred cardinality is Times(1).
  • If there are n WillOnce()’s but no WillRepeatedly(), where n >= 1, the cardinality is Times(n).
  • If there are n WillOnce()’s and one WillRepeatedly(), where n >= 0, the cardinality is Times(AtLeast(n)).
EXPECT_CALL(mockObj, func())
.Times(AtLeast(1))
.WillOnce(Return(123))
.WillRepeatedly(Return(456))

上面这段代码要求func函数至少运行一次,第一次返回123,之后每次返回456。

Important note: The EXPECT_CALL() statement evaluates the action clause only once, even though the action may be performed many times. Therefore you must be careful about side effects. The following may not do what you want:

int n = 100;
EXPECT_CALL(turtle, GetX())
.Times(4)
.WillRepeatedly(Return(n++));

因为Return是宏,所以只会替换一次,所以不管GetX调用几次,返回都是101,而不是101,102,103,…

All Expectations Are Sticky
所谓的sticky就是EXPECT_CALL总是生效的,除非你显示的将其失效。如下,所有的EXPECT_CALL都会生效,那么最后一个会覆盖前面所有的,也就是GetX总会返回10。

using ::testing::Return;
...
for (int i = n; i > 0; i--) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i));
}

如果希望他返回,30,20,10,…,应该这么写

using ::testing::Return;
...
for (int i = n; i > 0; i--) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}

RetireOnSaturation就是显示的让其失效。还有一个办法:

using ::testing::InSequence;
using ::testing::Return;
...
{
InSequence s;
for (int i = 1; i <= n; i++) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}
}

因为InSequence的关系,在前面的EXPECT_CALL生效以后,就会自动失效,所以不会产生覆盖的效果。
Ordered vs Unordered Calls

using ::testing::InSequence;...
TEST(FooTest, DrawsLineSegment) {
...
{
InSequence dummy;
EXPECT_CALL(turtle, PenDown());
EXPECT_CALL(turtle, Forward(100));
EXPECT_CALL(turtle, PenUp());
}
Foo();
}

Expecting Partially Ordered Calls
Google Mock allows you to impose an arbitrary DAG (directed acyclic graph) on the calls. One way to express the DAG is to
use the After clause of EXPECT_CALL .

using ::testing::Sequence;
Sequence s1, s2;
EXPECT_CALL(foo, A())
.InSequence(s1, s2);
EXPECT_CALL(bar, B())
.InSequence(s1);
EXPECT_CALL(bar, C())
.InSequence(s2);
EXPECT_CALL(foo, D())
.InSequence(s2);

specifies the following DAG (where s1 is A -> B , and s2 is A -> C -> D ):

+---> B
|
A ---|
|
+---> C ---> D

Uninteresting Calls
这是gmock报的warning。当针对某一个待测函数设置了EXPECT spec,却并没有调用的时候,就会报这个warning。此时gmock认为你对该函数并没有兴趣,所以就不需要这个EXPECT spec。当然你可以选择忽略这个warning,但我认为出这个warning的时候,多半是test漏写了什么。

Returning Live Values from Mock Methods

using testing::ByRef;
using testing::Return;
class MockFoo : public Foo {
public:
MOCK_METHOD0(GetValue, int());
};
...
int x = 0;
MockFoo foo;
EXPECT_CALL(foo, GetValue())
// .WillRepeatedly(Return(ByRef(x))); X error
.WillRepeatedly(ReturnPointee(x));
x = 42;
EXPECT_EQ(42, foo.GetValue());

一些测试case

写Unit Test并不像想像的那么简单,并不是调用了框架,针对每个函数写test case就可以。按我目前的理解有以下几种挑战:

  1. Test Case如何解耦。不要有重复测试(overlap)。
    例如:在写A函数的时候,写了测试testA,B函数会调用A函数,那么在写完A之后写B的测试testB时,是否要将A mock/fake/stub掉? 如果不将A函数Fake掉,则testA和testB之间就是有overlap。我认为这之间可以有取舍,最佳状态应当是此时,将testA删除,只保留testB。但仍应根据具体情况而定。

  2. 如何针对依赖关系进行mock化。
    例如出现这样的语句: B = new A,则类B依赖于类A。但类A并没有必要编译进test。因为一旦加入A,则势必会引入更多依赖关系,而导致test编译崩溃。依赖关系的解决无穷无尽。在做Android的单元测试时,可以用PowerMock取代Mockito来Mock构造函数,将构造函数Fake化成类似工厂函数,返回类实例。具体参看这篇博文Android单元测试
    但实际上,按照现在的理解,其实Mock构造函数是不可取的,首先造成被测代码spec不清晰,试想一个构造函数怎么会返回另一个类的实例。其次,在C++中很难做到Mock构造函数。好的做法应当是运用Dependency Injection。例如:

class A
{}
class B
{
void func()
{
A* a = new A;
}
}

类B应当改写为:

class B
{
void func(A*)
{
...
}
}

将类A指针传入,解决dependency的问题。

  1. 如何能够让测试稳定,在任意环境下均返回同样的测试结果。
    这个一般涉及测试环境的影响。例如调用网络相关的功能,在没有网络的环境就没法进行。再例如测试时操作真实具体文件,则该文件被测试外人为或代码修改,则测试可能就会莫名失败。
    针对这些情况,我们应当在测试中尽量避免。例如前者,我们应当对网络接口进行Mock化,后者应当在测试的setUp和tearDown中生成虚假文件用于测试,并在测试完成时做清理。

  2. 会因为很小的被测代码改动,而导致大面积测试失败,甚至测试崩溃。
    这个就是gmock文档中提到的要针对接口编程,针对接口测试。Robert C·Martin在《敏捷软件开发-原则、模式与实践》一书中有提出:所有的代码都应依赖于抽象接口。因为抽象接口是经过抽象的,相对具体的实现代码较为稳定。而被依赖的代码应该尽可能保持稳定,这样基于之上的代码才不会因为依赖的改动而改动。

下面列出几种我在实际写test case时遇到的情况,在gmock中的解决方案。

按照函数参数返回结果 - Fake

例如:

class A
{
virtual int func(int a, int b);
}
EXPECT_CALL(mockA, func())
.WillRepeatedly(a+b);

gmock中可以这样做:Using Functions/Methods/Functors as Actions

using ::testing::_;
using ::testing::Invoke;
class MockFoo : public Foo {
public:
MOCK_METHOD2(Sum, int(int x, int y));
MOCK_METHOD1(ComplexJob, bool(int x));
};
int CalculateSum(int x, int y) { return x + y; }
class Helper {
public:
bool ComplexJob(int x);
};
...
MockFoo foo;
Helper helper;
EXPECT_CALL(foo, Sum(_, _))
.WillOnce(Invoke(CalculateSum));
EXPECT_CALL(foo, ComplexJob(_))
.WillOnce(Invoke(&helper, &Helper::ComplexJob));
foo.Sum(5, 6); // Invokes CalculateSum(5, 6).
foo.ComplexJob(10); // Invokes helper.ComplexJob(10);

Mock non-virtual函数

// A simple packet stream class. None of its members is virtual.
class ConcretePacketStream {
public:
void AppendPacket(Packet* new_packet);
const Packet* GetPacket(size_t packet_number) const;
size_t NumberOfPackets() const;
...
};
// A mock packet stream class. It inherits from no other, but defines
// GetPacket() and NumberOfPackets().
class MockPacketStream {
public:
MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number));
MOCK_CONST_METHOD0(NumberOfPackets, size_t());
...
}
template <class PacketStream>
void CreateConnection(PacketStream* stream) { ... }
template <class PacketStream>
class PacketReader {
public:
void ReadPackets(PacketStream* stream, size_t packet_num);
};
MockPacketStream mock_stream;
EXPECT_CALL(mock_stream, ...)...;
.. set more expectations on mock_stream ...
PacketReader<MockPacketStream> reader(&mock_stream);
... exercise reader ...

为什么要这么做?
因为只能这么做。普通的mock,要通过继承被测试类,并重写virtual函数来实现。而上面的ConcretePacketStream和MockPacketStream并任何没有关系,也就是说,如果传入后者的指针,不用reinterpret_cast是不能转成前者的指针的。
所以想一个变通的办法,用模板类来定义被测代码,在测试时传入mock类,在生产时,传入真实类。

Mocking Side Effects

EXPECT_CALL(mutator, MutateInt(_))
.WillOnce(DoAll(SetArgPointee<0>(5), Return(true)));
EXPECT_CALL(mutator, Mutate(NotNull(), 5))
.WillOnce(SetArrayArgument<0>(values, values + 5));

第一个将MutateInt第一个参数指针指向的int,设为5,并返回true。
第二个将values数组的[0,5)拷贝到参数1指向的地址。
如果仍需要返回,则用DoAll,如下:

EXPECT_CALL(mutator, MutateInt(_))
.WillOnce(DoAll(SetArgPointee<0>(5),
Return(true)));

Selecting an Action’s Arguments

using ::testing::_;
using ::testing::Invoke;
bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y,
const map<pair<int, int>, double>& weight,
double min_weight, double max_wight) {
return IsVisibleInQuadrant1(visible, x, y);
}.
..
EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _))
.WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works.

定义自己的adaptor MyIsVisibleInQuadrant1,或者用gmock提供的方法优雅的解决。

using ::testing::_;
using ::testing::Invoke;
using ::testing::WithArgs;
...
EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _))
.WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1)));
// No need to define your own adaptor.

Mocking Private or Protected Methods

class Foo {
public:
...
virtual bool Transform(Gadget* g) = 0;
protected:
virtual void Resume();
private:
virtual int GetTimeOut();
};
class MockFoo : public Foo {
public:
...
MOCK_METHOD1(Transform, bool(Gadget* g));
// 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());
};

C++ allows a subclass to specify a different access level than the base class on a virtual function.

Misc

Keep in mind that one doesn’t have to verify more than one property in one test. In fact, it’s a good style to verify only one
thing in one test. If you do that, a bug will likely break only one or two tests instead of dozens

When it’s being destroyed, your friendly mock object will automatically verify that all expectations on it have been satisfied,
and will generate Google Test failures if not.

Currently these are only platforms that support the pthreads library (this includes Linux and Mac).

加上命令行参数–gmock_verbose=info可以显示所有EXPECT_CALL的具体调用情况。

Some useful tips in gtest

Selecting Tests

If you set the GTEST_FILTER environment variable or the –gtest_filter flag to a filter string, Google Test will only run the tests whose full names (in the form of TestCaseName.TestName) match the filter.
The format of a filter is a ‘:’-separated list of wildcard patterns (called the positive patterns) optionally followed by a ‘-’ and another ‘:’-separated pattern list (called the negative patterns).

  • ./foo_test Has no flag, and thus runs all its tests.
  • ./foo_test –gtest_filter=* Also runs everything, due to the single match-everything * value.
  • ./foo_test –gtest_filter=FooTest.* Runs everything in test case FooTest.
  • ./foo_test –gtest_filter=Null:Constructor Runs any test whose full name contains either “Null” or “Constructor”.
  • ./foo_test –gtest_filter=-DeathTest. Runs all non-death tests.
  • ./foo_test –gtest_filter=FooTest.*-FooTest.Bar Runs everything in test case FooTest except FooTest.Bar

Temporarily Disabling Tests

// Tests that Foo does Abc.
TEST(FooTest, DISABLED_DoesAbc) { ... }
class DISABLED_BarTest : public ::testing::Test { ... };
// Tests that Bar does Xyz.
TEST_F(DISABLED_BarTest, DoesXyz) { ... }

Temporarily Enabling Disabled Tests

just invoke the test program with the –gtest_also_run_disabled_tests flag or set the GTEST_ALSO_RUN_DISABLED_TESTS environment variable to a value other than 0.

Repeating the Tests

$ foo_test –gtest_repeat=1000Repeat foo_test 1000 times and don’t stop at failures.
$ foo_test –gtest_repeat=-1A negative count means repeating forever.
$ foo_test –gtest_repeat=1000 –gtest_break_on_failureRepeat foo_test 1000 times, stopping at the first failure. This is especially useful when running under a debugger: when the testfails, it will drop into the debugger and you can then inspect variables and stacks.
$ foo_test –gtest_repeat=1000 –gtest_filter=FooBarRepeat the tests whose name matches the filter 1000 times.
%23%20C/C++%20Test%20Framework%20-%20Google%20Test%20%26%20Google%20Mock%0A@%28myblog%29%5BTDD%2C%20gtest%2C%20gmock%5D%0A%0AGoogle%20Test%uFF0CGoogle%20Mock%u4EE5%u4E0B%u7B80%u79F0gtest%uFF0Cgmock%u3002%0A%u5728%u63A5%u89E6gtest%uFF0Cgmock%u4E4B%u524D%uFF0C%u6D4B%u8BD5C/C++%20code%u4F7F%u7528%5BUnitTest++%5D%28https%3A//github.com/unittest-cpp/unittest-cpp%29%u3002%u8FD9%u662F%u4E00%u4E2A%u5F88%u7B80%u6D01%u7684%u6846%u67B6%uFF0C%u4E0A%u624B%u5F88%u5FEB%u3002%u53C2%u770B%u53E6%u4E00%u7BC7%u535A%u6587%5BUnitTest++%u7B80%u4ECB%5D%28https%3A//zhougy0717.github.io/2016/02/29/UnitTest++%25E7%25AE%2580%25E4%25BB%258B/%29%u3002%u6D4B%u8BD5%u76F8%u5173%u7684%u529F%u80FD%u591F%u7528%uFF0C%u4F46%u662F%u6CA1%u6709mock%u5E93%u3002%u8FD9%u5E26%u6765%u7684%u95EE%u9898%u662F%uFF1A%0A1.%20%u6D4B%u8BD5%u9057%u7559%u4EE3%u7801%u7684%u65F6%u5019%uFF0C%u9700%u8981%u81EA%u884Cfake%u76F8%u5173%u4EE3%u7801%u3002%u8FD9%u4E2A%u5728%u9057%u7559%u7CFB%u7EDF%u5F88%u5E9E%u5927%u65F6%uFF0C%u8981%u9887%u8D39%u5FC3%u529B%u3002%u800C%u4E14%u8FC7%u591A%u6D89%u53CA%u7EC6%u8282%uFF0C%u5BFC%u81F4%u6D4B%u8BD5%u6781%u4E0D%u7A33%u5B9A%uFF0C%u7CFB%u7EDF%u4EE3%u7801%u4EFB%u610F%u7684%u6F14%u8FDB%uFF0C%u90FD%u4F1A%u5BFC%u81F4%u5927%u5806%u7684%u6D4B%u8BD5%u5931%u8D25%uFF0C%u751A%u81F3%u6D4B%u8BD5%u65E0%u6CD5%u8FDB%u884C%u3002%0A2.%20%u6D4B%u8BD5case%u4E4B%u95F4%u65E0%u6CD5%u5F88%u597D%u7684%u89E3%u8026%u3002%u7ED3%u679C%u540C%u6837%u662F%u6D4B%u8BD5%u4E0D%u591F%u7A33%u5B9A%u3002%u672F%u8BED%u662F%u6D4B%u8BD5%u4EE3%u7801%u5F88%u201C%u8106%u5F31%u201D%u3002%0A%0AJava%uFF0CPython%uFF0CJavaScript%u90FD%u6709%u81EA%u5DF1%u7684mock%u5E93%u3002Python%u7684Mock%u7C7B%uFF0CJava%u7684Mockito/PowerMock%uFF0CJavaScript%u7684Sinon%u3002%u4E8E%u662F%uFF0C%u5728%u7F51%u4E0A%u641C%u7D22%u4E86%u4E00%u4E0BC/C++%u7684Mock%u5E93%uFF0C%u4E8E%u662F%u770B%u5230%u4E86gtest%uFF0Cgmock%u3002%u7136%u540E%u5C31%u6709%u4E86%u8FD9%u4E00%u7BC7%u3002%0A%0A%23%23%20%u7B80%u4ECB%0A%u4E0D%u7528%u53BB%u7F51%u4E0A%u8D39%u5FC3%u627E%u6559%u7A0B%uFF0C%u4E24%u4E2A%u9879%u76EE%u7684%u6587%u6863%u90FD%u975E%u5E38%u68D2%u3002%u5165%u53E3%u7EDF%u4E00%u5728%5Bgtest%20GitHub%u9879%u76EE%u4E3B%u9875%5D%28https%3A//github.com/google/googletest%29%u4E0A%u3002%u800C%u4E14%u8BE5%u6587%u6863%u4E0D%u4EC5%u5F88%u597D%u7684%u4ECB%u7ECD%u4E86gtest%uFF0Cgmock%u7684%u7528%u6CD5%uFF0C%u5176%u4E2D%u8FD8%u6D89%u53CA%u4E86%u5F88%u591ATDD%u6216%u8005UnitTest%u7684%u771F%u77E5%u707C%u89C1%uFF0C%u5F88%u503C%u5F97%u8BFB%u4E00%u8BFB%u3002%0A%u8981%u4F7F%u7528gtest%u975E%u5E38%u7B80%u5355%uFF1A%u7F16%u8BD1%u51FAgtest%uFF0Cgmock%uFF0C%u518D%u94FE%u5165%u4F60%u7684%u6D4B%u8BD5%u7A0B%u5E8F%u3002%0A%0A%23%23%23%20%u7F16%u8BD1gtest/gmock%0Agtest%uFF0Cgmock%u5747%u7528cmake%u6765%u7BA1%u7406%u8DE8%u5E73%u53F0%uFF0C%u5148%u7528cmake%u6765%u751F%u6210Makefile%u3002%u7528%u547D%u4EE4%60cmake%20-G%20%22Unix%20Makefiles%22%20/path/to/CMakeList.txt%60%0A%0A%23%23%23%20Makefile%0A%u7136%u540E%u6309%u7167%u4E0B%u9762%u7F16%u5199Makefile%u3002%u6CE8%u610Fgmock_main%u662F%u4E00%u4E2Amain%u51FD%u6570%u6765%u8C03%u7528%u6240%u6709%u7684test%20case%uFF0C%u7701%u5F97%u81EA%u5DF1%u5199main%u51FD%u6570%u4E86%u3002%0A%60%60%60%0ACC%20%3D%20gcc%0ACPP%20%3D%20g++%0ALINK%20%3D%20g++%0ACFLAGS%20%3D%20-g%20-Wall%20-Werror%20-Wextra%20-std%3Dgnu99%0ACPPFLAGS%20%3D%20-g%20-Wall%20-Werror%20-Wextra%0ALIBS%20%3D%20-L./lib%20-lgtest%20-lgmock%20-lgmock_main%20-lpthread%0A%0AC__SOURCES%20%3D%20%24%28wildcard%20*.c%29%0ACPPSOURCES%20%3D%20%24%28wildcard%20*.cpp%29%0AOBJECTS%20%3D%20%24%28patsubst%20%25.c%2C%20%25.o%2C%20%24%28C__SOURCES%29%29%20%24%28patsubst%20%25.cpp%2C%20%25.o%2C%20%24%28CPPSOURCES%29%29%0ATARGET%20%3D%20test_exe%0A%0Afirst%3A%20all%0A%0A%25.o%3A%20%25.c%0A%20%20%20%20%24%28CC%29%20%24%28INCLUDES%29%20-c%20%24%28CFLAGS%29%20-o%20%24@%20%24%3C%0A%0A%25.o%3A%20%25.cpp%0A%20%20%20%20%24%28CPP%29%20%24%28INCLUDES%29%20-c%20%24%28CPPFLAGS%29%20-o%20%24@%20%24%3C%0A%0Aall%3A%20%24%28TARGET%29%0A%0A%24%28TARGET%29%3A%20%24%28OBJECTS%29%0A%20%20%20%20%24%28LINK%29%20%24%28CPPFLAGS%29%20%24%28LIBS%29%20-o%20%24%28TARGET%29%20%24%28OBJECTS%29%0A%0A.PHONY%20%3A%20clean%0A%0Aclean%3A%0A%20%20%20%20rm%20-f%20%24%28TARGET%29%20%24%28OBJECTS%29%0A%60%60%60%0A%0A%23%23%23%20Terms%0A%7C%20Meaning%20%20%20%20%7C%20Google%20Test%20Term%20%7C%20ISTQB%20Term%20%7C%0A%7C%20%3A--------%3A%20%7C%20%3A--------------%3A%20%7C%20%3A--------%3A%20%7C%0A%7C%20Exercise%20a%20particular%20program%20path%20with%20specific%20input%20values%20and%20verify%20the%20results%20%7C%20TEST%28%29%20%7C%20Test%20Case%0A%7CA%20set%20of%20several%20tests%20related%20to%20one%20component%20%7C%20Test%20Case%20%7C%20Test%20Suite%0A%0A%23%23%23%20Test%0A%60%60%60%0A%23include%20%22gtest/gtest.h%22%0A%23include%20%22gmock/gmock.h%22%0A%0Ausing%20%3A%3Atesting%3A%3AReturn%3B%0Ausing%20%3A%3Atesting%3A%3ATest%3B%0Ausing%20%3A%3Atesting%3A%3A_%3B%0Ausing%20%3A%3Atesting%3A%3AAtLeast%3B%0A%0ATEST%28TestCaseName%2C%20should_this_test_do%29%0A%7B%0A%09...%0A%20%20%20%20EXPECT_STREQ%28%22%7B%7D%22%2C%20str%29%3B%0A%7D%0A%60%60%60%0A%u4E2D%u95F4%u7684%u90A3%u5806namespace%u90FD%u662Fgtest/gmock%u5E93%u91CC%u5B9A%u4E49%u7684matcher%u5B8F%u6216%u8005%u5404%u79CD%u6709%u7528%u7684%u5B8F%u3002%0A%0A%23%23%23%20Test%20Fixture%0A%u5728%u6D4B%u8BD5%u6709%u91CD%u590D%u7684%u65F6%u5019%uFF0C%u5C31%u8981%u7528%u5230Test%20Fixture%u4E86%uFF0C%u4E5F%u5C31%u662FsetUp%20/%20tearDown%u3002%0A%60%60%60%0Aclass%20QueueTest%20%3A%20public%20%3A%3Atesting%3A%3ATest%20%7B%0A%20protected%3A%0A%20%20virtual%20void%20SetUp%28%29%20%7B%0A%20%20%20%20q1_.Enqueue%281%29%3B%0A%20%20%20%20q2_.Enqueue%282%29%3B%0A%20%20%20%20q2_.Enqueue%283%29%3B%0A%20%20%7D%0A%0A%20%20//%20virtual%20void%20TearDown%28%29%20%7B%7D%0A%0A%20%20Queue%3Cint%3E%20q0_%3B%0A%20%20Queue%3Cint%3E%20q1_%3B%0A%20%20Queue%3Cint%3E%20q2_%3B%0A%7D%3B%0A%0ATEST_F%28QueueTest%2C%20IsEmptyInitially%29%20%7B%0A%20%20EXPECT_EQ%280%2C%20q0_.size%28%29%29%3B%0A%7D%0A%0ATEST_F%28QueueTest%2C%20DequeueWorks%29%20%7B%0A%20%20int*%20n%20%3D%20q0_.Dequeue%28%29%3B%0A%20%20EXPECT_EQ%28NULL%2C%20n%29%3B%0A%0A%20%20n%20%3D%20q1_.Dequeue%28%29%3B%0A%20%20ASSERT_TRUE%28n%20%21%3D%20NULL%29%3B%0A%20%20EXPECT_EQ%281%2C%20*n%29%3B%0A%20%20EXPECT_EQ%280%2C%20q1_.size%28%29%29%3B%0A%20%20delete%20n%3B%0A%0A%20%20n%20%3D%20q2_.Dequeue%28%29%3B%0A%20%20ASSERT_TRUE%28n%20%21%3D%20NULL%29%3B%0A%20%20EXPECT_EQ%282%2C%20*n%29%3B%0A%20%20EXPECT_EQ%281%2C%20q2_.size%28%29%29%3B%0A%20%20delete%20n%3B%0A%7D%0A%60%60%60%0A%23%23%23%23%20constructor/destructor%20vs.%20SetUp/TearDown%0A%3EWhen%20you%20need%20to%20write%20per-test%20set-up%20and%20tear-down%20logic%2C%20you%20have%20the%20choice%20between%20using%20the%20test%20fixture%20constructor/destructor%20or%20SetUp%28%29/TearDown%28%29.%20The%20former%20is%20usually%20preferred%2C%20as%20it%20has%20the%20following%20benefits%3A%0A%0A%3E-%20By%20initializing%20a%20member%20variable%20in%20the%20constructor%2C%20we%20have%20the%20option%20to%20make%20it%20const%2C%20which%20helps%20prevent%20accidental%20changes%20to%20its%20value%20and%20makes%20the%20tests%20more%20obviously%20correct.%0A-%20**In%20case%20we%20need%20to%20subclass%20the%20test%20fixture%20class%2C%20the%20subclass%27%20constructor%20is%20guaranteed%20to%20call%20the%20base%20class%27%20constructor%20first%2C%20and%20the%20subclass%27%20destructor%20is%20guaranteed%20to%20call%20the%20base%20class%27%20destructor%20afterward.%20**With%20SetUp%28%29/TearDown%28%29%2C%20a%20subclass%20may%20make%20the%20mistake%20of%20forgetting%20to%20call%20the%20base%20class%27%20SetUp%28%29/TearDown%28%29%20or%20call%20them%20at%20the%20wrong%20moment.%0A%0A%3E%20Benefit%20for%20using%20SetUp/TearDown%3A%0A%0A%3E-%20If%20the%20tear-down%20operation%20could%20throw%20an%20exception%2C%20you%20must%20use%20TearDown%28%29%20as%20opposed%20to%20the%20destructor%2C%20as%20throwing%20in%20a%20destructor%20leads%20to%20undefined%20behavior%20and%20usually%20will%20kill%20your%20program%20right%20away.%20Note%20that%20many%20standard%20libraries%20%28like%20STL%29%20may%20throw%20when%20exceptions%20are%20enabled%20in%20the%20compiler.%20Therefore%20you%20should%20prefer%20TearDown%28%29%20if%20you%20want%20to%20write%20portable%20tests%20that%20work%20with%20or%20without%20exceptions.%0A-%20The%20assertion%20macros%20throw%20an%20exception%20when%20flag%20--gtest_throw_on_failure%20is%20specified.%20Therefore%2C%20you%20shouldn%27t%20use%20Google%20Test%20assertions%20in%20a%20destructor%20if%20you%20plan%20to%20run%20your%20tests%20with%20this%20flag.%0A-%20In%20a%20constructor%20or%20destructor%2C%20you%20cannot%20make%20a%20virtual%20function%20call%20on%20this%20object.%20%28You%20can%20call%20a%20method%20declared%20as%20virtual%2C%20but%20it%20will%20be%20statically%20bound.%29%20Therefore%2C%20if%20you%20need%20to%20call%20a%20method%20that%20will%20be%20overriden%20in%20a%20derived%20class%2C%20you%20have%20to%20use%20SetUp%28%29/TearDown%28%29.%0A%0A%u7B80%u8A00%u4E4B%uFF0C%u5728%u903B%u8F91%u4E0A%uFF0C%u8FD9%u4E24%u7EC4%u7684%u4F5C%u7528%u76F8%u540C%uFF0C%u90FD%u662F%u6BCF%u4E2A%u6D4B%u8BD5%u4E4B%u524D%u4E4B%u540E%u4F1A%u505A%u4E00%u4E9B%u5904%u7406%u5DE5%u4F5C%u3002Constructor/Destructor%u7684%u597D%u5904%u662F%u63D0%u4F9B%u4E86%u7EE7%u627F%u3002setUp/tearDown%u7684%u597D%u5904%u662F%u53EF%u4EE5%u5904%u7406exception%uFF0C%u8FD9%u662F%u4E0D%u80FD%u653E%u5728%u6790%u6784%u51FD%u6570%u91CC%u7684%u3002%0A%23%23%23%20SetUpTestCase%20/%20TearDownTestCase%0ATest%20Case%u7EA7%u522B%u7684SetUp/TearDown%0A%60%60%60%0Aclass%20FooTest%20%3A%20public%20%3A%3Atesting%3A%3ATest%20%7B%0A%20protected%3A%0A%20%20//%20Per-test-case%20set-up.%0A%20%20//%20Called%20before%20the%20first%20test%20in%20this%20test%20case.%0A%20%20//%20Can%20be%20omitted%20if%20not%20needed.%0A%20%20static%20void%20SetUpTestCase%28%29%20%7B%0A%20%20%20%20shared_resource_%20%3D%20new%20...%3B%0A%20%20%7D%0A%0A%20%20//%20Per-test-case%20tear-down.%0A%20%20//%20Called%20after%20the%20last%20test%20in%20this%20test%20case.%0A%20%20//%20Can%20be%20omitted%20if%20not%20needed.%0A%20%20static%20void%20TearDownTestCase%28%29%20%7B%0A%20%20%20%20delete%20shared_resource_%3B%0A%20%20%20%20shared_resource_%20%3D%20NULL%3B%0A%20%20%7D%0A%0A%20%20//%20You%20can%20define%20per-test%20set-up%20and%20tear-down%20logic%20as%20usual.%0A%20%20virtual%20void%20SetUp%28%29%20%7B%20...%20%7D%0A%20%20virtual%20void%20TearDown%28%29%20%7B%20...%20%7D%0A%0A%20%20//%20Some%20expensive%20resource%20shared%20by%20all%20tests.%0A%20%20static%20T*%20shared_resource_%3B%0A%7D%3B%0A%60%60%60%0A%23%23%23%20SetUp/TearDown%20Environment%20%0A%3E-%20First%2C%20you%20subclass%20the%20%3A%3Atesting%3A%3AEnvironment%20class%20to%20define%20a%20test%20environment%2C%20which%20knows%20how%20to%20set-up%20and%20tear-down%3A%0A%3E-%20Then%2C%20you%20register%20an%20instance%20of%20your%20environment%20class%20with%20Google%20Test%20by%20calling%20the%20%60%3A%3Atesting%3A%3AAddGlobalTestEnvironment%28%29%60%20function%3A%0A%3ENow%2C%20when%20RUN_ALL_TESTS%28%29%20is%20called%2C%20it%20first%20calls%20the%20SetUp%28%29%20method%20of%20the%20environment%20object%2C%20then%20runs%20the%20tests%20if%20there%20was%20no%20fatal%20failures%2C%20and%20finally%20calls%20TearDown%28%29%20of%20the%20environment%20object.%0A-%20It%27s%20OK%20to%20register%20multiple%20environment%20objects.%20In%20this%20case%2C%20their%20SetUp%28%29%20will%20be%20called%20in%20the%20order%20they%20are%20registered%2C%20and%20their%20TearDown%28%29%20will%20be%20called%20in%20the%20reverse%20order.%0A-%20Note%20that%20Google%20Test%20takes%20ownership%20of%20the%20registered%20environment%20objects.%20Therefore%20do%20not%20delete%20them%20by%20yourself.%0A%0A%60%60%60%0Aclass%20Environment%20%7B%0A%20public%3A%0A%20%20virtual%20%7EEnvironment%28%29%20%7B%7D%0A%20%20//%20Override%20this%20to%20define%20how%20to%20set%20up%20the%20environment.%0A%20%20virtual%20void%20SetUp%28%29%20%7B%7D%0A%20%20//%20Override%20this%20to%20define%20how%20to%20tear%20down%20the%20environment.%0A%20%20virtual%20void%20TearDown%28%29%20%7B%7D%0A%7D%3B%0A%0AEnvironment*%20AddGlobalTestEnvironment%28Environment*%20env%29%3B%0A%60%60%60%0A%23%23%23%20%u65AD%u8A00%0A%u6709%u4E24%u79CD%u65AD%u8A00EXPECT%5C_xxx%u548CASSERT%5C_xxx%u3002%u524D%u8005%u4F1A%u8BA9%u6D4B%u8BD5%u7EC8%u6B62%uFF0C%u540E%u8005%u4E0D%u4F1A%uFF0C%u53EA%u4F1A%u8BA9%u6D4B%u8BD5fail%u3002%0A%0A%0A%23%23%20gmock%0A%u4E4B%u6240%u4EE5%u8981%u5207%u5230gtest%uFF0C%u552F%u4E00%u7684%u539F%u56E0%u5C31%u662Fgmock%uFF0C%u6240%u4EE5%u8981%u4E13%u5F00%u4E00%u7AE0%u91CD%u70B9%u4ECB%u7ECD%u4E00%u4E0B%u3002%u6240%u6709%u5185%u5BB9%u5747%u6765%u81EA%u4E8E%u5B98%u65B9%u6587%u6863%u3002%u5185%u5BB9%u6DF1%u5EA6%u7531%u6D45%u5165%u6DF1%uFF0C%u4F9D%u6B21%u5982%u4E0B%uFF1A%0A-%20%5BGoogle%20Mock%20for%20Dummies%5D%28https%3A//github.com/google/googletest/blob/master/googlemock/docs/ForDummies.md%29%0A-%20%5BCookBook%5D%28https%3A//github.com/google/googletest/blob/master/googlemock/docs/CookBook.md%29%0A%0A%u6700%u540E%u8FD8%u6709%u53C2%u8003%u624B%u518C%3A%0A-%20%5BCheat%20Sheet%5D%28https%3A//github.com/google/googletest/blob/master/googlemock/docs/CheatSheet.md%29%0A%0A%23%23%23%20%u7B80%u4ECB%0A%3EGoogle%20C++%20Mocking%20Framework%20%28or%20Google%20Mock%20for%20short%29%20is%20a%20library%20%28sometimes%20we%20also%20call%20it%20a%20%22framework%22%20to%20make%20it%20sound%20cool%29%20for%20creating%20mock%20classes%20and%20using%20them.%20It%20does%20to%20C++%20what%20jMock%20and%20EasyMock%20do%20to%20Java.%0A%0A%23%23%23%23%20%u4F55%u4E3AMock%uFF1F%0A%3E**Mocks**%20are%20objects%20pre-programmed%20with%20expectations%2C%20which%20form%20a%20specification%20of%20the%20calls%20they%20are%20expected%20to%20receive.%0A%0A%u76F8%u5E94%u7684%u8FD8%u6709Fake%u548CStub%0A%3E**Fake**%20objects%20have%20working%20implementations%2C%20but%20usually%20take%20some%20shortcut%20%28perhaps%20to%20make%20the%20operations%20less%20expensive%29%2C%20which%20makes%20them%20not%20suitable%20for%20production.%20An%20in-memory%20file%20system%20would%20be%20an%20example%20of%20a%20fake.%0A%0Agmock%u7684%u6587%u6863%u91CC%u53EA%u63D0%u5230%u4E86Fake%uFF0C%u4ECEMartin%20Fowler%u7684%u6587%u7AE0%5BMocks%20Aren%27t%20Stubs%5D%28https%3A//martinfowler.com/articles/mocksArentStubs.html%29%u4E2D%u6458%u5F55%u5982%u4E0B%3A%0A%3E**Stubs**%20provide%20canned%20answers%20to%20calls%20made%20during%20the%20test%2C%20usually%20not%20responding%20at%20all%20to%20anything%20outside%20what%27s%20programmed%20in%20for%20the%20test.%0A%0A%u4E09%u8005%u4F5C%u7528%u76F8%u540C%uFF0C%u90FD%u662F%u6A21%u62DF%u7CFB%u7EDF%u5176%u4ED6%u90E8%u5206%u7684%u529F%u80FD%uFF0C%u8FBE%u5230%u4EE3%u7801%u9694%u79BB%u7684%u6548%u679C%uFF0C%u65B9%u4FBF%u6D4B%u8BD5%u3002%u4F46%u662FMock%u7684%u7279%u70B9%u662F%u66F4OO%u5316%uFF0C%u4E5F%u7B26%u5408TDD%u6216%u8005BDD%u7684%u601D%u60F3%u2014%u2014%u9488%u5BF9%u4E00%u4E2Aobject%u8BBE%u7F6E%u671F%u5F85%uFF0C%u518D%u5BF9%u9F50verify%u3002%0A%0AMocks%20vs.%20Stubs%20-%20from%20Martin%20Fowler%0A%3EIn%20order%20to%20use%20state%20verification%20on%20the%20stub%2C%20I%20need%20to%20make%20some%20extra%20methods%20on%20the%20stub%20to%20help%20with%20verification.%20As%20a%20result%20the%20stub%20implements%20MailService%20but%20adds%20extra%20test%20methods.%0A%0A%3EMock%20objects%20always%20use%20behavior%20verification%2C%20a%20stub%20can%20go%20either%20way.%20Meszaros%20refers%20to%20stubs%20that%20use%20behavior%20verification%20as%20a%20Test%20Spy.%20The%20difference%20is%20in%20how%20exactly%20the%20double%20runs%20and%20verifies%20and%20I%27ll%20leave%20that%20for%20you%20to%20explore%20on%20your%20own.%0A%0A%23%23%23%23%20Getting%20Started%0AClass%20to%20Mock%0A%60%60%60%0Aclass%20Turtle%20%7B%0A%20%20...%0A%20%20virtual%20%7ETurtle%28%29%20%7B%7D%0A%20%20virtual%20void%20PenUp%28%29%20%3D%200%3B%0A%20%20virtual%20void%20PenDown%28%29%20%3D%200%3B%0A%20%20virtual%20void%20Forward%28int%20distance%29%20%3D%200%3B%0A%7D%3B%0A%60%60%60%0AMock%20class%0A%60%60%60%0A%23include%20%22gmock/gmock.h%22%20%20//%20Brings%20in%20Google%20Mock.%0Aclass%20MockTurtle%20%3A%20public%20Turtle%20%7B%0A%20public%3A%0A%20%20...%0A%20%20MOCK_METHOD0%28PenUp%2C%20void%28%29%29%3B%0A%20%20MOCK_METHOD0%28PenDown%2C%20void%28%29%29%3B%0A%20%20MOCK_METHOD1%28Forward%2C%20void%28int%20distance%29%29%3B%0A%7D%3B%0A%60%60%60%0A%u9488%u5BF9%u5F85Mock%u7684Turtle%20class%u8981%u6CE8%u610F%u7684%u662F%3A%0A%3ENote%20that%20the%20**destructor%20of%20Turtle%20must%20be%20virtual**%2C%20as%20is%20the%20case%20for%20all%20classes%20you%20intend%20to%20inherit%20from%20-%20otherwise%20the%20destructor%20of%20the%20derived%20class%20will%20not%20be%20called%20when%20you%20delete%20an%20object%20through%20a%20base%20pointer%2C%20and%20you%27ll%20get%20corrupted%20program%20states%20like%20memory%20leaks.%0A%0AUse%20it%0A%60%60%60%0A%23include%20%22path/to/mock-turtle.h%22%0A%23include%20%22gmock/gmock.h%22%0A%23include%20%22gtest/gtest.h%22%0Ausing%20%3A%3Atesting%3A%3AAtLeast%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%231%0A%0ATEST%28PainterTest%2C%20CanDrawSomething%29%20%7B%0A%20%20MockTurtle%20turtle%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%232%0A%20%20EXPECT_CALL%28turtle%2C%20PenDown%28%29%29%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%233%0A%20%20%20%20%20%20.Times%28AtLeast%281%29%29%3B%0A%0A%20%20Painter%20painter%28%26turtle%29%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%234%0A%0A%20%20EXPECT_TRUE%28painter.DrawCircle%280%2C%200%2C%2010%29%29%3B%0A%7D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%235%0A%0Aint%20main%28int%20argc%2C%20char**%20argv%29%20%7B%0A%20%20//%20The%20following%20line%20must%20be%20executed%20to%20initialize%20Google%20Mock%0A%20%20//%20%28and%20Google%20Test%29%20before%20running%20the%20tests.%0A%20%20%3A%3Atesting%3A%3AInitGoogleMock%28%26argc%2C%20argv%29%3B%0A%20%20return%20RUN_ALL_TESTS%28%29%3B%0A%7D%0A%60%60%60%0A%u5982%u679C%u4F60%u613F%u610F%u7684%u8BDD%uFF0C%u53EF%u4EE5%u81EA%u5DF1%u5199main%uFF0C%u5982%u679C%u4F60%u60F3%u5077%u61D2%uFF0C%u8BB0%u5F97%u5728Makefile%u91CClink%20gmock_main%u3002%0A%u6309Google%u7684%u8BF4%u6CD5%uFF0Cgmock%u4E5F%u53EF%u4EE5%u548C%u5176%u4ED6%u6D4B%u8BD5%u6846%u67B6%u517C%u5BB9%uFF0C%u4F46%u603B%u611F%u89C9%u633A%u60AC%u7684%u3002%0A%0A%23%23%23%23%20%u4E00%u4E9B%u6709%u7528%u7684%u5DE5%u5177%0A**Marcher**%0A%60EXPECT_CALL%28turtle%2C%20Forward%28_%29%29%3B%60%0A%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%u4EE5%u4EFB%u610F%u53C2%u6570%u8C03%u7528Forward%0A%60EXPECT_CALL%28turtle%2C%20Forward%28Ge%28100%29%29%29%3B%60%0A%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%u4EE5%u5927%u4E8E100%u7684%u6574%u6570%u8C03%u7528Forward%0A%09%0A**Cardinalities%3A%20How%20Many%20Times%20Will%20It%20Be%20Called%3F**%0A%3E-%20If%20neither%20WillOnce%28%29%20nor%20WillRepeatedly%28%29%20is%20in%20the%20EXPECT_CALL%28%29%2C%20the%20inferred%20cardinality%20is%20Times%281%29.%0A-%20If%20there%20are%20n%20WillOnce%28%29%27s%20but%20no%20WillRepeatedly%28%29%2C%20where%20n%20%3E%3D%201%2C%20the%20cardinality%20is%20Times%28n%29.%0A-%20If%20there%20are%20n%20WillOnce%28%29%27s%20and%20one%20WillRepeatedly%28%29%2C%20where%20n%20%3E%3D%200%2C%20the%20cardinality%20is%20Times%28AtLeast%28n%29%29.%0A%0A%60%60%60%0AEXPECT_CALL%28mockObj%2C%20func%28%29%29%0A%09.Times%28AtLeast%281%29%29%0A%09.WillOnce%28Return%28123%29%29%0A%09.WillRepeatedly%28Return%28456%29%29%0A%60%60%60%0A%u4E0A%u9762%u8FD9%u6BB5%u4EE3%u7801%u8981%u6C42func%u51FD%u6570%u81F3%u5C11%u8FD0%u884C%u4E00%u6B21%uFF0C%u7B2C%u4E00%u6B21%u8FD4%u56DE123%uFF0C%u4E4B%u540E%u6BCF%u6B21%u8FD4%u56DE456%u3002%0A%0A%3E**Important%20note**%3A%20The%20EXPECT_CALL%28%29%20statement%20evaluates%20the%20action%20clause%20only%20once%2C%20even%20though%20the%20action%20may%20be%20performed%20many%20times.%20Therefore%20you%20must%20be%20careful%20about%20side%20effects.%20The%20following%20may%20not%20do%20what%20you%20want%3A%0A%0A%60%60%60%0Aint%20n%20%3D%20100%3B%0AEXPECT_CALL%28turtle%2C%20GetX%28%29%29%0A.Times%284%29%0A.WillRepeatedly%28Return%28n++%29%29%3B%0A%60%60%60%0A%u56E0%u4E3AReturn%u662F%u5B8F%uFF0C%u6240%u4EE5%u53EA%u4F1A%u66FF%u6362%u4E00%u6B21%uFF0C%u6240%u4EE5%u4E0D%u7BA1GetX%u8C03%u7528%u51E0%u6B21%uFF0C%u8FD4%u56DE%u90FD%u662F101%uFF0C%u800C%u4E0D%u662F101%2C102%2C103%2C...%0A%0A**All%20Expectations%20Are%20Sticky**%0A%u6240%u8C13%u7684sticky%u5C31%u662FEXPECT%5C_CALL%u603B%u662F%u751F%u6548%u7684%uFF0C%u9664%u975E%u4F60%u663E%u793A%u7684%u5C06%u5176%u5931%u6548%u3002%u5982%u4E0B%uFF0C%u6240%u6709%u7684EXPECT%5C_CALL%u90FD%u4F1A%u751F%u6548%uFF0C%u90A3%u4E48%u6700%u540E%u4E00%u4E2A%u4F1A%u8986%u76D6%u524D%u9762%u6240%u6709%u7684%uFF0C%u4E5F%u5C31%u662FGetX%u603B%u4F1A%u8FD4%u56DE10%u3002%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3AReturn%3B%0A...%0Afor%20%28int%20i%20%3D%20n%3B%20i%20%3E%200%3B%20i--%29%20%7B%0A%20%20EXPECT_CALL%28turtle%2C%20GetX%28%29%29%0A%20%20%20%20%20%20.WillOnce%28Return%2810*i%29%29%3B%0A%7D%0A%60%60%60%0A%u5982%u679C%u5E0C%u671B%u4ED6%u8FD4%u56DE%uFF0C30%2C20%2C10%uFF0C...%uFF0C%u5E94%u8BE5%u8FD9%u4E48%u5199%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3AReturn%3B%0A...%0Afor%20%28int%20i%20%3D%20n%3B%20i%20%3E%200%3B%20i--%29%20%7B%0A%20%20EXPECT_CALL%28turtle%2C%20GetX%28%29%29%0A%20%20%20%20.WillOnce%28Return%2810*i%29%29%0A%20%20%20%20.RetiresOnSaturation%28%29%3B%0A%7D%0A%60%60%60%0A%60RetireOnSaturation%60%u5C31%u662F%u663E%u793A%u7684%u8BA9%u5176%u5931%u6548%u3002%u8FD8%u6709%u4E00%u4E2A%u529E%u6CD5%uFF1A%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3AInSequence%3B%0Ausing%20%3A%3Atesting%3A%3AReturn%3B%0A...%0A%7B%0A%20%20InSequence%20s%3B%0A%0A%20%20for%20%28int%20i%20%3D%201%3B%20i%20%3C%3D%20n%3B%20i++%29%20%7B%0A%20%20%20%20EXPECT_CALL%28turtle%2C%20GetX%28%29%29%0A%20%20%20%20%20%20%20%20.WillOnce%28Return%2810*i%29%29%0A%20%20%20%20%20%20%20%20.RetiresOnSaturation%28%29%3B%0A%20%20%7D%0A%7D%0A%60%60%60%0A%u56E0%u4E3AInSequence%u7684%u5173%u7CFB%uFF0C%u5728%u524D%u9762%u7684EXPECT%5C_CALL%u751F%u6548%u4EE5%u540E%uFF0C%u5C31%u4F1A%u81EA%u52A8%u5931%u6548%uFF0C%u6240%u4EE5%u4E0D%u4F1A%u4EA7%u751F%u8986%u76D6%u7684%u6548%u679C%u3002%0A**Ordered%20vs%20Unordered%20Calls**%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3AInSequence%3B...%0ATEST%28FooTest%2C%20DrawsLineSegment%29%20%7B%0A%20%20...%0A%20%20%7B%0A%20%20%20%20InSequence%20dummy%3B%0A%0A%20%20%20%20EXPECT_CALL%28turtle%2C%20PenDown%28%29%29%3B%0A%20%20%20%20EXPECT_CALL%28turtle%2C%20Forward%28100%29%29%3B%0A%20%20%20%20EXPECT_CALL%28turtle%2C%20PenUp%28%29%29%3B%0A%20%20%7D%0A%20%20Foo%28%29%3B%0A%7D%0A%60%60%60%0A**Expecting%20Partially%20Ordered%20Calls**%0AGoogle%20Mock%20allows%20you%20to%20impose%20an%20arbitrary%20DAG%20%28directed%20acyclic%20graph%29%20on%20the%20calls.%20One%20way%20to%20express%20the%20DAG%20is%20to%0Ause%20the%20After%20clause%20of%20EXPECT_CALL%20.%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3ASequence%3B%0ASequence%20s1%2C%20s2%3B%0AEXPECT_CALL%28foo%2C%20A%28%29%29%0A%09.InSequence%28s1%2C%20s2%29%3B%0AEXPECT_CALL%28bar%2C%20B%28%29%29%0A%09.InSequence%28s1%29%3B%0AEXPECT_CALL%28bar%2C%20C%28%29%29%0A%09.InSequence%28s2%29%3B%0AEXPECT_CALL%28foo%2C%20D%28%29%29%0A%09.InSequence%28s2%29%3B%0A%60%60%60%0Aspecifies%20the%20following%20DAG%20%28where%20s1%20is%20A%20-%3E%20B%20%2C%20and%20s2%20is%20A%20-%3E%20C%20-%3E%20D%20%29%3A%0A%60%60%60%0A%09%20+---%3E%20B%0A%20%20%20%20%20%7C%0AA%20---%7C%0A%09%20%7C%0A%09%20+---%3E%20C%20---%3E%20D%0A%60%60%60%0A**Uninteresting%20Calls**%0A%u8FD9%u662Fgmock%u62A5%u7684warning%u3002%u5F53%u9488%u5BF9%u67D0%u4E00%u4E2A%u5F85%u6D4B%u51FD%u6570%u8BBE%u7F6E%u4E86EXPECT%20spec%uFF0C%u5374%u5E76%u6CA1%u6709%u8C03%u7528%u7684%u65F6%u5019%uFF0C%u5C31%u4F1A%u62A5%u8FD9%u4E2Awarning%u3002%u6B64%u65F6gmock%u8BA4%u4E3A%u4F60%u5BF9%u8BE5%u51FD%u6570%u5E76%u6CA1%u6709%u5174%u8DA3%uFF0C%u6240%u4EE5%u5C31%u4E0D%u9700%u8981%u8FD9%u4E2AEXPECT%20spec%u3002%u5F53%u7136%u4F60%u53EF%u4EE5%u9009%u62E9%u5FFD%u7565%u8FD9%u4E2Awarning%uFF0C%u4F46%u6211%u8BA4%u4E3A%u51FA%u8FD9%u4E2Awarning%u7684%u65F6%u5019%uFF0C%u591A%u534A%u662Ftest%u6F0F%u5199%u4E86%u4EC0%u4E48%u3002%0A%0A**Returning%20Live%20Values%20from%20Mock%20Methods**%0A%60%60%60%0Ausing%20testing%3A%3AByRef%3B%0Ausing%20testing%3A%3AReturn%3B%0Aclass%20MockFoo%20%3A%20public%20Foo%20%7B%0A%09public%3A%0A%09%09MOCK_METHOD0%28GetValue%2C%20int%28%29%29%3B%0A%7D%3B%0A...%0Aint%20x%20%3D%200%3B%0AMockFoo%20foo%3B%0AEXPECT_CALL%28foo%2C%20GetValue%28%29%29%0A%09//%20.WillRepeatedly%28Return%28ByRef%28x%29%29%29%3B%20X%20error%0A%09.WillRepeatedly%28ReturnPointee%28x%29%29%3B%0Ax%20%3D%2042%3B%0AEXPECT_EQ%2842%2C%20foo.GetValue%28%29%29%3B%0A%60%60%60%0A%0A%23%23%23%20%u4E00%u4E9B%u6D4B%u8BD5case%0A%u5199Unit%20Test%u5E76%u4E0D%u50CF%u60F3%u50CF%u7684%u90A3%u4E48%u7B80%u5355%uFF0C%u5E76%u4E0D%u662F%u8C03%u7528%u4E86%u6846%u67B6%uFF0C%u9488%u5BF9%u6BCF%u4E2A%u51FD%u6570%u5199test%20case%u5C31%u53EF%u4EE5%u3002%u6309%u6211%u76EE%u524D%u7684%u7406%u89E3%u6709%u4EE5%u4E0B%u51E0%u79CD%u6311%u6218%uFF1A%0A1.%20Test%20Case%u5982%u4F55%u89E3%u8026%u3002%u4E0D%u8981%u6709%u91CD%u590D%u6D4B%u8BD5%28overlap%29%u3002%0A%u4F8B%u5982%uFF1A%u5728%u5199A%u51FD%u6570%u7684%u65F6%u5019%uFF0C%u5199%u4E86%u6D4B%u8BD5testA%uFF0CB%u51FD%u6570%u4F1A%u8C03%u7528A%u51FD%u6570%uFF0C%u90A3%u4E48%u5728%u5199%u5B8CA%u4E4B%u540E%u5199B%u7684%u6D4B%u8BD5testB%u65F6%uFF0C%u662F%u5426%u8981%u5C06A%20mock/fake/stub%u6389%3F%20%u5982%u679C%u4E0D%u5C06A%u51FD%u6570Fake%u6389%uFF0C%u5219testA%u548CtestB%u4E4B%u95F4%u5C31%u662F%u6709overlap%u3002%u6211%u8BA4%u4E3A%u8FD9%u4E4B%u95F4%u53EF%u4EE5%u6709%u53D6%u820D%uFF0C%u6700%u4F73%u72B6%u6001%u5E94%u5F53%u662F%u6B64%u65F6%uFF0C%u5C06testA%u5220%u9664%uFF0C%u53EA%u4FDD%u7559testB%u3002%u4F46%u4ECD%u5E94%u6839%u636E%u5177%u4F53%u60C5%u51B5%u800C%u5B9A%u3002%0A%0A2.%20%u5982%u4F55%u9488%u5BF9%u4F9D%u8D56%u5173%u7CFB%u8FDB%u884Cmock%u5316%u3002%0A%u4F8B%u5982%u51FA%u73B0%u8FD9%u6837%u7684%u8BED%u53E5%3A%20%60B%20%3D%20new%20A%60%uFF0C%u5219%u7C7BB%u4F9D%u8D56%u4E8E%u7C7BA%u3002%u4F46%u7C7BA%u5E76%u6CA1%u6709%u5FC5%u8981%u7F16%u8BD1%u8FDBtest%u3002%u56E0%u4E3A%u4E00%u65E6%u52A0%u5165A%uFF0C%u5219%u52BF%u5FC5%u4F1A%u5F15%u5165%u66F4%u591A%u4F9D%u8D56%u5173%u7CFB%uFF0C%u800C%u5BFC%u81F4test%u7F16%u8BD1%u5D29%u6E83%u3002%u4F9D%u8D56%u5173%u7CFB%u7684%u89E3%u51B3%u65E0%u7A77%u65E0%u5C3D%u3002%u5728%u505AAndroid%u7684%u5355%u5143%u6D4B%u8BD5%u65F6%uFF0C%u53EF%u4EE5%u7528PowerMock%u53D6%u4EE3Mockito%u6765Mock%u6784%u9020%u51FD%u6570%uFF0C%u5C06%u6784%u9020%u51FD%u6570Fake%u5316%u6210%u7C7B%u4F3C%u5DE5%u5382%u51FD%u6570%uFF0C%u8FD4%u56DE%u7C7B%u5B9E%u4F8B%u3002%u5177%u4F53%u53C2%u770B%u8FD9%u7BC7%u535A%u6587%5BAndroid%u5355%u5143%u6D4B%u8BD5%5D%28https%3A//zhougy0717.github.io/2016/10/19/Android%25E5%258D%2595%25E5%2585%2583%25E6%25B5%258B%25E8%25AF%2595/%29%u3002%0A%u4F46%u5B9E%u9645%u4E0A%uFF0C%u6309%u7167%u73B0%u5728%u7684%u7406%u89E3%uFF0C%u5176%u5B9EMock%u6784%u9020%u51FD%u6570%u662F%u4E0D%u53EF%u53D6%u7684%uFF0C%u9996%u5148%u9020%u6210%u88AB%u6D4B%u4EE3%u7801spec%u4E0D%u6E05%u6670%uFF0C%u8BD5%u60F3%u4E00%u4E2A%u6784%u9020%u51FD%u6570%u600E%u4E48%u4F1A%u8FD4%u56DE%u53E6%u4E00%u4E2A%u7C7B%u7684%u5B9E%u4F8B%u3002%u5176%u6B21%uFF0C%u5728C++%u4E2D%u5F88%u96BE%u505A%u5230Mock%u6784%u9020%u51FD%u6570%u3002%u597D%u7684%u505A%u6CD5%u5E94%u5F53%u662F%u8FD0%u7528Dependency%20Injection%u3002%u4F8B%u5982%uFF1A%0A%60%60%60%0Aclass%20A%20%0A%7B%7D%0A%0Aclass%20B%0A%7B%0A%09void%20func%28%29%0A%09%7B%0A%09%09A*%20a%20%3D%20new%20A%3B%0A%09%7D%0A%7D%0A%60%60%60%0A%u7C7BB%u5E94%u5F53%u6539%u5199%u4E3A%uFF1A%0A%60%60%60%0Aclass%20B%0A%7B%0A%09void%20func%28A*%29%0A%09%7B%0A%09%09...%0A%09%7D%0A%7D%0A%60%60%60%0A%u5C06%u7C7BA%u6307%u9488%u4F20%u5165%uFF0C%u89E3%u51B3dependency%u7684%u95EE%u9898%u3002%0A%0A3.%20%u5982%u4F55%u80FD%u591F%u8BA9%u6D4B%u8BD5%u7A33%u5B9A%uFF0C%u5728%u4EFB%u610F%u73AF%u5883%u4E0B%u5747%u8FD4%u56DE%u540C%u6837%u7684%u6D4B%u8BD5%u7ED3%u679C%u3002%0A%u8FD9%u4E2A%u4E00%u822C%u6D89%u53CA%u6D4B%u8BD5%u73AF%u5883%u7684%u5F71%u54CD%u3002%u4F8B%u5982%u8C03%u7528%u7F51%u7EDC%u76F8%u5173%u7684%u529F%u80FD%uFF0C%u5728%u6CA1%u6709%u7F51%u7EDC%u7684%u73AF%u5883%u5C31%u6CA1%u6CD5%u8FDB%u884C%u3002%u518D%u4F8B%u5982%u6D4B%u8BD5%u65F6%u64CD%u4F5C%u771F%u5B9E%u5177%u4F53%u6587%u4EF6%uFF0C%u5219%u8BE5%u6587%u4EF6%u88AB%u6D4B%u8BD5%u5916%u4EBA%u4E3A%u6216%u4EE3%u7801%u4FEE%u6539%uFF0C%u5219%u6D4B%u8BD5%u53EF%u80FD%u5C31%u4F1A%u83AB%u540D%u5931%u8D25%u3002%0A%u9488%u5BF9%u8FD9%u4E9B%u60C5%u51B5%uFF0C%u6211%u4EEC%u5E94%u5F53%u5728%u6D4B%u8BD5%u4E2D%u5C3D%u91CF%u907F%u514D%u3002%u4F8B%u5982%u524D%u8005%uFF0C%u6211%u4EEC%u5E94%u5F53%u5BF9%u7F51%u7EDC%u63A5%u53E3%u8FDB%u884CMock%u5316%uFF0C%u540E%u8005%u5E94%u5F53%u5728%u6D4B%u8BD5%u7684setUp%u548CtearDown%u4E2D%u751F%u6210%u865A%u5047%u6587%u4EF6%u7528%u4E8E%u6D4B%u8BD5%uFF0C%u5E76%u5728%u6D4B%u8BD5%u5B8C%u6210%u65F6%u505A%u6E05%u7406%u3002%0A%0A4.%20%u4F1A%u56E0%u4E3A%u5F88%u5C0F%u7684%u88AB%u6D4B%u4EE3%u7801%u6539%u52A8%uFF0C%u800C%u5BFC%u81F4%u5927%u9762%u79EF%u6D4B%u8BD5%u5931%u8D25%uFF0C%u751A%u81F3%u6D4B%u8BD5%u5D29%u6E83%u3002%0A%u8FD9%u4E2A%u5C31%u662Fgmock%u6587%u6863%u4E2D%u63D0%u5230%u7684%u8981%u9488%u5BF9%u63A5%u53E3%u7F16%u7A0B%uFF0C%u9488%u5BF9%u63A5%u53E3%u6D4B%u8BD5%u3002Robert%20C%B7Martin%u5728%u300A%u654F%u6377%u8F6F%u4EF6%u5F00%u53D1%uFF0D%u539F%u5219%u3001%u6A21%u5F0F%u4E0E%u5B9E%u8DF5%u300B%u4E00%u4E66%u4E2D%u6709%u63D0%u51FA%uFF1A%u6240%u6709%u7684%u4EE3%u7801%u90FD%u5E94%u4F9D%u8D56%u4E8E%u62BD%u8C61%u63A5%u53E3%u3002%u56E0%u4E3A%u62BD%u8C61%u63A5%u53E3%u662F%u7ECF%u8FC7%u62BD%u8C61%u7684%uFF0C%u76F8%u5BF9%u5177%u4F53%u7684%u5B9E%u73B0%u4EE3%u7801%u8F83%u4E3A%u7A33%u5B9A%u3002%u800C%u88AB%u4F9D%u8D56%u7684%u4EE3%u7801%u5E94%u8BE5%u5C3D%u53EF%u80FD%u4FDD%u6301%u7A33%u5B9A%uFF0C%u8FD9%u6837%u57FA%u4E8E%u4E4B%u4E0A%u7684%u4EE3%u7801%u624D%u4E0D%u4F1A%u56E0%u4E3A%u4F9D%u8D56%u7684%u6539%u52A8%u800C%u6539%u52A8%u3002%0A%0A%u4E0B%u9762%u5217%u51FA%u51E0%u79CD%u6211%u5728%u5B9E%u9645%u5199test%20case%u65F6%u9047%u5230%u7684%u60C5%u51B5%uFF0C%u5728gmock%u4E2D%u7684%u89E3%u51B3%u65B9%u6848%u3002%0A%0A%23%23%23%20%u6309%u7167%u51FD%u6570%u53C2%u6570%u8FD4%u56DE%u7ED3%u679C%20-%20Fake%0A%u4F8B%u5982%3A%0A%60%60%60%0Aclass%20A%0A%7B%0A%09virtual%20int%20func%28int%20a%2C%20int%20b%29%3B%0A%7D%0A%0AEXPECT_CALL%28mockA%2C%20func%28%29%29%0A%09.WillRepeatedly%28a+b%29%3B%0A%60%60%60%0Agmock%u4E2D%u53EF%u4EE5%u8FD9%u6837%u505A%uFF1AUsing%20Functions/Methods/Functors%20as%20Actions%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3A_%3B%0Ausing%20%3A%3Atesting%3A%3AInvoke%3B%0Aclass%20MockFoo%20%3A%20public%20Foo%20%7B%0Apublic%3A%0A%09MOCK_METHOD2%28Sum%2C%20int%28int%20x%2C%20int%20y%29%29%3B%0A%09MOCK_METHOD1%28ComplexJob%2C%20bool%28int%20x%29%29%3B%0A%7D%3B%0Aint%20CalculateSum%28int%20x%2C%20int%20y%29%20%7B%20return%20x%20+%20y%3B%20%7D%0Aclass%20Helper%20%7B%0Apublic%3A%0A%09bool%20ComplexJob%28int%20x%29%3B%0A%7D%3B%0A...%0AMockFoo%20foo%3B%0AHelper%20helper%3B%0AEXPECT_CALL%28foo%2C%20Sum%28_%2C%20_%29%29%0A%09.WillOnce%28Invoke%28CalculateSum%29%29%3B%0AEXPECT_CALL%28foo%2C%20ComplexJob%28_%29%29%0A%09.WillOnce%28Invoke%28%26helper%2C%20%26Helper%3A%3AComplexJob%29%29%3B%0Afoo.Sum%285%2C%206%29%3B%20//%20Invokes%20CalculateSum%285%2C%206%29.%0Afoo.ComplexJob%2810%29%3B%20//%20Invokes%20helper.ComplexJob%2810%29%3B%0A%60%60%60%0A%0A%23%23%23%20Mock%20non-virtual%u51FD%u6570%0A%60%60%60%0A//%20A%20simple%20packet%20stream%20class.%20None%20of%20its%20members%20is%20virtual.%0Aclass%20ConcretePacketStream%20%7B%0A%09public%3A%0A%09%09void%20AppendPacket%28Packet*%20new_packet%29%3B%0A%09%09const%20Packet*%20GetPacket%28size_t%20packet_number%29%20const%3B%0A%09%09size_t%20NumberOfPackets%28%29%20const%3B%0A%09%09...%0A%7D%3B%0A//%20A%20mock%20packet%20stream%20class.%20It%20inherits%20from%20no%20other%2C%20but%20defines%0A//%20GetPacket%28%29%20and%20NumberOfPackets%28%29.%0Aclass%20MockPacketStream%20%7B%0A%09public%3A%0A%09%09MOCK_CONST_METHOD1%28GetPacket%2C%20const%20Packet*%28size_t%20packet_number%29%29%3B%0A%09%09MOCK_CONST_METHOD0%28NumberOfPackets%2C%20size_t%28%29%29%3B%0A%09%09...%0A%7D%0A%0Atemplate%20%3Cclass%20PacketStream%3E%0Avoid%20CreateConnection%28PacketStream*%20stream%29%20%7B%20...%20%7D%0Atemplate%20%3Cclass%20PacketStream%3E%0Aclass%20PacketReader%20%7B%0A%09public%3A%0A%09void%20ReadPackets%28PacketStream*%20stream%2C%20size_t%20packet_num%29%3B%0A%7D%3B%0A%0AMockPacketStream%20mock_stream%3B%0AEXPECT_CALL%28mock_stream%2C%20...%29...%3B%0A..%20set%20more%20expectations%20on%20mock_stream%20...%0APacketReader%3CMockPacketStream%3E%20reader%28%26mock_stream%29%3B%0A...%20exercise%20reader%20...%0A%60%60%60%0A%u4E3A%u4EC0%u4E48%u8981%u8FD9%u4E48%u505A%uFF1F%0A%u56E0%u4E3A%u53EA%u80FD%u8FD9%u4E48%u505A%u3002%u666E%u901A%u7684mock%uFF0C%u8981%u901A%u8FC7%u7EE7%u627F%u88AB%u6D4B%u8BD5%u7C7B%uFF0C%u5E76%u91CD%u5199virtual%u51FD%u6570%u6765%u5B9E%u73B0%u3002%u800C%u4E0A%u9762%u7684ConcretePacketStream%u548CMockPacketStream%u5E76%u4EFB%u4F55%u6CA1%u6709%u5173%u7CFB%uFF0C%u4E5F%u5C31%u662F%u8BF4%uFF0C%u5982%u679C%u4F20%u5165%u540E%u8005%u7684%u6307%u9488%uFF0C%u4E0D%u7528reinterpret_cast%u662F%u4E0D%u80FD%u8F6C%u6210%u524D%u8005%u7684%u6307%u9488%u7684%u3002%0A%u6240%u4EE5%u60F3%u4E00%u4E2A%u53D8%u901A%u7684%u529E%u6CD5%uFF0C%u7528%u6A21%u677F%u7C7B%u6765%u5B9A%u4E49%u88AB%u6D4B%u4EE3%u7801%uFF0C%u5728%u6D4B%u8BD5%u65F6%u4F20%u5165mock%u7C7B%uFF0C%u5728%u751F%u4EA7%u65F6%uFF0C%u4F20%u5165%u771F%u5B9E%u7C7B%u3002%0A%0A%23%23%23%20Mocking%20Side%20Effects%0A%60%60%60%0AEXPECT_CALL%28mutator%2C%20MutateInt%28_%29%29%0A%09.WillOnce%28DoAll%28SetArgPointee%3C0%3E%285%29%2C%20Return%28true%29%29%29%3B%0AEXPECT_CALL%28mutator%2C%20Mutate%28NotNull%28%29%2C%205%29%29%0A%09.WillOnce%28SetArrayArgument%3C0%3E%28values%2C%20values%20+%205%29%29%3B%0A%60%60%60%0A%u7B2C%u4E00%u4E2A%u5C06MutateInt%u7B2C%u4E00%u4E2A%u53C2%u6570%u6307%u9488%u6307%u5411%u7684int%uFF0C%u8BBE%u4E3A5%uFF0C%u5E76%u8FD4%u56DEtrue%u3002%0A%u7B2C%u4E8C%u4E2A%u5C06values%u6570%u7EC4%u7684%5B0%2C5%29%u62F7%u8D1D%u5230%u53C2%u65701%u6307%u5411%u7684%u5730%u5740%u3002%0A%u5982%u679C%u4ECD%u9700%u8981%u8FD4%u56DE%uFF0C%u5219%u7528DoAll%uFF0C%u5982%u4E0B%uFF1A%0A%60%60%60%0AEXPECT_CALL%28mutator%2C%20MutateInt%28_%29%29%0A%20%20%20%20%20%20.WillOnce%28DoAll%28SetArgPointee%3C0%3E%285%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Return%28true%29%29%29%3B%0A%60%60%60%0A%0A%23%23%23%20Selecting%20an%20Action%27s%20Arguments%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3A_%3B%0Ausing%20%3A%3Atesting%3A%3AInvoke%3B%0Abool%20MyIsVisibleInQuadrant1%28bool%20visible%2C%20const%20string%26%20name%2C%20int%20x%2C%20int%20y%2C%0Aconst%20map%3Cpair%3Cint%2C%20int%3E%2C%20double%3E%26%20weight%2C%0Adouble%20min_weight%2C%20double%20max_wight%29%20%7B%0A%09return%20IsVisibleInQuadrant1%28visible%2C%20x%2C%20y%29%3B%0A%7D.%0A..%0AEXPECT_CALL%28mock%2C%20Foo%28_%2C%20_%2C%20_%2C%20_%2C%20_%2C%20_%2C%20_%29%29%0A%09.WillOnce%28Invoke%28MyIsVisibleInQuadrant1%29%29%3B%20//%20Now%20it%20works.%0A%60%60%60%0A%u5B9A%u4E49%u81EA%u5DF1%u7684adaptor%20MyIsVisibleInQuadrant1%uFF0C%u6216%u8005%u7528gmock%u63D0%u4F9B%u7684%u65B9%u6CD5%u4F18%u96C5%u7684%u89E3%u51B3%u3002%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3A_%3B%0Ausing%20%3A%3Atesting%3A%3AInvoke%3B%0Ausing%20%3A%3Atesting%3A%3AWithArgs%3B%0A...%0AEXPECT_CALL%28mock%2C%20Foo%28_%2C%20_%2C%20_%2C%20_%2C%20_%2C%20_%2C%20_%29%29%0A.WillOnce%28WithArgs%3C0%2C%202%2C%203%3E%28Invoke%28IsVisibleInQuadrant1%29%29%29%3B%0A//%20No%20need%20to%20define%20your%20own%20adaptor.%0A%60%60%60%0A%0A%23%23%23%23%20Mocking%20Private%20or%20Protected%20Methods%0A%60%60%60%0Aclass%20Foo%20%7B%0A%09public%3A%0A%09%09...%0A%09%09virtual%20bool%20Transform%28Gadget*%20g%29%20%3D%200%3B%0A%09protected%3A%0A%09%09virtual%20void%20Resume%28%29%3B%0A%09private%3A%0A%09%09virtual%20int%20GetTimeOut%28%29%3B%0A%7D%3B%0Aclass%20MockFoo%20%3A%20public%20Foo%20%7B%0A%09public%3A%0A%09%09...%0A%09%09MOCK_METHOD1%28Transform%2C%20bool%28Gadget*%20g%29%29%3B%0A%09%09//%20The%20following%20must%20be%20in%20the%20public%20section%2C%20even%20though%20the%0A%09%09//%20methods%20are%20protected%20or%20private%20in%20the%20base%20class.%0A%09%09MOCK_METHOD0%28Resume%2C%20void%28%29%29%3B%0A%09%09MOCK_METHOD0%28GetTimeOut%2C%20int%28%29%29%3B%0A%7D%3B%0A%60%60%60%0A%3EC++%20allows%20a%20subclass%20to%20specify%20a%20different%20access%20level%20than%20the%20base%20class%20on%20a%20virtual%20function.%0A%0A%23%23%23%23%20Misc%0AKeep%20in%20mind%20that%20one%20doesn%27t%20have%20to%20verify%20more%20than%20one%20property%20in%20one%20test.%20In%20fact%2C%20it%27s%20a%20good%20style%20to%20verify%20only%20one%0Athing%20in%20one%20test.%20If%20you%20do%20that%2C%20a%20bug%20will%20likely%20break%20only%20one%20or%20two%20tests%20instead%20of%20dozens%0A%0AWhen%20it%27s%20being%20destroyed%2C%20your%20friendly%20mock%20object%20will%20automatically%20verify%20that%20all%20expectations%20on%20it%20have%20been%20satisfied%2C%0Aand%20will%20generate%20Google%20Test%20failures%20if%20not.%20%0A%0ACurrently%20these%20are%20only%20platforms%20that%20support%20the%20pthreads%20library%20%28this%20includes%20Linux%20and%20Mac%29.%20%0A%0A%u52A0%u4E0A%u547D%u4EE4%u884C%u53C2%u6570--gmock_verbose%3Dinfo%u53EF%u4EE5%u663E%u793A%u6240%u6709EXPECT%5C_CALL%u7684%u5177%u4F53%u8C03%u7528%u60C5%u51B5%u3002%0A%0A%23%23%20Some%20useful%20tips%20in%20gtest%0A%23%23%23%20Selecting%20Tests%0A%3E%20If%20you%20set%20the%20**GTEST_FILTER**%20environment%20variable%20or%20the%20**--gtest_filter**%20flag%20to%20a%20filter%20string%2C%20Google%20Test%20will%20only%20run%20the%20tests%20whose%20full%20names%20%28in%20the%20form%20of%20TestCaseName.TestName%29%20match%20the%20filter.%0A%3E%20The%20format%20of%20a%20filter%20is%20a%20%27%3A%27-separated%20list%20of%20wildcard%20patterns%20%28called%20the%20positive%20patterns%29%20optionally%20followed%20by%20a%20%27-%27%20and%20another%20%27%3A%27-separated%20pattern%20list%20%28called%20the%20negative%20patterns%29.%0A%0A-%20./foo_test%20Has%20no%20flag%2C%20and%20thus%20runs%20all%20its%20tests.%0A-%20./foo_test%20--gtest_filter%3D*%20Also%20runs%20everything%2C%20due%20to%20the%20single%20match-everything%20*%20value.%0A-%20./foo_test%20--gtest_filter%3DFooTest.*%20Runs%20everything%20in%20test%20case%20FooTest.%0A-%20./foo_test%20--gtest_filter%3D*Null*%3A*Constructor*%20Runs%20any%20test%20whose%20full%20name%20contains%20either%20%22Null%22%20or%20%22Constructor%22.%0A-%20./foo_test%20--gtest_filter%3D-*DeathTest.*%20Runs%20all%20non-death%20tests.%0A-%20./foo_test%20--gtest_filter%3DFooTest.*-FooTest.Bar%20Runs%20everything%20in%20test%20case%20FooTest%20except%20FooTest.Bar%0A%0ATemporarily%20Disabling%20Tests%0A%60%60%60%0A//%20Tests%20that%20Foo%20does%20Abc.%0ATEST%28FooTest%2C%20DISABLED_DoesAbc%29%20%7B%20...%20%7D%0A%0Aclass%20DISABLED_BarTest%20%3A%20public%20%3A%3Atesting%3A%3ATest%20%7B%20...%20%7D%3B%0A%0A//%20Tests%20that%20Bar%20does%20Xyz.%0ATEST_F%28DISABLED_BarTest%2C%20DoesXyz%29%20%7B%20...%20%7D%0A%60%60%60%0A%0ATemporarily%20Enabling%20Disabled%20Tests%0A%3Ejust%20invoke%20the%20test%20program%20with%20the%20**--gtest_also_run_disabled_tests**%20flag%20or%20set%20the%20**GTEST_ALSO_RUN_DISABLED_TESTS**%20environment%20variable%20to%20a%20value%20other%20than%200.%0A%0A%23%23%23%20Repeating%20the%20Tests%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%20%20%20%20%20%20%20%20%20%20%20%20%7C%0A%7C%20%3A--------%3A%20%7C%20%3A--------------%3A%20%7C%20%3A--------%3A%20%7C%0A%7C%5C%24%20foo_test%20--gtest_repeat%3D1000%20%7C%09Repeat%20foo_test%201000%20times%20and%20don%27t%20stop%20at%20failures.%7C%0A%7C%5C%24%20foo_test%20--gtest_repeat%3D-1%09%7CA%20negative%20count%20means%20repeating%20forever.%7C%0A%7C%5C%24%20foo_test%20--gtest_repeat%3D1000%20--gtest_break_on_failure%09%7C%20Repeat%20foo_test%201000%20times%2C%20stopping%20at%20the%20first%20failure.%20This%20is%20especially%20useful%20when%20running%20under%20a%20debugger%3A%20when%20the%20testfails%2C%20it%20will%20drop%20into%20the%20debugger%20and%20you%20can%20then%20inspect%20variables%20and%20stacks.%7C%0A%7C%24%20foo_test%20--gtest_repeat%3D1000%20--gtest_filter%3DFooBar%09%7C%20Repeat%20the%20tests%20whose%20name%20matches%20the%20filter%201000%20times.%7C

Edit

Google Test,Google Mock以下简称gtest,gmock。
在接触gtest,gmock之前,测试C/C++ code使用UnitTest++。这是一个很简洁的框架,上手很快。参看另一篇博文UnitTest++简介。测试相关的功能够用,但是没有mock库。这带来的问题是:

  1. 测试遗留代码的时候,需要自行fake相关代码。这个在遗留系统很庞大时,要颇费心力。而且过多涉及细节,导致测试极不稳定,系统代码任意的演进,都会导致大堆的测试失败,甚至测试无法进行。
  2. 测试case之间无法很好的解耦。结果同样是测试不够稳定。术语是测试代码很“脆弱”。

Java,Python,JavaScript都有自己的mock库。Python的Mock类,Java的Mockito/PowerMock,JavaScript的Sinon。于是,在网上搜索了一下C/C++的Mock库,于是看到了gtest,gmock。然后就有了这一篇。

简介

不用去网上费心找教程,两个项目的文档都非常棒。入口统一在gtest GitHub项目主页上。而且该文档不仅很好的介绍了gtest,gmock的用法,其中还涉及了很多TDD或者UnitTest的真知灼见,很值得读一读。
要使用gtest非常简单:编译出gtest,gmock,再链入你的测试程序。

编译gtest/gmock

gtest,gmock均用cmake来管理跨平台,先用cmake来生成Makefile。用命令cmake -G "Unix Makefiles" /path/to/CMakeList.txt

Makefile

然后按照下面编写Makefile。注意gmock_main是一个main函数来调用所有的test case,省得自己写main函数了。

CC = gcc
CPP = g++
LINK = g++
CFLAGS = -g -Wall -Werror -Wextra -std=gnu99
CPPFLAGS = -g -Wall -Werror -Wextra
LIBS = -L./lib -lgtest -lgmock -lgmock_main -lpthread
C__SOURCES = $(wildcard *.c)
CPPSOURCES = $(wildcard *.cpp)
OBJECTS = $(patsubst %.c, %.o, $(C__SOURCES)) $(patsubst %.cpp, %.o, $(CPPSOURCES))
TARGET = test_exe
first: all
%.o: %.c
$(CC) $(INCLUDES) -c $(CFLAGS) -o $@ $<
%.o: %.cpp
$(CPP) $(INCLUDES) -c $(CPPFLAGS) -o $@ $<
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(LINK) $(CPPFLAGS) $(LIBS) -o $(TARGET) $(OBJECTS)
.PHONY : clean
clean:
rm -f $(TARGET) $(OBJECTS)

Terms

MeaningGoogle Test TermISTQB Term
Exercise a particular program path with specific input values and verify the resultsTEST()Test Case
A set of several tests related to one componentTest CaseTest Suite

Test

#include "gtest/gtest.h"
#include "gmock/gmock.h"
using ::testing::Return;
using ::testing::Test;
using ::testing::_;
using ::testing::AtLeast;
TEST(TestCaseName, should_this_test_do)
{
...
EXPECT_STREQ("{}", str);
}

中间的那堆namespace都是gtest/gmock库里定义的matcher宏或者各种有用的宏。

Test Fixture

在测试有重复的时候,就要用到Test Fixture了,也就是setUp / tearDown。

class QueueTest : public ::testing::Test {
protected:
virtual void SetUp() {
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// virtual void TearDown() {}
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
TEST_F(QueueTest, IsEmptyInitially) {
EXPECT_EQ(0, q0_.size());
}
TEST_F(QueueTest, DequeueWorks) {
int* n = q0_.Dequeue();
EXPECT_EQ(NULL, n);
n = q1_.Dequeue();
ASSERT_TRUE(n != NULL);
EXPECT_EQ(1, *n);
EXPECT_EQ(0, q1_.size());
delete n;
n = q2_.Dequeue();
ASSERT_TRUE(n != NULL);
EXPECT_EQ(2, *n);
EXPECT_EQ(1, q2_.size());
delete n;
}

constructor/destructor vs. SetUp/TearDown

When you need to write per-test set-up and tear-down logic, you have the choice between using the test fixture constructor/destructor or SetUp()/TearDown(). The former is usually preferred, as it has the following benefits:

  • By initializing a member variable in the constructor, we have the option to make it const, which helps prevent accidental changes to its value and makes the tests more obviously correct.
  • In case we need to subclass the test fixture class, the subclass’ constructor is guaranteed to call the base class’ constructor first, and the subclass’ destructor is guaranteed to call the base class’ destructor afterward. With SetUp()/TearDown(), a subclass may make the mistake of forgetting to call the base class’ SetUp()/TearDown() or call them at the wrong moment.

Benefit for using SetUp/TearDown:

  • If the tear-down operation could throw an exception, you must use TearDown() as opposed to the destructor, as throwing in a destructor leads to undefined behavior and usually will kill your program right away. Note that many standard libraries (like STL) may throw when exceptions are enabled in the compiler. Therefore you should prefer TearDown() if you want to write portable tests that work with or without exceptions.
  • The assertion macros throw an exception when flag –gtest_throw_on_failure is specified. Therefore, you shouldn’t use Google Test assertions in a destructor if you plan to run your tests with this flag.
  • In a constructor or destructor, you cannot make a virtual function call on this object. (You can call a method declared as virtual, but it will be statically bound.) Therefore, if you need to call a method that will be overriden in a derived class, you have to use SetUp()/TearDown().

简言之,在逻辑上,这两组的作用相同,都是每个测试之前之后会做一些处理工作。Constructor/Destructor的好处是提供了继承。setUp/tearDown的好处是可以处理exception,这是不能放在析构函数里的。

SetUpTestCase / TearDownTestCase

Test Case级别的SetUp/TearDown

class FooTest : public ::testing::Test {
protected:
// Per-test-case set-up.
// Called before the first test in this test case.
// Can be omitted if not needed.
static void SetUpTestCase() {
shared_resource_ = new ...;
}
// Per-test-case tear-down.
// Called after the last test in this test case.
// Can be omitted if not needed.
static void TearDownTestCase() {
delete shared_resource_;
shared_resource_ = NULL;
}
// You can define per-test set-up and tear-down logic as usual.
virtual void SetUp() { ... }
virtual void TearDown() { ... }
// Some expensive resource shared by all tests.
static T* shared_resource_;
};

SetUp/TearDown Environment

  • First, you subclass the ::testing::Environment class to define a test environment, which knows how to set-up and tear-down:
  • Then, you register an instance of your environment class with Google Test by calling the ::testing::AddGlobalTestEnvironment() function:
    Now, when RUN_ALL_TESTS() is called, it first calls the SetUp() method of the environment object, then runs the tests if there was no fatal failures, and finally calls TearDown() of the environment object.
  • It’s OK to register multiple environment objects. In this case, their SetUp() will be called in the order they are registered, and their TearDown() will be called in the reverse order.
  • Note that Google Test takes ownership of the registered environment objects. Therefore do not delete them by yourself.
class Environment {
public:
virtual ~Environment() {}
// Override this to define how to set up the environment.
virtual void SetUp() {}
// Override this to define how to tear down the environment.
virtual void TearDown() {}
};
Environment* AddGlobalTestEnvironment(Environment* env);

断言

有两种断言EXPECT_xxx和ASSERT_xxx。前者会让测试终止,后者不会,只会让测试fail。

gmock

之所以要切到gtest,唯一的原因就是gmock,所以要专开一章重点介绍一下。所有内容均来自于官方文档。内容深度由浅入深,依次如下:

最后还有参考手册:

简介

Google C++ Mocking Framework (or Google Mock for short) is a library (sometimes we also call it a “framework” to make it sound cool) for creating mock classes and using them. It does to C++ what jMock and EasyMock do to Java.

何为Mock?

Mocks are objects pre-programmed with expectations, which form a specification of the calls they are expected to receive.

相应的还有Fake和Stub

Fake objects have working implementations, but usually take some shortcut (perhaps to make the operations less expensive), which makes them not suitable for production. An in-memory file system would be an example of a fake.

gmock的文档里只提到了Fake,从Martin Fowler的文章Mocks Aren’t Stubs中摘录如下:

Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test.

三者作用相同,都是模拟系统其他部分的功能,达到代码隔离的效果,方便测试。但是Mock的特点是更OO化,也符合TDD或者BDD的思想——针对一个object设置期待,再对齐verify。

Mocks vs. Stubs - from Martin Fowler

In order to use state verification on the stub, I need to make some extra methods on the stub to help with verification. As a result the stub implements MailService but adds extra test methods.

Mock objects always use behavior verification, a stub can go either way. Meszaros refers to stubs that use behavior verification as a Test Spy. The difference is in how exactly the double runs and verifies and I’ll leave that for you to explore on your own.

Getting Started

Class to Mock

class Turtle {
...
virtual ~Turtle() {}
virtual void PenUp() = 0;
virtual void PenDown() = 0;
virtual void Forward(int distance) = 0;
};

Mock class

#include "gmock/gmock.h" // Brings in Google Mock.
class MockTurtle : public Turtle {
public:
...
MOCK_METHOD0(PenUp, void());
MOCK_METHOD0(PenDown, void());
MOCK_METHOD1(Forward, void(int distance));
};

针对待Mock的Turtle class要注意的是:

Note that the destructor of Turtle must be virtual, as is the case for all classes you intend to inherit from - otherwise the destructor of the derived class will not be called when you delete an object through a base pointer, and you’ll get corrupted program states like memory leaks.

Use it

#include "path/to/mock-turtle.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::AtLeast; // #1
TEST(PainterTest, CanDrawSomething) {
MockTurtle turtle; // #2
EXPECT_CALL(turtle, PenDown()) // #3
.Times(AtLeast(1));
Painter painter(&turtle); // #4
EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
} // #5
int main(int argc, char** argv) {
// The following line must be executed to initialize Google Mock
// (and Google Test) before running the tests.
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}

如果你愿意的话,可以自己写main,如果你想偷懒,记得在Makefile里link gmock_main。
按Google的说法,gmock也可以和其他测试框架兼容,但总感觉挺悬的。

一些有用的工具

Marcher
EXPECT_CALL(turtle, Forward(_));
     以任意参数调用Forward
EXPECT_CALL(turtle, Forward(Ge(100)));
     以大于100的整数调用Forward

Cardinalities: How Many Times Will It Be Called?

  • If neither WillOnce() nor WillRepeatedly() is in the EXPECT_CALL(), the inferred cardinality is Times(1).
  • If there are n WillOnce()’s but no WillRepeatedly(), where n >= 1, the cardinality is Times(n).
  • If there are n WillOnce()’s and one WillRepeatedly(), where n >= 0, the cardinality is Times(AtLeast(n)).
EXPECT_CALL(mockObj, func())
.Times(AtLeast(1))
.WillOnce(Return(123))
.WillRepeatedly(Return(456))

上面这段代码要求func函数至少运行一次,第一次返回123,之后每次返回456。

Important note: The EXPECT_CALL() statement evaluates the action clause only once, even though the action may be performed many times. Therefore you must be careful about side effects. The following may not do what you want:

int n = 100;
EXPECT_CALL(turtle, GetX())
.Times(4)
.WillRepeatedly(Return(n++));

因为Return是宏,所以只会替换一次,所以不管GetX调用几次,返回都是101,而不是101,102,103,…

All Expectations Are Sticky
所谓的sticky就是EXPECT_CALL总是生效的,除非你显示的将其失效。如下,所有的EXPECT_CALL都会生效,那么最后一个会覆盖前面所有的,也就是GetX总会返回10。

using ::testing::Return;
...
for (int i = n; i > 0; i--) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i));
}

如果希望他返回,30,20,10,…,应该这么写

using ::testing::Return;
...
for (int i = n; i > 0; i--) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}

RetireOnSaturation就是显示的让其失效。还有一个办法:

using ::testing::InSequence;
using ::testing::Return;
...
{
InSequence s;
for (int i = 1; i <= n; i++) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}
}

因为InSequence的关系,在前面的EXPECT_CALL生效以后,就会自动失效,所以不会产生覆盖的效果。
Ordered vs Unordered Calls

using ::testing::InSequence;...
TEST(FooTest, DrawsLineSegment) {
...
{
InSequence dummy;
EXPECT_CALL(turtle, PenDown());
EXPECT_CALL(turtle, Forward(100));
EXPECT_CALL(turtle, PenUp());
}
Foo();
}

Expecting Partially Ordered Calls
Google Mock allows you to impose an arbitrary DAG (directed acyclic graph) on the calls. One way to express the DAG is to
use the After clause of EXPECT_CALL .

using ::testing::Sequence;
Sequence s1, s2;
EXPECT_CALL(foo, A())
.InSequence(s1, s2);
EXPECT_CALL(bar, B())
.InSequence(s1);
EXPECT_CALL(bar, C())
.InSequence(s2);
EXPECT_CALL(foo, D())
.InSequence(s2);

specifies the following DAG (where s1 is A -> B , and s2 is A -> C -> D ):

+---> B
|
A ---|
|
+---> C ---> D

Uninteresting Calls
这是gmock报的warning。当针对某一个待测函数设置了EXPECT spec,却并没有调用的时候,就会报这个warning。此时gmock认为你对该函数并没有兴趣,所以就不需要这个EXPECT spec。当然你可以选择忽略这个warning,但我认为出这个warning的时候,多半是test漏写了什么。

Returning Live Values from Mock Methods

using testing::ByRef;
using testing::Return;
class MockFoo : public Foo {
public:
MOCK_METHOD0(GetValue, int());
};
...
int x = 0;
MockFoo foo;
EXPECT_CALL(foo, GetValue())
// .WillRepeatedly(Return(ByRef(x))); X error
.WillRepeatedly(ReturnPointee(x));
x = 42;
EXPECT_EQ(42, foo.GetValue());

一些测试case

写Unit Test并不像想像的那么简单,并不是调用了框架,针对每个函数写test case就可以。按我目前的理解有以下几种挑战:

  1. Test Case如何解耦。不要有重复测试(overlap)。
    例如:在写A函数的时候,写了测试testA,B函数会调用A函数,那么在写完A之后写B的测试testB时,是否要将A mock/fake/stub掉? 如果不将A函数Fake掉,则testA和testB之间就是有overlap。我认为这之间可以有取舍,最佳状态应当是此时,将testA删除,只保留testB。但仍应根据具体情况而定。

  2. 如何针对依赖关系进行mock化。
    例如出现这样的语句: B = new A,则类B依赖于类A。但类A并没有必要编译进test。因为一旦加入A,则势必会引入更多依赖关系,而导致test编译崩溃。依赖关系的解决无穷无尽。在做Android的单元测试时,可以用PowerMock取代Mockito来Mock构造函数,将构造函数Fake化成类似工厂函数,返回类实例。具体参看这篇博文Android单元测试
    但实际上,按照现在的理解,其实Mock构造函数是不可取的,首先造成被测代码spec不清晰,试想一个构造函数怎么会返回另一个类的实例。其次,在C++中很难做到Mock构造函数。好的做法应当是运用Dependency Injection。例如:

class A
{}
class B
{
void func()
{
A* a = new A;
}
}

类B应当改写为:

class B
{
void func(A*)
{
...
}
}

将类A指针传入,解决dependency的问题。

  1. 如何能够让测试稳定,在任意环境下均返回同样的测试结果。
    这个一般涉及测试环境的影响。例如调用网络相关的功能,在没有网络的环境就没法进行。再例如测试时操作真实具体文件,则该文件被测试外人为或代码修改,则测试可能就会莫名失败。
    针对这些情况,我们应当在测试中尽量避免。例如前者,我们应当对网络接口进行Mock化,后者应当在测试的setUp和tearDown中生成虚假文件用于测试,并在测试完成时做清理。

  2. 会因为很小的被测代码改动,而导致大面积测试失败,甚至测试崩溃。
    这个就是gmock文档中提到的要针对接口编程,针对接口测试。Robert C·Martin在《敏捷软件开发-原则、模式与实践》一书中有提出:所有的代码都应依赖于抽象接口。因为抽象接口是经过抽象的,相对具体的实现代码较为稳定。而被依赖的代码应该尽可能保持稳定,这样基于之上的代码才不会因为依赖的改动而改动。

下面列出几种我在实际写test case时遇到的情况,在gmock中的解决方案。

按照函数参数返回结果 - Fake

例如:

class A
{
virtual int func(int a, int b);
}
EXPECT_CALL(mockA, func())
.WillRepeatedly(a+b);

gmock中可以这样做:Using Functions/Methods/Functors as Actions

using ::testing::_;
using ::testing::Invoke;
class MockFoo : public Foo {
public:
MOCK_METHOD2(Sum, int(int x, int y));
MOCK_METHOD1(ComplexJob, bool(int x));
};
int CalculateSum(int x, int y) { return x + y; }
class Helper {
public:
bool ComplexJob(int x);
};
...
MockFoo foo;
Helper helper;
EXPECT_CALL(foo, Sum(_, _))
.WillOnce(Invoke(CalculateSum));
EXPECT_CALL(foo, ComplexJob(_))
.WillOnce(Invoke(&helper, &Helper::ComplexJob));
foo.Sum(5, 6); // Invokes CalculateSum(5, 6).
foo.ComplexJob(10); // Invokes helper.ComplexJob(10);

Mock non-virtual函数

// A simple packet stream class. None of its members is virtual.
class ConcretePacketStream {
public:
void AppendPacket(Packet* new_packet);
const Packet* GetPacket(size_t packet_number) const;
size_t NumberOfPackets() const;
...
};
// A mock packet stream class. It inherits from no other, but defines
// GetPacket() and NumberOfPackets().
class MockPacketStream {
public:
MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number));
MOCK_CONST_METHOD0(NumberOfPackets, size_t());
...
}
template <class PacketStream>
void CreateConnection(PacketStream* stream) { ... }
template <class PacketStream>
class PacketReader {
public:
void ReadPackets(PacketStream* stream, size_t packet_num);
};
MockPacketStream mock_stream;
EXPECT_CALL(mock_stream, ...)...;
.. set more expectations on mock_stream ...
PacketReader<MockPacketStream> reader(&mock_stream);
... exercise reader ...

为什么要这么做?
因为只能这么做。普通的mock,要通过继承被测试类,并重写virtual函数来实现。而上面的ConcretePacketStream和MockPacketStream并任何没有关系,也就是说,如果传入后者的指针,不用reinterpret_cast是不能转成前者的指针的。
所以想一个变通的办法,用模板类来定义被测代码,在测试时传入mock类,在生产时,传入真实类。

Mocking Side Effects

EXPECT_CALL(mutator, MutateInt(_))
.WillOnce(DoAll(SetArgPointee<0>(5), Return(true)));
EXPECT_CALL(mutator, Mutate(NotNull(), 5))
.WillOnce(SetArrayArgument<0>(values, values + 5));

第一个将MutateInt第一个参数指针指向的int,设为5,并返回true。
第二个将values数组的[0,5)拷贝到参数1指向的地址。
如果仍需要返回,则用DoAll,如下:

EXPECT_CALL(mutator, MutateInt(_))
.WillOnce(DoAll(SetArgPointee<0>(5),
Return(true)));

Selecting an Action’s Arguments

using ::testing::_;
using ::testing::Invoke;
bool MyIsVisibleInQuadrant1(bool visible, const string& name, int x, int y,
const map<pair<int, int>, double>& weight,
double min_weight, double max_wight) {
return IsVisibleInQuadrant1(visible, x, y);
}.
..
EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _))
.WillOnce(Invoke(MyIsVisibleInQuadrant1)); // Now it works.

定义自己的adaptor MyIsVisibleInQuadrant1,或者用gmock提供的方法优雅的解决。

using ::testing::_;
using ::testing::Invoke;
using ::testing::WithArgs;
...
EXPECT_CALL(mock, Foo(_, _, _, _, _, _, _))
.WillOnce(WithArgs<0, 2, 3>(Invoke(IsVisibleInQuadrant1)));
// No need to define your own adaptor.

Mocking Private or Protected Methods

class Foo {
public:
...
virtual bool Transform(Gadget* g) = 0;
protected:
virtual void Resume();
private:
virtual int GetTimeOut();
};
class MockFoo : public Foo {
public:
...
MOCK_METHOD1(Transform, bool(Gadget* g));
// 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());
};

C++ allows a subclass to specify a different access level than the base class on a virtual function.

Misc

Keep in mind that one doesn’t have to verify more than one property in one test. In fact, it’s a good style to verify only one
thing in one test. If you do that, a bug will likely break only one or two tests instead of dozens

When it’s being destroyed, your friendly mock object will automatically verify that all expectations on it have been satisfied,
and will generate Google Test failures if not.

Currently these are only platforms that support the pthreads library (this includes Linux and Mac).

加上命令行参数–gmock_verbose=info可以显示所有EXPECT_CALL的具体调用情况。

Some useful tips in gtest

Selecting Tests

If you set the GTEST_FILTER environment variable or the –gtest_filter flag to a filter string, Google Test will only run the tests whose full names (in the form of TestCaseName.TestName) match the filter.
The format of a filter is a ‘:’-separated list of wildcard patterns (called the positive patterns) optionally followed by a ‘-’ and another ‘:’-separated pattern list (called the negative patterns).

  • ./foo_test Has no flag, and thus runs all its tests.
  • ./foo_test –gtest_filter=* Also runs everything, due to the single match-everything * value.
  • ./foo_test –gtest_filter=FooTest.* Runs everything in test case FooTest.
  • ./foo_test –gtest_filter=Null:Constructor Runs any test whose full name contains either “Null” or “Constructor”.
  • ./foo_test –gtest_filter=-DeathTest. Runs all non-death tests.
  • ./foo_test –gtest_filter=FooTest.*-FooTest.Bar Runs everything in test case FooTest except FooTest.Bar

Temporarily Disabling Tests

// Tests that Foo does Abc.
TEST(FooTest, DISABLED_DoesAbc) { ... }
class DISABLED_BarTest : public ::testing::Test { ... };
// Tests that Bar does Xyz.
TEST_F(DISABLED_BarTest, DoesXyz) { ... }

Temporarily Enabling Disabled Tests

just invoke the test program with the –gtest_also_run_disabled_tests flag or set the GTEST_ALSO_RUN_DISABLED_TESTS environment variable to a value other than 0.

Repeating the Tests

$ foo_test –gtest_repeat=1000Repeat foo_test 1000 times and don’t stop at failures.
$ foo_test –gtest_repeat=-1A negative count means repeating forever.
$ foo_test –gtest_repeat=1000 –gtest_break_on_failureRepeat foo_test 1000 times, stopping at the first failure. This is especially useful when running under a debugger: when the testfails, it will drop into the debugger and you can then inspect variables and stacks.
$ foo_test –gtest_repeat=1000 –gtest_filter=FooBarRepeat the tests whose name matches the filter 1000 times.
%23%20C/C++%20Test%20Framework%20-%20Google%20Test%20%26%20Google%20Mock%0A@%28myblog%29%5BTDD%2C%20gtest%2C%20gmock%5D%0A%0AGoogle%20Test%uFF0CGoogle%20Mock%u4EE5%u4E0B%u7B80%u79F0gtest%uFF0Cgmock%u3002%0A%u5728%u63A5%u89E6gtest%uFF0Cgmock%u4E4B%u524D%uFF0C%u6D4B%u8BD5C/C++%20code%u4F7F%u7528%5BUnitTest++%5D%28https%3A//github.com/unittest-cpp/unittest-cpp%29%u3002%u8FD9%u662F%u4E00%u4E2A%u5F88%u7B80%u6D01%u7684%u6846%u67B6%uFF0C%u4E0A%u624B%u5F88%u5FEB%u3002%u53C2%u770B%u53E6%u4E00%u7BC7%u535A%u6587%5BUnitTest++%u7B80%u4ECB%5D%28https%3A//zhougy0717.github.io/2016/02/29/UnitTest++%25E7%25AE%2580%25E4%25BB%258B/%29%u3002%u6D4B%u8BD5%u76F8%u5173%u7684%u529F%u80FD%u591F%u7528%uFF0C%u4F46%u662F%u6CA1%u6709mock%u5E93%u3002%u8FD9%u5E26%u6765%u7684%u95EE%u9898%u662F%uFF1A%0A1.%20%u6D4B%u8BD5%u9057%u7559%u4EE3%u7801%u7684%u65F6%u5019%uFF0C%u9700%u8981%u81EA%u884Cfake%u76F8%u5173%u4EE3%u7801%u3002%u8FD9%u4E2A%u5728%u9057%u7559%u7CFB%u7EDF%u5F88%u5E9E%u5927%u65F6%uFF0C%u8981%u9887%u8D39%u5FC3%u529B%u3002%u800C%u4E14%u8FC7%u591A%u6D89%u53CA%u7EC6%u8282%uFF0C%u5BFC%u81F4%u6D4B%u8BD5%u6781%u4E0D%u7A33%u5B9A%uFF0C%u7CFB%u7EDF%u4EE3%u7801%u4EFB%u610F%u7684%u6F14%u8FDB%uFF0C%u90FD%u4F1A%u5BFC%u81F4%u5927%u5806%u7684%u6D4B%u8BD5%u5931%u8D25%uFF0C%u751A%u81F3%u6D4B%u8BD5%u65E0%u6CD5%u8FDB%u884C%u3002%0A2.%20%u6D4B%u8BD5case%u4E4B%u95F4%u65E0%u6CD5%u5F88%u597D%u7684%u89E3%u8026%u3002%u7ED3%u679C%u540C%u6837%u662F%u6D4B%u8BD5%u4E0D%u591F%u7A33%u5B9A%u3002%u672F%u8BED%u662F%u6D4B%u8BD5%u4EE3%u7801%u5F88%u201C%u8106%u5F31%u201D%u3002%0A%0AJava%uFF0CPython%uFF0CJavaScript%u90FD%u6709%u81EA%u5DF1%u7684mock%u5E93%u3002Python%u7684Mock%u7C7B%uFF0CJava%u7684Mockito/PowerMock%uFF0CJavaScript%u7684Sinon%u3002%u4E8E%u662F%uFF0C%u5728%u7F51%u4E0A%u641C%u7D22%u4E86%u4E00%u4E0BC/C++%u7684Mock%u5E93%uFF0C%u4E8E%u662F%u770B%u5230%u4E86gtest%uFF0Cgmock%u3002%u7136%u540E%u5C31%u6709%u4E86%u8FD9%u4E00%u7BC7%u3002%0A%0A%23%23%20%u7B80%u4ECB%0A%u4E0D%u7528%u53BB%u7F51%u4E0A%u8D39%u5FC3%u627E%u6559%u7A0B%uFF0C%u4E24%u4E2A%u9879%u76EE%u7684%u6587%u6863%u90FD%u975E%u5E38%u68D2%u3002%u5165%u53E3%u7EDF%u4E00%u5728%5Bgtest%20GitHub%u9879%u76EE%u4E3B%u9875%5D%28https%3A//github.com/google/googletest%29%u4E0A%u3002%u800C%u4E14%u8BE5%u6587%u6863%u4E0D%u4EC5%u5F88%u597D%u7684%u4ECB%u7ECD%u4E86gtest%uFF0Cgmock%u7684%u7528%u6CD5%uFF0C%u5176%u4E2D%u8FD8%u6D89%u53CA%u4E86%u5F88%u591ATDD%u6216%u8005UnitTest%u7684%u771F%u77E5%u707C%u89C1%uFF0C%u5F88%u503C%u5F97%u8BFB%u4E00%u8BFB%u3002%0A%u8981%u4F7F%u7528gtest%u975E%u5E38%u7B80%u5355%uFF1A%u7F16%u8BD1%u51FAgtest%uFF0Cgmock%uFF0C%u518D%u94FE%u5165%u4F60%u7684%u6D4B%u8BD5%u7A0B%u5E8F%u3002%0A%0A%23%23%23%20%u7F16%u8BD1gtest/gmock%0Agtest%uFF0Cgmock%u5747%u7528cmake%u6765%u7BA1%u7406%u8DE8%u5E73%u53F0%uFF0C%u5148%u7528cmake%u6765%u751F%u6210Makefile%u3002%u7528%u547D%u4EE4%60cmake%20-G%20%22Unix%20Makefiles%22%20/path/to/CMakeList.txt%60%0A%0A%23%23%23%20Makefile%0A%u7136%u540E%u6309%u7167%u4E0B%u9762%u7F16%u5199Makefile%u3002%u6CE8%u610Fgmock_main%u662F%u4E00%u4E2Amain%u51FD%u6570%u6765%u8C03%u7528%u6240%u6709%u7684test%20case%uFF0C%u7701%u5F97%u81EA%u5DF1%u5199main%u51FD%u6570%u4E86%u3002%0A%60%60%60%0ACC%20%3D%20gcc%0ACPP%20%3D%20g++%0ALINK%20%3D%20g++%0ACFLAGS%20%3D%20-g%20-Wall%20-Werror%20-Wextra%20-std%3Dgnu99%0ACPPFLAGS%20%3D%20-g%20-Wall%20-Werror%20-Wextra%0ALIBS%20%3D%20-L./lib%20-lgtest%20-lgmock%20-lgmock_main%20-lpthread%0A%0AC__SOURCES%20%3D%20%24%28wildcard%20*.c%29%0ACPPSOURCES%20%3D%20%24%28wildcard%20*.cpp%29%0AOBJECTS%20%3D%20%24%28patsubst%20%25.c%2C%20%25.o%2C%20%24%28C__SOURCES%29%29%20%24%28patsubst%20%25.cpp%2C%20%25.o%2C%20%24%28CPPSOURCES%29%29%0ATARGET%20%3D%20test_exe%0A%0Afirst%3A%20all%0A%0A%25.o%3A%20%25.c%0A%20%20%20%20%24%28CC%29%20%24%28INCLUDES%29%20-c%20%24%28CFLAGS%29%20-o%20%24@%20%24%3C%0A%0A%25.o%3A%20%25.cpp%0A%20%20%20%20%24%28CPP%29%20%24%28INCLUDES%29%20-c%20%24%28CPPFLAGS%29%20-o%20%24@%20%24%3C%0A%0Aall%3A%20%24%28TARGET%29%0A%0A%24%28TARGET%29%3A%20%24%28OBJECTS%29%0A%20%20%20%20%24%28LINK%29%20%24%28CPPFLAGS%29%20%24%28LIBS%29%20-o%20%24%28TARGET%29%20%24%28OBJECTS%29%0A%0A.PHONY%20%3A%20clean%0A%0Aclean%3A%0A%20%20%20%20rm%20-f%20%24%28TARGET%29%20%24%28OBJECTS%29%0A%60%60%60%0A%0A%23%23%23%20Terms%0A%7C%20Meaning%20%20%20%20%7C%20Google%20Test%20Term%20%7C%20ISTQB%20Term%20%7C%0A%7C%20%3A--------%3A%20%7C%20%3A--------------%3A%20%7C%20%3A--------%3A%20%7C%0A%7C%20Exercise%20a%20particular%20program%20path%20with%20specific%20input%20values%20and%20verify%20the%20results%20%7C%20TEST%28%29%20%7C%20Test%20Case%0A%7CA%20set%20of%20several%20tests%20related%20to%20one%20component%20%7C%20Test%20Case%20%7C%20Test%20Suite%0A%0A%23%23%23%20Test%0A%60%60%60%0A%23include%20%22gtest/gtest.h%22%0A%23include%20%22gmock/gmock.h%22%0A%0Ausing%20%3A%3Atesting%3A%3AReturn%3B%0Ausing%20%3A%3Atesting%3A%3ATest%3B%0Ausing%20%3A%3Atesting%3A%3A_%3B%0Ausing%20%3A%3Atesting%3A%3AAtLeast%3B%0A%0ATEST%28TestCaseName%2C%20should_this_test_do%29%0A%7B%0A%09...%0A%20%20%20%20EXPECT_STREQ%28%22%7B%7D%22%2C%20str%29%3B%0A%7D%0A%60%60%60%0A%u4E2D%u95F4%u7684%u90A3%u5806namespace%u90FD%u662Fgtest/gmock%u5E93%u91CC%u5B9A%u4E49%u7684matcher%u5B8F%u6216%u8005%u5404%u79CD%u6709%u7528%u7684%u5B8F%u3002%0A%0A%23%23%23%20Test%20Fixture%0A%u5728%u6D4B%u8BD5%u6709%u91CD%u590D%u7684%u65F6%u5019%uFF0C%u5C31%u8981%u7528%u5230Test%20Fixture%u4E86%uFF0C%u4E5F%u5C31%u662FsetUp%20/%20tearDown%u3002%0A%60%60%60%0Aclass%20QueueTest%20%3A%20public%20%3A%3Atesting%3A%3ATest%20%7B%0A%20protected%3A%0A%20%20virtual%20void%20SetUp%28%29%20%7B%0A%20%20%20%20q1_.Enqueue%281%29%3B%0A%20%20%20%20q2_.Enqueue%282%29%3B%0A%20%20%20%20q2_.Enqueue%283%29%3B%0A%20%20%7D%0A%0A%20%20//%20virtual%20void%20TearDown%28%29%20%7B%7D%0A%0A%20%20Queue%3Cint%3E%20q0_%3B%0A%20%20Queue%3Cint%3E%20q1_%3B%0A%20%20Queue%3Cint%3E%20q2_%3B%0A%7D%3B%0A%0ATEST_F%28QueueTest%2C%20IsEmptyInitially%29%20%7B%0A%20%20EXPECT_EQ%280%2C%20q0_.size%28%29%29%3B%0A%7D%0A%0ATEST_F%28QueueTest%2C%20DequeueWorks%29%20%7B%0A%20%20int*%20n%20%3D%20q0_.Dequeue%28%29%3B%0A%20%20EXPECT_EQ%28NULL%2C%20n%29%3B%0A%0A%20%20n%20%3D%20q1_.Dequeue%28%29%3B%0A%20%20ASSERT_TRUE%28n%20%21%3D%20NULL%29%3B%0A%20%20EXPECT_EQ%281%2C%20*n%29%3B%0A%20%20EXPECT_EQ%280%2C%20q1_.size%28%29%29%3B%0A%20%20delete%20n%3B%0A%0A%20%20n%20%3D%20q2_.Dequeue%28%29%3B%0A%20%20ASSERT_TRUE%28n%20%21%3D%20NULL%29%3B%0A%20%20EXPECT_EQ%282%2C%20*n%29%3B%0A%20%20EXPECT_EQ%281%2C%20q2_.size%28%29%29%3B%0A%20%20delete%20n%3B%0A%7D%0A%60%60%60%0A%23%23%23%23%20constructor/destructor%20vs.%20SetUp/TearDown%0A%3EWhen%20you%20need%20to%20write%20per-test%20set-up%20and%20tear-down%20logic%2C%20you%20have%20the%20choice%20between%20using%20the%20test%20fixture%20constructor/destructor%20or%20SetUp%28%29/TearDown%28%29.%20The%20former%20is%20usually%20preferred%2C%20as%20it%20has%20the%20following%20benefits%3A%0A%0A%3E-%20By%20initializing%20a%20member%20variable%20in%20the%20constructor%2C%20we%20have%20the%20option%20to%20make%20it%20const%2C%20which%20helps%20prevent%20accidental%20changes%20to%20its%20value%20and%20makes%20the%20tests%20more%20obviously%20correct.%0A-%20**In%20case%20we%20need%20to%20subclass%20the%20test%20fixture%20class%2C%20the%20subclass%27%20constructor%20is%20guaranteed%20to%20call%20the%20base%20class%27%20constructor%20first%2C%20and%20the%20subclass%27%20destructor%20is%20guaranteed%20to%20call%20the%20base%20class%27%20destructor%20afterward.%20**With%20SetUp%28%29/TearDown%28%29%2C%20a%20subclass%20may%20make%20the%20mistake%20of%20forgetting%20to%20call%20the%20base%20class%27%20SetUp%28%29/TearDown%28%29%20or%20call%20them%20at%20the%20wrong%20moment.%0A%0A%3E%20Benefit%20for%20using%20SetUp/TearDown%3A%0A%0A%3E-%20If%20the%20tear-down%20operation%20could%20throw%20an%20exception%2C%20you%20must%20use%20TearDown%28%29%20as%20opposed%20to%20the%20destructor%2C%20as%20throwing%20in%20a%20destructor%20leads%20to%20undefined%20behavior%20and%20usually%20will%20kill%20your%20program%20right%20away.%20Note%20that%20many%20standard%20libraries%20%28like%20STL%29%20may%20throw%20when%20exceptions%20are%20enabled%20in%20the%20compiler.%20Therefore%20you%20should%20prefer%20TearDown%28%29%20if%20you%20want%20to%20write%20portable%20tests%20that%20work%20with%20or%20without%20exceptions.%0A-%20The%20assertion%20macros%20throw%20an%20exception%20when%20flag%20--gtest_throw_on_failure%20is%20specified.%20Therefore%2C%20you%20shouldn%27t%20use%20Google%20Test%20assertions%20in%20a%20destructor%20if%20you%20plan%20to%20run%20your%20tests%20with%20this%20flag.%0A-%20In%20a%20constructor%20or%20destructor%2C%20you%20cannot%20make%20a%20virtual%20function%20call%20on%20this%20object.%20%28You%20can%20call%20a%20method%20declared%20as%20virtual%2C%20but%20it%20will%20be%20statically%20bound.%29%20Therefore%2C%20if%20you%20need%20to%20call%20a%20method%20that%20will%20be%20overriden%20in%20a%20derived%20class%2C%20you%20have%20to%20use%20SetUp%28%29/TearDown%28%29.%0A%0A%u7B80%u8A00%u4E4B%uFF0C%u5728%u903B%u8F91%u4E0A%uFF0C%u8FD9%u4E24%u7EC4%u7684%u4F5C%u7528%u76F8%u540C%uFF0C%u90FD%u662F%u6BCF%u4E2A%u6D4B%u8BD5%u4E4B%u524D%u4E4B%u540E%u4F1A%u505A%u4E00%u4E9B%u5904%u7406%u5DE5%u4F5C%u3002Constructor/Destructor%u7684%u597D%u5904%u662F%u63D0%u4F9B%u4E86%u7EE7%u627F%u3002setUp/tearDown%u7684%u597D%u5904%u662F%u53EF%u4EE5%u5904%u7406exception%uFF0C%u8FD9%u662F%u4E0D%u80FD%u653E%u5728%u6790%u6784%u51FD%u6570%u91CC%u7684%u3002%0A%23%23%23%20SetUpTestCase%20/%20TearDownTestCase%0ATest%20Case%u7EA7%u522B%u7684SetUp/TearDown%0A%60%60%60%0Aclass%20FooTest%20%3A%20public%20%3A%3Atesting%3A%3ATest%20%7B%0A%20protected%3A%0A%20%20//%20Per-test-case%20set-up.%0A%20%20//%20Called%20before%20the%20first%20test%20in%20this%20test%20case.%0A%20%20//%20Can%20be%20omitted%20if%20not%20needed.%0A%20%20static%20void%20SetUpTestCase%28%29%20%7B%0A%20%20%20%20shared_resource_%20%3D%20new%20...%3B%0A%20%20%7D%0A%0A%20%20//%20Per-test-case%20tear-down.%0A%20%20//%20Called%20after%20the%20last%20test%20in%20this%20test%20case.%0A%20%20//%20Can%20be%20omitted%20if%20not%20needed.%0A%20%20static%20void%20TearDownTestCase%28%29%20%7B%0A%20%20%20%20delete%20shared_resource_%3B%0A%20%20%20%20shared_resource_%20%3D%20NULL%3B%0A%20%20%7D%0A%0A%20%20//%20You%20can%20define%20per-test%20set-up%20and%20tear-down%20logic%20as%20usual.%0A%20%20virtual%20void%20SetUp%28%29%20%7B%20...%20%7D%0A%20%20virtual%20void%20TearDown%28%29%20%7B%20...%20%7D%0A%0A%20%20//%20Some%20expensive%20resource%20shared%20by%20all%20tests.%0A%20%20static%20T*%20shared_resource_%3B%0A%7D%3B%0A%60%60%60%0A%23%23%23%20SetUp/TearDown%20Environment%20%0A%3E-%20First%2C%20you%20subclass%20the%20%3A%3Atesting%3A%3AEnvironment%20class%20to%20define%20a%20test%20environment%2C%20which%20knows%20how%20to%20set-up%20and%20tear-down%3A%0A%3E-%20Then%2C%20you%20register%20an%20instance%20of%20your%20environment%20class%20with%20Google%20Test%20by%20calling%20the%20%60%3A%3Atesting%3A%3AAddGlobalTestEnvironment%28%29%60%20function%3A%0A%3ENow%2C%20when%20RUN_ALL_TESTS%28%29%20is%20called%2C%20it%20first%20calls%20the%20SetUp%28%29%20method%20of%20the%20environment%20object%2C%20then%20runs%20the%20tests%20if%20there%20was%20no%20fatal%20failures%2C%20and%20finally%20calls%20TearDown%28%29%20of%20the%20environment%20object.%0A-%20It%27s%20OK%20to%20register%20multiple%20environment%20objects.%20In%20this%20case%2C%20their%20SetUp%28%29%20will%20be%20called%20in%20the%20order%20they%20are%20registered%2C%20and%20their%20TearDown%28%29%20will%20be%20called%20in%20the%20reverse%20order.%0A-%20Note%20that%20Google%20Test%20takes%20ownership%20of%20the%20registered%20environment%20objects.%20Therefore%20do%20not%20delete%20them%20by%20yourself.%0A%0A%60%60%60%0Aclass%20Environment%20%7B%0A%20public%3A%0A%20%20virtual%20%7EEnvironment%28%29%20%7B%7D%0A%20%20//%20Override%20this%20to%20define%20how%20to%20set%20up%20the%20environment.%0A%20%20virtual%20void%20SetUp%28%29%20%7B%7D%0A%20%20//%20Override%20this%20to%20define%20how%20to%20tear%20down%20the%20environment.%0A%20%20virtual%20void%20TearDown%28%29%20%7B%7D%0A%7D%3B%0A%0AEnvironment*%20AddGlobalTestEnvironment%28Environment*%20env%29%3B%0A%60%60%60%0A%23%23%23%20%u65AD%u8A00%0A%u6709%u4E24%u79CD%u65AD%u8A00EXPECT%5C_xxx%u548CASSERT%5C_xxx%u3002%u524D%u8005%u4F1A%u8BA9%u6D4B%u8BD5%u7EC8%u6B62%uFF0C%u540E%u8005%u4E0D%u4F1A%uFF0C%u53EA%u4F1A%u8BA9%u6D4B%u8BD5fail%u3002%0A%0A%0A%23%23%20gmock%0A%u4E4B%u6240%u4EE5%u8981%u5207%u5230gtest%uFF0C%u552F%u4E00%u7684%u539F%u56E0%u5C31%u662Fgmock%uFF0C%u6240%u4EE5%u8981%u4E13%u5F00%u4E00%u7AE0%u91CD%u70B9%u4ECB%u7ECD%u4E00%u4E0B%u3002%u6240%u6709%u5185%u5BB9%u5747%u6765%u81EA%u4E8E%u5B98%u65B9%u6587%u6863%u3002%u5185%u5BB9%u6DF1%u5EA6%u7531%u6D45%u5165%u6DF1%uFF0C%u4F9D%u6B21%u5982%u4E0B%uFF1A%0A-%20%5BGoogle%20Mock%20for%20Dummies%5D%28https%3A//github.com/google/googletest/blob/master/googlemock/docs/ForDummies.md%29%0A-%20%5BCookBook%5D%28https%3A//github.com/google/googletest/blob/master/googlemock/docs/CookBook.md%29%0A%0A%u6700%u540E%u8FD8%u6709%u53C2%u8003%u624B%u518C%3A%0A-%20%5BCheat%20Sheet%5D%28https%3A//github.com/google/googletest/blob/master/googlemock/docs/CheatSheet.md%29%0A%0A%23%23%23%20%u7B80%u4ECB%0A%3EGoogle%20C++%20Mocking%20Framework%20%28or%20Google%20Mock%20for%20short%29%20is%20a%20library%20%28sometimes%20we%20also%20call%20it%20a%20%22framework%22%20to%20make%20it%20sound%20cool%29%20for%20creating%20mock%20classes%20and%20using%20them.%20It%20does%20to%20C++%20what%20jMock%20and%20EasyMock%20do%20to%20Java.%0A%0A%23%23%23%23%20%u4F55%u4E3AMock%uFF1F%0A%3E**Mocks**%20are%20objects%20pre-programmed%20with%20expectations%2C%20which%20form%20a%20specification%20of%20the%20calls%20they%20are%20expected%20to%20receive.%0A%0A%u76F8%u5E94%u7684%u8FD8%u6709Fake%u548CStub%0A%3E**Fake**%20objects%20have%20working%20implementations%2C%20but%20usually%20take%20some%20shortcut%20%28perhaps%20to%20make%20the%20operations%20less%20expensive%29%2C%20which%20makes%20them%20not%20suitable%20for%20production.%20An%20in-memory%20file%20system%20would%20be%20an%20example%20of%20a%20fake.%0A%0Agmock%u7684%u6587%u6863%u91CC%u53EA%u63D0%u5230%u4E86Fake%uFF0C%u4ECEMartin%20Fowler%u7684%u6587%u7AE0%5BMocks%20Aren%27t%20Stubs%5D%28https%3A//martinfowler.com/articles/mocksArentStubs.html%29%u4E2D%u6458%u5F55%u5982%u4E0B%3A%0A%3E**Stubs**%20provide%20canned%20answers%20to%20calls%20made%20during%20the%20test%2C%20usually%20not%20responding%20at%20all%20to%20anything%20outside%20what%27s%20programmed%20in%20for%20the%20test.%0A%0A%u4E09%u8005%u4F5C%u7528%u76F8%u540C%uFF0C%u90FD%u662F%u6A21%u62DF%u7CFB%u7EDF%u5176%u4ED6%u90E8%u5206%u7684%u529F%u80FD%uFF0C%u8FBE%u5230%u4EE3%u7801%u9694%u79BB%u7684%u6548%u679C%uFF0C%u65B9%u4FBF%u6D4B%u8BD5%u3002%u4F46%u662FMock%u7684%u7279%u70B9%u662F%u66F4OO%u5316%uFF0C%u4E5F%u7B26%u5408TDD%u6216%u8005BDD%u7684%u601D%u60F3%u2014%u2014%u9488%u5BF9%u4E00%u4E2Aobject%u8BBE%u7F6E%u671F%u5F85%uFF0C%u518D%u5BF9%u9F50verify%u3002%0A%0AMocks%20vs.%20Stubs%20-%20from%20Martin%20Fowler%0A%3EIn%20order%20to%20use%20state%20verification%20on%20the%20stub%2C%20I%20need%20to%20make%20some%20extra%20methods%20on%20the%20stub%20to%20help%20with%20verification.%20As%20a%20result%20the%20stub%20implements%20MailService%20but%20adds%20extra%20test%20methods.%0A%0A%3EMock%20objects%20always%20use%20behavior%20verification%2C%20a%20stub%20can%20go%20either%20way.%20Meszaros%20refers%20to%20stubs%20that%20use%20behavior%20verification%20as%20a%20Test%20Spy.%20The%20difference%20is%20in%20how%20exactly%20the%20double%20runs%20and%20verifies%20and%20I%27ll%20leave%20that%20for%20you%20to%20explore%20on%20your%20own.%0A%0A%23%23%23%23%20Getting%20Started%0AClass%20to%20Mock%0A%60%60%60%0Aclass%20Turtle%20%7B%0A%20%20...%0A%20%20virtual%20%7ETurtle%28%29%20%7B%7D%0A%20%20virtual%20void%20PenUp%28%29%20%3D%200%3B%0A%20%20virtual%20void%20PenDown%28%29%20%3D%200%3B%0A%20%20virtual%20void%20Forward%28int%20distance%29%20%3D%200%3B%0A%7D%3B%0A%60%60%60%0AMock%20class%0A%60%60%60%0A%23include%20%22gmock/gmock.h%22%20%20//%20Brings%20in%20Google%20Mock.%0Aclass%20MockTurtle%20%3A%20public%20Turtle%20%7B%0A%20public%3A%0A%20%20...%0A%20%20MOCK_METHOD0%28PenUp%2C%20void%28%29%29%3B%0A%20%20MOCK_METHOD0%28PenDown%2C%20void%28%29%29%3B%0A%20%20MOCK_METHOD1%28Forward%2C%20void%28int%20distance%29%29%3B%0A%7D%3B%0A%60%60%60%0A%u9488%u5BF9%u5F85Mock%u7684Turtle%20class%u8981%u6CE8%u610F%u7684%u662F%3A%0A%3ENote%20that%20the%20**destructor%20of%20Turtle%20must%20be%20virtual**%2C%20as%20is%20the%20case%20for%20all%20classes%20you%20intend%20to%20inherit%20from%20-%20otherwise%20the%20destructor%20of%20the%20derived%20class%20will%20not%20be%20called%20when%20you%20delete%20an%20object%20through%20a%20base%20pointer%2C%20and%20you%27ll%20get%20corrupted%20program%20states%20like%20memory%20leaks.%0A%0AUse%20it%0A%60%60%60%0A%23include%20%22path/to/mock-turtle.h%22%0A%23include%20%22gmock/gmock.h%22%0A%23include%20%22gtest/gtest.h%22%0Ausing%20%3A%3Atesting%3A%3AAtLeast%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%231%0A%0ATEST%28PainterTest%2C%20CanDrawSomething%29%20%7B%0A%20%20MockTurtle%20turtle%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%232%0A%20%20EXPECT_CALL%28turtle%2C%20PenDown%28%29%29%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%233%0A%20%20%20%20%20%20.Times%28AtLeast%281%29%29%3B%0A%0A%20%20Painter%20painter%28%26turtle%29%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%234%0A%0A%20%20EXPECT_TRUE%28painter.DrawCircle%280%2C%200%2C%2010%29%29%3B%0A%7D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20//%20%235%0A%0Aint%20main%28int%20argc%2C%20char**%20argv%29%20%7B%0A%20%20//%20The%20following%20line%20must%20be%20executed%20to%20initialize%20Google%20Mock%0A%20%20//%20%28and%20Google%20Test%29%20before%20running%20the%20tests.%0A%20%20%3A%3Atesting%3A%3AInitGoogleMock%28%26argc%2C%20argv%29%3B%0A%20%20return%20RUN_ALL_TESTS%28%29%3B%0A%7D%0A%60%60%60%0A%u5982%u679C%u4F60%u613F%u610F%u7684%u8BDD%uFF0C%u53EF%u4EE5%u81EA%u5DF1%u5199main%uFF0C%u5982%u679C%u4F60%u60F3%u5077%u61D2%uFF0C%u8BB0%u5F97%u5728Makefile%u91CClink%20gmock_main%u3002%0A%u6309Google%u7684%u8BF4%u6CD5%uFF0Cgmock%u4E5F%u53EF%u4EE5%u548C%u5176%u4ED6%u6D4B%u8BD5%u6846%u67B6%u517C%u5BB9%uFF0C%u4F46%u603B%u611F%u89C9%u633A%u60AC%u7684%u3002%0A%0A%23%23%23%23%20%u4E00%u4E9B%u6709%u7528%u7684%u5DE5%u5177%0A**Marcher**%0A%60EXPECT_CALL%28turtle%2C%20Forward%28_%29%29%3B%60%0A%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%u4EE5%u4EFB%u610F%u53C2%u6570%u8C03%u7528Forward%0A%60EXPECT_CALL%28turtle%2C%20Forward%28Ge%28100%29%29%29%3B%60%0A%26nbsp%3B%26nbsp%3B%26nbsp%3B%26nbsp%3B%20%u4EE5%u5927%u4E8E100%u7684%u6574%u6570%u8C03%u7528Forward%0A%09%0A**Cardinalities%3A%20How%20Many%20Times%20Will%20It%20Be%20Called%3F**%0A%3E-%20If%20neither%20WillOnce%28%29%20nor%20WillRepeatedly%28%29%20is%20in%20the%20EXPECT_CALL%28%29%2C%20the%20inferred%20cardinality%20is%20Times%281%29.%0A-%20If%20there%20are%20n%20WillOnce%28%29%27s%20but%20no%20WillRepeatedly%28%29%2C%20where%20n%20%3E%3D%201%2C%20the%20cardinality%20is%20Times%28n%29.%0A-%20If%20there%20are%20n%20WillOnce%28%29%27s%20and%20one%20WillRepeatedly%28%29%2C%20where%20n%20%3E%3D%200%2C%20the%20cardinality%20is%20Times%28AtLeast%28n%29%29.%0A%0A%60%60%60%0AEXPECT_CALL%28mockObj%2C%20func%28%29%29%0A%09.Times%28AtLeast%281%29%29%0A%09.WillOnce%28Return%28123%29%29%0A%09.WillRepeatedly%28Return%28456%29%29%0A%60%60%60%0A%u4E0A%u9762%u8FD9%u6BB5%u4EE3%u7801%u8981%u6C42func%u51FD%u6570%u81F3%u5C11%u8FD0%u884C%u4E00%u6B21%uFF0C%u7B2C%u4E00%u6B21%u8FD4%u56DE123%uFF0C%u4E4B%u540E%u6BCF%u6B21%u8FD4%u56DE456%u3002%0A%0A%3E**Important%20note**%3A%20The%20EXPECT_CALL%28%29%20statement%20evaluates%20the%20action%20clause%20only%20once%2C%20even%20though%20the%20action%20may%20be%20performed%20many%20times.%20Therefore%20you%20must%20be%20careful%20about%20side%20effects.%20The%20following%20may%20not%20do%20what%20you%20want%3A%0A%0A%60%60%60%0Aint%20n%20%3D%20100%3B%0AEXPECT_CALL%28turtle%2C%20GetX%28%29%29%0A.Times%284%29%0A.WillRepeatedly%28Return%28n++%29%29%3B%0A%60%60%60%0A%u56E0%u4E3AReturn%u662F%u5B8F%uFF0C%u6240%u4EE5%u53EA%u4F1A%u66FF%u6362%u4E00%u6B21%uFF0C%u6240%u4EE5%u4E0D%u7BA1GetX%u8C03%u7528%u51E0%u6B21%uFF0C%u8FD4%u56DE%u90FD%u662F101%uFF0C%u800C%u4E0D%u662F101%2C102%2C103%2C...%0A%0A**All%20Expectations%20Are%20Sticky**%0A%u6240%u8C13%u7684sticky%u5C31%u662FEXPECT%5C_CALL%u603B%u662F%u751F%u6548%u7684%uFF0C%u9664%u975E%u4F60%u663E%u793A%u7684%u5C06%u5176%u5931%u6548%u3002%u5982%u4E0B%uFF0C%u6240%u6709%u7684EXPECT%5C_CALL%u90FD%u4F1A%u751F%u6548%uFF0C%u90A3%u4E48%u6700%u540E%u4E00%u4E2A%u4F1A%u8986%u76D6%u524D%u9762%u6240%u6709%u7684%uFF0C%u4E5F%u5C31%u662FGetX%u603B%u4F1A%u8FD4%u56DE10%u3002%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3AReturn%3B%0A...%0Afor%20%28int%20i%20%3D%20n%3B%20i%20%3E%200%3B%20i--%29%20%7B%0A%20%20EXPECT_CALL%28turtle%2C%20GetX%28%29%29%0A%20%20%20%20%20%20.WillOnce%28Return%2810*i%29%29%3B%0A%7D%0A%60%60%60%0A%u5982%u679C%u5E0C%u671B%u4ED6%u8FD4%u56DE%uFF0C30%2C20%2C10%uFF0C...%uFF0C%u5E94%u8BE5%u8FD9%u4E48%u5199%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3AReturn%3B%0A...%0Afor%20%28int%20i%20%3D%20n%3B%20i%20%3E%200%3B%20i--%29%20%7B%0A%20%20EXPECT_CALL%28turtle%2C%20GetX%28%29%29%0A%20%20%20%20.WillOnce%28Return%2810*i%29%29%0A%20%20%20%20.RetiresOnSaturation%28%29%3B%0A%7D%0A%60%60%60%0A%60RetireOnSaturation%60%u5C31%u662F%u663E%u793A%u7684%u8BA9%u5176%u5931%u6548%u3002%u8FD8%u6709%u4E00%u4E2A%u529E%u6CD5%uFF1A%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3AInSequence%3B%0Ausing%20%3A%3Atesting%3A%3AReturn%3B%0A...%0A%7B%0A%20%20InSequence%20s%3B%0A%0A%20%20for%20%28int%20i%20%3D%201%3B%20i%20%3C%3D%20n%3B%20i++%29%20%7B%0A%20%20%20%20EXPECT_CALL%28turtle%2C%20GetX%28%29%29%0A%20%20%20%20%20%20%20%20.WillOnce%28Return%2810*i%29%29%0A%20%20%20%20%20%20%20%20.RetiresOnSaturation%28%29%3B%0A%20%20%7D%0A%7D%0A%60%60%60%0A%u56E0%u4E3AInSequence%u7684%u5173%u7CFB%uFF0C%u5728%u524D%u9762%u7684EXPECT%5C_CALL%u751F%u6548%u4EE5%u540E%uFF0C%u5C31%u4F1A%u81EA%u52A8%u5931%u6548%uFF0C%u6240%u4EE5%u4E0D%u4F1A%u4EA7%u751F%u8986%u76D6%u7684%u6548%u679C%u3002%0A**Ordered%20vs%20Unordered%20Calls**%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3AInSequence%3B...%0ATEST%28FooTest%2C%20DrawsLineSegment%29%20%7B%0A%20%20...%0A%20%20%7B%0A%20%20%20%20InSequence%20dummy%3B%0A%0A%20%20%20%20EXPECT_CALL%28turtle%2C%20PenDown%28%29%29%3B%0A%20%20%20%20EXPECT_CALL%28turtle%2C%20Forward%28100%29%29%3B%0A%20%20%20%20EXPECT_CALL%28turtle%2C%20PenUp%28%29%29%3B%0A%20%20%7D%0A%20%20Foo%28%29%3B%0A%7D%0A%60%60%60%0A**Expecting%20Partially%20Ordered%20Calls**%0AGoogle%20Mock%20allows%20you%20to%20impose%20an%20arbitrary%20DAG%20%28directed%20acyclic%20graph%29%20on%20the%20calls.%20One%20way%20to%20express%20the%20DAG%20is%20to%0Ause%20the%20After%20clause%20of%20EXPECT_CALL%20.%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3ASequence%3B%0ASequence%20s1%2C%20s2%3B%0AEXPECT_CALL%28foo%2C%20A%28%29%29%0A%09.InSequence%28s1%2C%20s2%29%3B%0AEXPECT_CALL%28bar%2C%20B%28%29%29%0A%09.InSequence%28s1%29%3B%0AEXPECT_CALL%28bar%2C%20C%28%29%29%0A%09.InSequence%28s2%29%3B%0AEXPECT_CALL%28foo%2C%20D%28%29%29%0A%09.InSequence%28s2%29%3B%0A%60%60%60%0Aspecifies%20the%20following%20DAG%20%28where%20s1%20is%20A%20-%3E%20B%20%2C%20and%20s2%20is%20A%20-%3E%20C%20-%3E%20D%20%29%3A%0A%60%60%60%0A%09%20+---%3E%20B%0A%20%20%20%20%20%7C%0AA%20---%7C%0A%09%20%7C%0A%09%20+---%3E%20C%20---%3E%20D%0A%60%60%60%0A**Uninteresting%20Calls**%0A%u8FD9%u662Fgmock%u62A5%u7684warning%u3002%u5F53%u9488%u5BF9%u67D0%u4E00%u4E2A%u5F85%u6D4B%u51FD%u6570%u8BBE%u7F6E%u4E86EXPECT%20spec%uFF0C%u5374%u5E76%u6CA1%u6709%u8C03%u7528%u7684%u65F6%u5019%uFF0C%u5C31%u4F1A%u62A5%u8FD9%u4E2Awarning%u3002%u6B64%u65F6gmock%u8BA4%u4E3A%u4F60%u5BF9%u8BE5%u51FD%u6570%u5E76%u6CA1%u6709%u5174%u8DA3%uFF0C%u6240%u4EE5%u5C31%u4E0D%u9700%u8981%u8FD9%u4E2AEXPECT%20spec%u3002%u5F53%u7136%u4F60%u53EF%u4EE5%u9009%u62E9%u5FFD%u7565%u8FD9%u4E2Awarning%uFF0C%u4F46%u6211%u8BA4%u4E3A%u51FA%u8FD9%u4E2Awarning%u7684%u65F6%u5019%uFF0C%u591A%u534A%u662Ftest%u6F0F%u5199%u4E86%u4EC0%u4E48%u3002%0A%0A**Returning%20Live%20Values%20from%20Mock%20Methods**%0A%60%60%60%0Ausing%20testing%3A%3AByRef%3B%0Ausing%20testing%3A%3AReturn%3B%0Aclass%20MockFoo%20%3A%20public%20Foo%20%7B%0A%09public%3A%0A%09%09MOCK_METHOD0%28GetValue%2C%20int%28%29%29%3B%0A%7D%3B%0A...%0Aint%20x%20%3D%200%3B%0AMockFoo%20foo%3B%0AEXPECT_CALL%28foo%2C%20GetValue%28%29%29%0A%09//%20.WillRepeatedly%28Return%28ByRef%28x%29%29%29%3B%20X%20error%0A%09.WillRepeatedly%28ReturnPointee%28x%29%29%3B%0Ax%20%3D%2042%3B%0AEXPECT_EQ%2842%2C%20foo.GetValue%28%29%29%3B%0A%60%60%60%0A%0A%23%23%23%20%u4E00%u4E9B%u6D4B%u8BD5case%0A%u5199Unit%20Test%u5E76%u4E0D%u50CF%u60F3%u50CF%u7684%u90A3%u4E48%u7B80%u5355%uFF0C%u5E76%u4E0D%u662F%u8C03%u7528%u4E86%u6846%u67B6%uFF0C%u9488%u5BF9%u6BCF%u4E2A%u51FD%u6570%u5199test%20case%u5C31%u53EF%u4EE5%u3002%u6309%u6211%u76EE%u524D%u7684%u7406%u89E3%u6709%u4EE5%u4E0B%u51E0%u79CD%u6311%u6218%uFF1A%0A1.%20Test%20Case%u5982%u4F55%u89E3%u8026%u3002%u4E0D%u8981%u6709%u91CD%u590D%u6D4B%u8BD5%28overlap%29%u3002%0A%u4F8B%u5982%uFF1A%u5728%u5199A%u51FD%u6570%u7684%u65F6%u5019%uFF0C%u5199%u4E86%u6D4B%u8BD5testA%uFF0CB%u51FD%u6570%u4F1A%u8C03%u7528A%u51FD%u6570%uFF0C%u90A3%u4E48%u5728%u5199%u5B8CA%u4E4B%u540E%u5199B%u7684%u6D4B%u8BD5testB%u65F6%uFF0C%u662F%u5426%u8981%u5C06A%20mock/fake/stub%u6389%3F%20%u5982%u679C%u4E0D%u5C06A%u51FD%u6570Fake%u6389%uFF0C%u5219testA%u548CtestB%u4E4B%u95F4%u5C31%u662F%u6709overlap%u3002%u6211%u8BA4%u4E3A%u8FD9%u4E4B%u95F4%u53EF%u4EE5%u6709%u53D6%u820D%uFF0C%u6700%u4F73%u72B6%u6001%u5E94%u5F53%u662F%u6B64%u65F6%uFF0C%u5C06testA%u5220%u9664%uFF0C%u53EA%u4FDD%u7559testB%u3002%u4F46%u4ECD%u5E94%u6839%u636E%u5177%u4F53%u60C5%u51B5%u800C%u5B9A%u3002%0A%0A2.%20%u5982%u4F55%u9488%u5BF9%u4F9D%u8D56%u5173%u7CFB%u8FDB%u884Cmock%u5316%u3002%0A%u4F8B%u5982%u51FA%u73B0%u8FD9%u6837%u7684%u8BED%u53E5%3A%20%60B%20%3D%20new%20A%60%uFF0C%u5219%u7C7BB%u4F9D%u8D56%u4E8E%u7C7BA%u3002%u4F46%u7C7BA%u5E76%u6CA1%u6709%u5FC5%u8981%u7F16%u8BD1%u8FDBtest%u3002%u56E0%u4E3A%u4E00%u65E6%u52A0%u5165A%uFF0C%u5219%u52BF%u5FC5%u4F1A%u5F15%u5165%u66F4%u591A%u4F9D%u8D56%u5173%u7CFB%uFF0C%u800C%u5BFC%u81F4test%u7F16%u8BD1%u5D29%u6E83%u3002%u4F9D%u8D56%u5173%u7CFB%u7684%u89E3%u51B3%u65E0%u7A77%u65E0%u5C3D%u3002%u5728%u505AAndroid%u7684%u5355%u5143%u6D4B%u8BD5%u65F6%uFF0C%u53EF%u4EE5%u7528PowerMock%u53D6%u4EE3Mockito%u6765Mock%u6784%u9020%u51FD%u6570%uFF0C%u5C06%u6784%u9020%u51FD%u6570Fake%u5316%u6210%u7C7B%u4F3C%u5DE5%u5382%u51FD%u6570%uFF0C%u8FD4%u56DE%u7C7B%u5B9E%u4F8B%u3002%u5177%u4F53%u53C2%u770B%u8FD9%u7BC7%u535A%u6587%5BAndroid%u5355%u5143%u6D4B%u8BD5%5D%28https%3A//zhougy0717.github.io/2016/10/19/Android%25E5%258D%2595%25E5%2585%2583%25E6%25B5%258B%25E8%25AF%2595/%29%u3002%0A%u4F46%u5B9E%u9645%u4E0A%uFF0C%u6309%u7167%u73B0%u5728%u7684%u7406%u89E3%uFF0C%u5176%u5B9EMock%u6784%u9020%u51FD%u6570%u662F%u4E0D%u53EF%u53D6%u7684%uFF0C%u9996%u5148%u9020%u6210%u88AB%u6D4B%u4EE3%u7801spec%u4E0D%u6E05%u6670%uFF0C%u8BD5%u60F3%u4E00%u4E2A%u6784%u9020%u51FD%u6570%u600E%u4E48%u4F1A%u8FD4%u56DE%u53E6%u4E00%u4E2A%u7C7B%u7684%u5B9E%u4F8B%u3002%u5176%u6B21%uFF0C%u5728C++%u4E2D%u5F88%u96BE%u505A%u5230Mock%u6784%u9020%u51FD%u6570%u3002%u597D%u7684%u505A%u6CD5%u5E94%u5F53%u662F%u8FD0%u7528Dependency%20Injection%u3002%u4F8B%u5982%uFF1A%0A%60%60%60%0Aclass%20A%20%0A%7B%7D%0A%0Aclass%20B%0A%7B%0A%09void%20func%28%29%0A%09%7B%0A%09%09A*%20a%20%3D%20new%20A%3B%0A%09%7D%0A%7D%0A%60%60%60%0A%u7C7BB%u5E94%u5F53%u6539%u5199%u4E3A%uFF1A%0A%60%60%60%0Aclass%20B%0A%7B%0A%09void%20func%28A*%29%0A%09%7B%0A%09%09...%0A%09%7D%0A%7D%0A%60%60%60%0A%u5C06%u7C7BA%u6307%u9488%u4F20%u5165%uFF0C%u89E3%u51B3dependency%u7684%u95EE%u9898%u3002%0A%0A3.%20%u5982%u4F55%u80FD%u591F%u8BA9%u6D4B%u8BD5%u7A33%u5B9A%uFF0C%u5728%u4EFB%u610F%u73AF%u5883%u4E0B%u5747%u8FD4%u56DE%u540C%u6837%u7684%u6D4B%u8BD5%u7ED3%u679C%u3002%0A%u8FD9%u4E2A%u4E00%u822C%u6D89%u53CA%u6D4B%u8BD5%u73AF%u5883%u7684%u5F71%u54CD%u3002%u4F8B%u5982%u8C03%u7528%u7F51%u7EDC%u76F8%u5173%u7684%u529F%u80FD%uFF0C%u5728%u6CA1%u6709%u7F51%u7EDC%u7684%u73AF%u5883%u5C31%u6CA1%u6CD5%u8FDB%u884C%u3002%u518D%u4F8B%u5982%u6D4B%u8BD5%u65F6%u64CD%u4F5C%u771F%u5B9E%u5177%u4F53%u6587%u4EF6%uFF0C%u5219%u8BE5%u6587%u4EF6%u88AB%u6D4B%u8BD5%u5916%u4EBA%u4E3A%u6216%u4EE3%u7801%u4FEE%u6539%uFF0C%u5219%u6D4B%u8BD5%u53EF%u80FD%u5C31%u4F1A%u83AB%u540D%u5931%u8D25%u3002%0A%u9488%u5BF9%u8FD9%u4E9B%u60C5%u51B5%uFF0C%u6211%u4EEC%u5E94%u5F53%u5728%u6D4B%u8BD5%u4E2D%u5C3D%u91CF%u907F%u514D%u3002%u4F8B%u5982%u524D%u8005%uFF0C%u6211%u4EEC%u5E94%u5F53%u5BF9%u7F51%u7EDC%u63A5%u53E3%u8FDB%u884CMock%u5316%uFF0C%u540E%u8005%u5E94%u5F53%u5728%u6D4B%u8BD5%u7684setUp%u548CtearDown%u4E2D%u751F%u6210%u865A%u5047%u6587%u4EF6%u7528%u4E8E%u6D4B%u8BD5%uFF0C%u5E76%u5728%u6D4B%u8BD5%u5B8C%u6210%u65F6%u505A%u6E05%u7406%u3002%0A%0A4.%20%u4F1A%u56E0%u4E3A%u5F88%u5C0F%u7684%u88AB%u6D4B%u4EE3%u7801%u6539%u52A8%uFF0C%u800C%u5BFC%u81F4%u5927%u9762%u79EF%u6D4B%u8BD5%u5931%u8D25%uFF0C%u751A%u81F3%u6D4B%u8BD5%u5D29%u6E83%u3002%0A%u8FD9%u4E2A%u5C31%u662Fgmock%u6587%u6863%u4E2D%u63D0%u5230%u7684%u8981%u9488%u5BF9%u63A5%u53E3%u7F16%u7A0B%uFF0C%u9488%u5BF9%u63A5%u53E3%u6D4B%u8BD5%u3002Robert%20C%B7Martin%u5728%u300A%u654F%u6377%u8F6F%u4EF6%u5F00%u53D1%uFF0D%u539F%u5219%u3001%u6A21%u5F0F%u4E0E%u5B9E%u8DF5%u300B%u4E00%u4E66%u4E2D%u6709%u63D0%u51FA%uFF1A%u6240%u6709%u7684%u4EE3%u7801%u90FD%u5E94%u4F9D%u8D56%u4E8E%u62BD%u8C61%u63A5%u53E3%u3002%u56E0%u4E3A%u62BD%u8C61%u63A5%u53E3%u662F%u7ECF%u8FC7%u62BD%u8C61%u7684%uFF0C%u76F8%u5BF9%u5177%u4F53%u7684%u5B9E%u73B0%u4EE3%u7801%u8F83%u4E3A%u7A33%u5B9A%u3002%u800C%u88AB%u4F9D%u8D56%u7684%u4EE3%u7801%u5E94%u8BE5%u5C3D%u53EF%u80FD%u4FDD%u6301%u7A33%u5B9A%uFF0C%u8FD9%u6837%u57FA%u4E8E%u4E4B%u4E0A%u7684%u4EE3%u7801%u624D%u4E0D%u4F1A%u56E0%u4E3A%u4F9D%u8D56%u7684%u6539%u52A8%u800C%u6539%u52A8%u3002%0A%0A%u4E0B%u9762%u5217%u51FA%u51E0%u79CD%u6211%u5728%u5B9E%u9645%u5199test%20case%u65F6%u9047%u5230%u7684%u60C5%u51B5%uFF0C%u5728gmock%u4E2D%u7684%u89E3%u51B3%u65B9%u6848%u3002%0A%0A%23%23%23%20%u6309%u7167%u51FD%u6570%u53C2%u6570%u8FD4%u56DE%u7ED3%u679C%20-%20Fake%0A%u4F8B%u5982%3A%0A%60%60%60%0Aclass%20A%0A%7B%0A%09virtual%20int%20func%28int%20a%2C%20int%20b%29%3B%0A%7D%0A%0AEXPECT_CALL%28mockA%2C%20func%28%29%29%0A%09.WillRepeatedly%28a+b%29%3B%0A%60%60%60%0Agmock%u4E2D%u53EF%u4EE5%u8FD9%u6837%u505A%uFF1AUsing%20Functions/Methods/Functors%20as%20Actions%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3A_%3B%0Ausing%20%3A%3Atesting%3A%3AInvoke%3B%0Aclass%20MockFoo%20%3A%20public%20Foo%20%7B%0Apublic%3A%0A%09MOCK_METHOD2%28Sum%2C%20int%28int%20x%2C%20int%20y%29%29%3B%0A%09MOCK_METHOD1%28ComplexJob%2C%20bool%28int%20x%29%29%3B%0A%7D%3B%0Aint%20CalculateSum%28int%20x%2C%20int%20y%29%20%7B%20return%20x%20+%20y%3B%20%7D%0Aclass%20Helper%20%7B%0Apublic%3A%0A%09bool%20ComplexJob%28int%20x%29%3B%0A%7D%3B%0A...%0AMockFoo%20foo%3B%0AHelper%20helper%3B%0AEXPECT_CALL%28foo%2C%20Sum%28_%2C%20_%29%29%0A%09.WillOnce%28Invoke%28CalculateSum%29%29%3B%0AEXPECT_CALL%28foo%2C%20ComplexJob%28_%29%29%0A%09.WillOnce%28Invoke%28%26helper%2C%20%26Helper%3A%3AComplexJob%29%29%3B%0Afoo.Sum%285%2C%206%29%3B%20//%20Invokes%20CalculateSum%285%2C%206%29.%0Afoo.ComplexJob%2810%29%3B%20//%20Invokes%20helper.ComplexJob%2810%29%3B%0A%60%60%60%0A%0A%23%23%23%20Mock%20non-virtual%u51FD%u6570%0A%60%60%60%0A//%20A%20simple%20packet%20stream%20class.%20None%20of%20its%20members%20is%20virtual.%0Aclass%20ConcretePacketStream%20%7B%0A%09public%3A%0A%09%09void%20AppendPacket%28Packet*%20new_packet%29%3B%0A%09%09const%20Packet*%20GetPacket%28size_t%20packet_number%29%20const%3B%0A%09%09size_t%20NumberOfPackets%28%29%20const%3B%0A%09%09...%0A%7D%3B%0A//%20A%20mock%20packet%20stream%20class.%20It%20inherits%20from%20no%20other%2C%20but%20defines%0A//%20GetPacket%28%29%20and%20NumberOfPackets%28%29.%0Aclass%20MockPacketStream%20%7B%0A%09public%3A%0A%09%09MOCK_CONST_METHOD1%28GetPacket%2C%20const%20Packet*%28size_t%20packet_number%29%29%3B%0A%09%09MOCK_CONST_METHOD0%28NumberOfPackets%2C%20size_t%28%29%29%3B%0A%09%09...%0A%7D%0A%0Atemplate%20%3Cclass%20PacketStream%3E%0Avoid%20CreateConnection%28PacketStream*%20stream%29%20%7B%20...%20%7D%0Atemplate%20%3Cclass%20PacketStream%3E%0Aclass%20PacketReader%20%7B%0A%09public%3A%0A%09void%20ReadPackets%28PacketStream*%20stream%2C%20size_t%20packet_num%29%3B%0A%7D%3B%0A%0AMockPacketStream%20mock_stream%3B%0AEXPECT_CALL%28mock_stream%2C%20...%29...%3B%0A..%20set%20more%20expectations%20on%20mock_stream%20...%0APacketReader%3CMockPacketStream%3E%20reader%28%26mock_stream%29%3B%0A...%20exercise%20reader%20...%0A%60%60%60%0A%u4E3A%u4EC0%u4E48%u8981%u8FD9%u4E48%u505A%uFF1F%0A%u56E0%u4E3A%u53EA%u80FD%u8FD9%u4E48%u505A%u3002%u666E%u901A%u7684mock%uFF0C%u8981%u901A%u8FC7%u7EE7%u627F%u88AB%u6D4B%u8BD5%u7C7B%uFF0C%u5E76%u91CD%u5199virtual%u51FD%u6570%u6765%u5B9E%u73B0%u3002%u800C%u4E0A%u9762%u7684ConcretePacketStream%u548CMockPacketStream%u5E76%u4EFB%u4F55%u6CA1%u6709%u5173%u7CFB%uFF0C%u4E5F%u5C31%u662F%u8BF4%uFF0C%u5982%u679C%u4F20%u5165%u540E%u8005%u7684%u6307%u9488%uFF0C%u4E0D%u7528reinterpret_cast%u662F%u4E0D%u80FD%u8F6C%u6210%u524D%u8005%u7684%u6307%u9488%u7684%u3002%0A%u6240%u4EE5%u60F3%u4E00%u4E2A%u53D8%u901A%u7684%u529E%u6CD5%uFF0C%u7528%u6A21%u677F%u7C7B%u6765%u5B9A%u4E49%u88AB%u6D4B%u4EE3%u7801%uFF0C%u5728%u6D4B%u8BD5%u65F6%u4F20%u5165mock%u7C7B%uFF0C%u5728%u751F%u4EA7%u65F6%uFF0C%u4F20%u5165%u771F%u5B9E%u7C7B%u3002%0A%0A%23%23%23%20Mocking%20Side%20Effects%0A%60%60%60%0AEXPECT_CALL%28mutator%2C%20MutateInt%28_%29%29%0A%09.WillOnce%28DoAll%28SetArgPointee%3C0%3E%285%29%2C%20Return%28true%29%29%29%3B%0AEXPECT_CALL%28mutator%2C%20Mutate%28NotNull%28%29%2C%205%29%29%0A%09.WillOnce%28SetArrayArgument%3C0%3E%28values%2C%20values%20+%205%29%29%3B%0A%60%60%60%0A%u7B2C%u4E00%u4E2A%u5C06MutateInt%u7B2C%u4E00%u4E2A%u53C2%u6570%u6307%u9488%u6307%u5411%u7684int%uFF0C%u8BBE%u4E3A5%uFF0C%u5E76%u8FD4%u56DEtrue%u3002%0A%u7B2C%u4E8C%u4E2A%u5C06values%u6570%u7EC4%u7684%5B0%2C5%29%u62F7%u8D1D%u5230%u53C2%u65701%u6307%u5411%u7684%u5730%u5740%u3002%0A%u5982%u679C%u4ECD%u9700%u8981%u8FD4%u56DE%uFF0C%u5219%u7528DoAll%uFF0C%u5982%u4E0B%uFF1A%0A%60%60%60%0AEXPECT_CALL%28mutator%2C%20MutateInt%28_%29%29%0A%20%20%20%20%20%20.WillOnce%28DoAll%28SetArgPointee%3C0%3E%285%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Return%28true%29%29%29%3B%0A%60%60%60%0A%0A%23%23%23%20Selecting%20an%20Action%27s%20Arguments%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3A_%3B%0Ausing%20%3A%3Atesting%3A%3AInvoke%3B%0Abool%20MyIsVisibleInQuadrant1%28bool%20visible%2C%20const%20string%26%20name%2C%20int%20x%2C%20int%20y%2C%0Aconst%20map%3Cpair%3Cint%2C%20int%3E%2C%20double%3E%26%20weight%2C%0Adouble%20min_weight%2C%20double%20max_wight%29%20%7B%0A%09return%20IsVisibleInQuadrant1%28visible%2C%20x%2C%20y%29%3B%0A%7D.%0A..%0AEXPECT_CALL%28mock%2C%20Foo%28_%2C%20_%2C%20_%2C%20_%2C%20_%2C%20_%2C%20_%29%29%0A%09.WillOnce%28Invoke%28MyIsVisibleInQuadrant1%29%29%3B%20//%20Now%20it%20works.%0A%60%60%60%0A%u5B9A%u4E49%u81EA%u5DF1%u7684adaptor%20MyIsVisibleInQuadrant1%uFF0C%u6216%u8005%u7528gmock%u63D0%u4F9B%u7684%u65B9%u6CD5%u4F18%u96C5%u7684%u89E3%u51B3%u3002%0A%60%60%60%0Ausing%20%3A%3Atesting%3A%3A_%3B%0Ausing%20%3A%3Atesting%3A%3AInvoke%3B%0Ausing%20%3A%3Atesting%3A%3AWithArgs%3B%0A...%0AEXPECT_CALL%28mock%2C%20Foo%28_%2C%20_%2C%20_%2C%20_%2C%20_%2C%20_%2C%20_%29%29%0A.WillOnce%28WithArgs%3C0%2C%202%2C%203%3E%28Invoke%28IsVisibleInQuadrant1%29%29%29%3B%0A//%20No%20need%20to%20define%20your%20own%20adaptor.%0A%60%60%60%0A%0A%23%23%23%23%20Mocking%20Private%20or%20Protected%20Methods%0A%60%60%60%0Aclass%20Foo%20%7B%0A%09public%3A%0A%09%09...%0A%09%09virtual%20bool%20Transform%28Gadget*%20g%29%20%3D%200%3B%0A%09protected%3A%0A%09%09virtual%20void%20Resume%28%29%3B%0A%09private%3A%0A%09%09virtual%20int%20GetTimeOut%28%29%3B%0A%7D%3B%0Aclass%20MockFoo%20%3A%20public%20Foo%20%7B%0A%09public%3A%0A%09%09...%0A%09%09MOCK_METHOD1%28Transform%2C%20bool%28Gadget*%20g%29%29%3B%0A%09%09//%20The%20following%20must%20be%20in%20the%20public%20section%2C%20even%20though%20the%0A%09%09//%20methods%20are%20protected%20or%20private%20in%20the%20base%20class.%0A%09%09MOCK_METHOD0%28Resume%2C%20void%28%29%29%3B%0A%09%09MOCK_METHOD0%28GetTimeOut%2C%20int%28%29%29%3B%0A%7D%3B%0A%60%60%60%0A%3EC++%20allows%20a%20subclass%20to%20specify%20a%20different%20access%20level%20than%20the%20base%20class%20on%20a%20virtual%20function.%0A%0A%23%23%23%23%20Misc%0AKeep%20in%20mind%20that%20one%20doesn%27t%20have%20to%20verify%20more%20than%20one%20property%20in%20one%20test.%20In%20fact%2C%20it%27s%20a%20good%20style%20to%20verify%20only%20one%0Athing%20in%20one%20test.%20If%20you%20do%20that%2C%20a%20bug%20will%20likely%20break%20only%20one%20or%20two%20tests%20instead%20of%20dozens%0A%0AWhen%20it%27s%20being%20destroyed%2C%20your%20friendly%20mock%20object%20will%20automatically%20verify%20that%20all%20expectations%20on%20it%20have%20been%20satisfied%2C%0Aand%20will%20generate%20Google%20Test%20failures%20if%20not.%20%0A%0ACurrently%20these%20are%20only%20platforms%20that%20support%20the%20pthreads%20library%20%28this%20includes%20Linux%20and%20Mac%29.%20%0A%0A%u52A0%u4E0A%u547D%u4EE4%u884C%u53C2%u6570--gmock_verbose%3Dinfo%u53EF%u4EE5%u663E%u793A%u6240%u6709EXPECT%5C_CALL%u7684%u5177%u4F53%u8C03%u7528%u60C5%u51B5%u3002%0A%0A%23%23%20Some%20useful%20tips%20in%20gtest%0A%23%23%23%20Selecting%20Tests%0A%3E%20If%20you%20set%20the%20**GTEST_FILTER**%20environment%20variable%20or%20the%20**--gtest_filter**%20flag%20to%20a%20filter%20string%2C%20Google%20Test%20will%20only%20run%20the%20tests%20whose%20full%20names%20%28in%20the%20form%20of%20TestCaseName.TestName%29%20match%20the%20filter.%0A%3E%20The%20format%20of%20a%20filter%20is%20a%20%27%3A%27-separated%20list%20of%20wildcard%20patterns%20%28called%20the%20positive%20patterns%29%20optionally%20followed%20by%20a%20%27-%27%20and%20another%20%27%3A%27-separated%20pattern%20list%20%28called%20the%20negative%20patterns%29.%0A%0A-%20./foo_test%20Has%20no%20flag%2C%20and%20thus%20runs%20all%20its%20tests.%0A-%20./foo_test%20--gtest_filter%3D*%20Also%20runs%20everything%2C%20due%20to%20the%20single%20match-everything%20*%20value.%0A-%20./foo_test%20--gtest_filter%3DFooTest.*%20Runs%20everything%20in%20test%20case%20FooTest.%0A-%20./foo_test%20--gtest_filter%3D*Null*%3A*Constructor*%20Runs%20any%20test%20whose%20full%20name%20contains%20either%20%22Null%22%20or%20%22Constructor%22.%0A-%20./foo_test%20--gtest_filter%3D-*DeathTest.*%20Runs%20all%20non-death%20tests.%0A-%20./foo_test%20--gtest_filter%3DFooTest.*-FooTest.Bar%20Runs%20everything%20in%20test%20case%20FooTest%20except%20FooTest.Bar%0A%0ATemporarily%20Disabling%20Tests%0A%60%60%60%0A//%20Tests%20that%20Foo%20does%20Abc.%0ATEST%28FooTest%2C%20DISABLED_DoesAbc%29%20%7B%20...%20%7D%0A%0Aclass%20DISABLED_BarTest%20%3A%20public%20%3A%3Atesting%3A%3ATest%20%7B%20...%20%7D%3B%0A%0A//%20Tests%20that%20Bar%20does%20Xyz.%0ATEST_F%28DISABLED_BarTest%2C%20DoesXyz%29%20%7B%20...%20%7D%0A%60%60%60%0A%0ATemporarily%20Enabling%20Disabled%20Tests%0A%3Ejust%20invoke%20the%20test%20program%20with%20the%20**--gtest_also_run_disabled_tests**%20flag%20or%20set%20the%20**GTEST_ALSO_RUN_DISABLED_TESTS**%20environment%20variable%20to%20a%20value%20other%20than%200.%0A%0A%23%23%23%20Repeating%20the%20Tests%0A%7C%20%20%20%20%20%20%20%20%20%20%20%20%7C%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%20%20%20%20%20%20%20%20%20%20%20%20%7C%0A%7C%20%3A--------%3A%20%7C%20%3A--------------%3A%20%7C%20%3A--------%3A%20%7C%0A%7C%5C%24%20foo_test%20--gtest_repeat%3D1000%20%7C%09Repeat%20foo_test%201000%20times%20and%20don%27t%20stop%20at%20failures.%7C%0A%7C%5C%24%20foo_test%20--gtest_repeat%3D-1%09%7CA%20negative%20count%20means%20repeating%20forever.%7C%0A%7C%5C%24%20foo_test%20--gtest_repeat%3D1000%20--gtest_break_on_failure%09%7C%20Repeat%20foo_test%201000%20times%2C%20stopping%20at%20the%20first%20failure.%20This%20is%20especially%20useful%20when%20running%20under%20a%20debugger%3A%20when%20the%20testfails%2C%20it%20will%20drop%20into%20the%20debugger%20and%20you%20can%20then%20inspect%20variables%20and%20stacks.%7C%0A%7C%24%20foo_test%20--gtest_repeat%3D1000%20--gtest_filter%3DFooBar%09%7C%20Repeat%20the%20tests%20whose%20name%20matches%20the%20filter%201000%20times.%7C

Edit

  angular.module('MyApp', [])
.controller('MyController', MyController)

MyController.$inject = ['$scope']
function MyController($scope) {
$scope.check = function(){}
}

MyController是controller的构造函数,通过$inject成员指定需要inject的部分(类比于上图的cardProc)。Angular在调用构造函数创建controller的时候,会把预先为这一块HTML创建的$scope传入该构造函数。

%23%20Angular%20JS%20-%20Dependency%20Injection%0A@%28myblog%29%5Bangular%2C%20javascript%5D%0A%0A%0A%21%5BAlt%20text%7C600x0%5D%28./1503652625713.png%29%0A%21%5BAlt%20text%7C600x0%5D%28./1503652641792.png%29%0A%60%60%60%0A%20%20angular.module%28%27MyApp%27%2C%20%5B%5D%29%0A%20%20%20%20.controller%28%27MyController%27%2C%20MyController%29%0A%0A%20%20MyController.%24inject%20%3D%20%5B%27%24scope%27%5D%0A%20%20function%20MyController%28%24scope%29%20%7B%0A%20%20%20%20%24scope.check%20%3D%20function%28%29%7B%7D%0A%20%20%7D%0A%60%60%60%0AMyController%u662Fcontroller%u7684%u6784%u9020%u51FD%u6570%uFF0C%u901A%u8FC7%5C%24inject%u6210%u5458%u6307%u5B9A%u9700%u8981inject%u7684%u90E8%u5206%28%u7C7B%u6BD4%u4E8E%u4E0A%u56FE%u7684cardProc%29%u3002Angular%u5728%u8C03%u7528%u6784%u9020%u51FD%u6570%u521B%u5EFAcontroller%u7684%u65F6%u5019%uFF0C%u4F1A%u628A%u9884%u5148%u4E3A%u8FD9%u4E00%u5757HTML%u521B%u5EFA%u7684%5C%24scope%u4F20%u5165%u8BE5%u6784%u9020%u51FD%u6570%u3002

Edit

$scope

Scope is an object that refers to the application model. It is an execution context for expressions. Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can watch expressions and propagate events.

简单说$scope用来做interpolation。

angular.module('scopeExample', [])
.controller('MyController', ['$scope', function($scope) {
$scope.username = 'World';
$scope.sayHello = function() {
$scope.greeting = 'Hello ' + $scope.username + '!';
};
}]);

When AngularJS evaluates {{name}}, it first looks at the scope associated with the given element for the name property. If no such property is found, it searches the parent scope and so on until the root scope is reached. In JavaScript this behavior is known as prototypical inheritance, and child scopes prototypically inherit from their parents.

这里很重要。比如下面的code

<ul>
<li ng-repeat="item in $ctrl.items"
ui-sref="itemList.itemDetail({itemId: $index})">
{{ item.name }}
<ui-view></ui-view>
</li>
</ul>

相应的ui-view用下面的模板。虽然在该ui-view的scope里没有item,但是按着原型链往上寻找,会在上一级的template中找到该属性。

<ul ng-if="itemDetail.id == item.id">
<li>Price: {{ item.price_large | currency }}</li>
<li>Description: {{ item.description }}</li>
</ul>

More

Scope Hierarchies

Where does Scope come from?

  • Each AngularJS application has exactly one root scope, but may have any number of child scopes. The location where the root scope is attached to the DOM is defined by the location of ng-app directive.
  • ng-controller and ng-repeat, create new child scopes and attach the child scope to the corresponding DOM element.
  • Component directives, which are created with the .component() helper always create an isolate scope.

Retrieving Scopes from the DOM.

To examine the scope in the debugger:

  • Right click on the element of interest in your browser and select ‘inspect element’. You should see the browser debugger with the element you clicked on highlighted.
  • The debugger allows you to access the currently selected element in the console as $0 variable.
  • To retrieve the associated scope in console execute: angular.element($0).scope()

The scope() function is only available when $compileProvider.debugInfoEnabled() is true (which is the default).

$scope and controller

In AngularJS, a Controller is defined by a JavaScript constructor function that is used to augment the AngularJS Scope.

Use controllers to:

  • Set up the initial state of the $scope object.
  • Add behavior to the $scope object.

可见controller就是被设计用来操作scope的,

Scopes and controllers interact with each other in the following situations:

  • Controllers use scopes to expose controller methods to templates (see ng-controller).
  • Controllers define methods (behavior) that can mutate the model (properties on the scope).
  • Controllers may register watches on the model. These watches execute immediately after the controller behavior executes.

controller通过inject来得到AngularJS为其创建的Scope实例。

angular.module('scopeExample', [])
.controller('MyController', MyController)
MyController.$inject = ['$scope']
function MyController($scope) {
$scope.username = 'World';
}

$digest

How to setup watchers?

  • $scope.$watch – don’t do this in a controller <==因为Angular已经帮我们设计好了,下面两个途径就是自动的watcher
  • {{ someProp }}
  •  <input … ng-model=”someProp”>

可以用$scope.$$watcherCount, $scope.$$atchers两个内部变量看到该$scope的watchers的状态

$digest vs. $apply

Digest Cycle does not get triggered automatically if events are unaware of Angular
Solution:
•  Call $digest after your custom code
•  Wrap your custom code inside of $apply
•  Find Angular specific service that handles the same functionality, e.g., $timeout

针对这三种情况,给出下面的sample code

$scope.upCounter = function () {
setTimeout(function () {
$scope.counter++;
console.log("Counter incremented!");
$scope.$digest();
}, 2000);
};
$scope.upCounter = function () {
setTimeout(function () {
$scope.$apply(function(){
$scope.counter++;
console.log("Counter incremented!");
})
}, 2000);
};
$scope.upCounter = function () {
$timeout(function () {
$scope.counter++;
console.log("Counter incremented!");
}, 2000);
};

2-way, 1-way, and 1-time binding

%23%20Angular%20JS%20-%20%24scope%0A@%28myblog%29%5Bangular%2C%20javascript%5D%0A%0A%0A%23%23%20%24scope%0A%3EScope%20is%20an%20object%20that%20refers%20to%20the%20application%20model.%20It%20is%20an%20execution%20context%20for%20expressions.%20Scopes%20are%20arranged%20in%20hierarchical%20structure%20which%20mimic%20the%20DOM%20structure%20of%20the%20application.%20Scopes%20can%20watch%20expressions%20and%20propagate%20events.%0A%0A%u7B80%u5355%u8BF4%24scope%u7528%u6765%u505Ainterpolation%u3002%0A%0A%60%60%60%0Aangular.module%28%27scopeExample%27%2C%20%5B%5D%29%0A.controller%28%27MyController%27%2C%20%5B%27%24scope%27%2C%20function%28%24scope%29%20%7B%0A%20%20%24scope.username%20%3D%20%27World%27%3B%0A%0A%20%20%24scope.sayHello%20%3D%20function%28%29%20%7B%0A%20%20%20%20%24scope.greeting%20%3D%20%27Hello%20%27%20+%20%24scope.username%20+%20%27%21%27%3B%0A%20%20%7D%3B%0A%7D%5D%29%3B%0A%60%60%60%0A%3EWhen%20AngularJS%20evaluates%20%7B%7Bname%7D%7D%2C%20it%20first%20looks%20at%20the%20scope%20associated%20with%20the%20given%20element%20for%20the%20name%20property.%20**If%20no%20such%20property%20is%20found%2C%20it%20searches%20the%20parent%20scope%20and%20so%20on%20until%20the%20root%20scope%20is%20reached.**%20In%20JavaScript%20this%20behavior%20is%20known%20as%20prototypical%20inheritance%2C%20and%20child%20scopes%20prototypically%20inherit%20from%20their%20parents.%0A%0A%u8FD9%u91CC%u5F88%u91CD%u8981%u3002%u6BD4%u5982%u4E0B%u9762%u7684code%0A%60%60%60%0A%3Cul%3E%0A%20%20%20%20%3Cli%20ng-repeat%3D%22item%20in%20%24ctrl.items%22%0A%20%20%20%20%20%20%20%20ui-sref%3D%22itemList.itemDetail%28%7BitemId%3A%20%24index%7D%29%22%3E%0A%20%20%20%20%20%20%20%20%7B%7B%20item.name%20%7D%7D%0A%20%20%20%20%20%20%20%20%3Cui-view%3E%3C/ui-view%3E%0A%20%20%20%20%3C/li%3E%0A%3C/ul%3E%0A%60%60%60%0A%u76F8%u5E94%u7684ui-view%u7528%u4E0B%u9762%u7684%u6A21%u677F%u3002%u867D%u7136%u5728%u8BE5ui-view%u7684scope%u91CC%u6CA1%u6709item%uFF0C%u4F46%u662F%u6309%u7740%u539F%u578B%u94FE%u5F80%u4E0A%u5BFB%u627E%uFF0C%u4F1A%u5728%u4E0A%u4E00%u7EA7%u7684template%u4E2D%u627E%u5230%u8BE5%u5C5E%u6027%u3002%0A%60%60%60%0A%3Cul%20ng-if%3D%22itemDetail.id%20%3D%3D%20item.id%22%3E%0A%20%20%20%20%3Cli%3EPrice%3A%20%7B%7B%20item.price_large%20%7C%20%20currency%20%7D%7D%3C/li%3E%0A%20%20%20%20%3Cli%3EDescription%3A%20%7B%7B%20item.description%20%7D%7D%3C/li%3E%0A%3C/ul%3E%0A%60%60%60%0A%0A%23%23%23%20More%0A%23%23%23%23%20Scope%20Hierarchies%0A**Where%20does%20Scope%20come%20from%3F**%0A-%20Each%20AngularJS%20application%20has%20exactly%20one%20root%20scope%2C%20but%20may%20have%20any%20number%20of%20child%20scopes.%20The%20location%20where%20the%20root%20scope%20is%20attached%20to%20the%20DOM%20is%20defined%20by%20the%20location%20of%20ng-app%20directive.%0A-%20ng-controller%20and%20ng-repeat%2C%20create%20new%20child%20scopes%20and%20attach%20the%20child%20scope%20to%20the%20corresponding%20DOM%20element.%20%0A-%20Component%20directives%2C%20which%20are%20created%20with%20the%20.component%28%29%20helper%20always%20create%20an%20isolate%20scope.%0A%0A%23%23%23%23%20Retrieving%20Scopes%20from%20the%20DOM.%0A%3E%20To%20examine%20the%20scope%20in%20the%20debugger%3A%0A-%20Right%20click%20on%20the%20element%20of%20interest%20in%20your%20browser%20and%20select%20%27inspect%20element%27.%20You%20should%20see%20the%20browser%20debugger%20with%20the%20element%20you%20clicked%20on%20highlighted.%0A-%20The%20debugger%20allows%20you%20to%20access%20the%20currently%20selected%20element%20in%20the%20console%20as%20%240%20variable.%0A-%20To%20retrieve%20the%20associated%20scope%20in%20console%20execute%3A%20angular.element%28%240%29.scope%28%29%0A%0A*The%20scope%28%29%20function%20is%20only%20available%20when%20%24compileProvider.debugInfoEnabled%28%29%20is%20true%20%28which%20is%20the%20default%29.*%0A%0A%23%23%23%23%20%24scope%20and%20controller%0A%3EIn%20AngularJS%2C%20a%20Controller%20is%20defined%20by%20a%20JavaScript%20constructor%20function%20that%20is%20used%20to%20augment%20the%20AngularJS%20Scope.%0A%3E%0A%3EUse%20controllers%20to%3A%0A%3E-%20Set%20up%20the%20initial%20state%20of%20the%20%24scope%20object.%0A%3E-%20Add%20behavior%20to%20the%20%24scope%20object.%0A%0A%u53EF%u89C1controller%u5C31%u662F%u88AB%u8BBE%u8BA1%u7528%u6765%u64CD%u4F5Cscope%u7684%uFF0C%0A%0A%3EScopes%20and%20controllers%20interact%20with%20each%20other%20in%20the%20following%20situations%3A%0A-%20Controllers%20use%20scopes%20to%20expose%20controller%20methods%20to%20templates%20%28see%20ng-controller%29.%0A-%20Controllers%20define%20methods%20%28behavior%29%20that%20can%20mutate%20the%20model%20%28properties%20on%20the%20scope%29.%0A-%20Controllers%20may%20register%20watches%20on%20the%20model.%20These%20watches%20execute%20immediately%20after%20the%20controller%20behavior%20executes.%0A%0Acontroller%u901A%u8FC7inject%u6765%u5F97%u5230AngularJS%u4E3A%u5176%u521B%u5EFA%u7684Scope%u5B9E%u4F8B%u3002%0A%60%60%60%0Aangular.module%28%27scopeExample%27%2C%20%5B%5D%29%0A.controller%28%27MyController%27%2C%20MyController%29%0AMyController.%24inject%20%3D%20%5B%27%24scope%27%5D%0Afunction%20MyController%28%24scope%29%20%7B%0A%20%20%24scope.username%20%3D%20%27World%27%3B%0A%7D%0A%60%60%60%0A%0A%23%23%20%24digest%0A%21%5BAlt%20text%5D%28./1503646988756.png%29%0A%u4E0A%u56FE%u7684%u610F%u601D%u662F%uFF0C%u5982%u679C%u7528JavaScript%u6807%u51C6%u7684onclick%u6765%u6807%u660Ebutton%20handler%uFF0C%u5219Angular%u5E76%u4E0D%u4F1A%u6FC0%u6D3Bdigest%20loop%u3002%u4F46%u662F%u5982%u679C%u7528%u4E86ng-click%2C%20ng-keyup%u6216%u8005%u662Fng-model%u6765%u7ED1%u5B9A%u6570%u636E%uFF0C%u5219%u4F1A%u5728%u53D1%u751F%u76F8%u5E94%u7684%u4E8B%u4EF6%u65F6%uFF0C%u6FC0%u53D1digest%20loop%u3002digest%20loop%u4F1A%u68C0%u6D4B%u6240%u6709%u7684watcher%u6709%u6CA1%u6709%u53D8%u5316%uFF0C%u5982%u679C%u6709%u53D8%u5316%u5219%u4F1A%u505A%u76F8%u5E94%u7684%u66F4%u65B0%28%u4F8B%u5982%uFF1A%u66F4%u65B0HTML%u4E2D%u7684%7B%7B...%7D%7D%uFF0C%u6216%u8005%u66F4%u65B0controller%u4E2D%u7684%u53D8%u91CF%28ng-model%29%29%u3002%0A%0A%23%23%23%20How%20to%20setup%20watchers%3F%0A%3E*%20%5C%24scope.%5C%24watch%20%u2013%20don%u2019t%20do%20this%20in%20a%20controller%20%3C%3D%3D%u56E0%u4E3AAngular%u5DF2%u7ECF%u5E2E%u6211%u4EEC%u8BBE%u8BA1%u597D%u4E86%uFF0C%u4E0B%u9762%u4E24%u4E2A%u9014%u5F84%u5C31%u662F%u81EA%u52A8%u7684watcher%0A%3E*%20%7B%7B%20someProp%20%7D%7D%0A%3E*%20%u202F%26lt%3Binput%20%u2026%20ng-model%3D%22someProp%22%3E%0A%0A%u53EF%u4EE5%u7528%5C%24scope.%5C%24%5C%24watcherCount%2C%20%5C%24scope.%5C%24%5C%24atchers%u4E24%u4E2A%u5185%u90E8%u53D8%u91CF%u770B%u5230%u8BE5%24scope%u7684watchers%u7684%u72B6%u6001%0A%0A%23%23%23%20%5C%24digest%20vs.%20%5C%24apply%0A%3EDigest%20Cycle%20does%20not%20get%20triggered%20automatically%20if%20events%20are%20unaware%20of%20Angular%0ASolution%3A%0A%u2022%u202F%20Call%20%5C%24digest%20after%20your%20custom%20code%0A%u2022%u202F%20Wrap%20your%20custom%20code%20inside%20of%20%5C%24apply%0A%u2022%u202F%20Find%20Angular%20specific%20service%20that%20handles%20the%20same%20functionality%2C%20e.g.%2C%20%24timeout%0A%0A%u9488%u5BF9%u8FD9%u4E09%u79CD%u60C5%u51B5%uFF0C%u7ED9%u51FA%u4E0B%u9762%u7684sample%20code%0A%60%60%60%0A%24scope.upCounter%20%3D%20function%20%28%29%20%7B%0A%20%20setTimeout%28function%20%28%29%20%7B%0A%20%20%20%20%24scope.counter++%3B%0A%20%20%20%20console.log%28%22Counter%20incremented%21%22%29%3B%0A%20%20%20%20%24scope.%24digest%28%29%3B%0A%20%20%7D%2C%202000%29%3B%0A%7D%3B%0A%60%60%60%0A%0A%60%60%60%0A%24scope.upCounter%20%3D%20function%20%28%29%20%7B%0A%20%20setTimeout%28function%20%28%29%20%7B%0A%20%20%20%20%24scope.%24apply%28function%28%29%7B%0A%20%20%20%20%20%20%24scope.counter++%3B%0A%20%20%20%20%20%20console.log%28%22Counter%20incremented%21%22%29%3B%0A%20%20%20%20%7D%29%0A%20%20%7D%2C%202000%29%3B%0A%7D%3B%0A%60%60%60%0A%0A%60%60%60%0A%24scope.upCounter%20%3D%20function%20%28%29%20%7B%0A%20%20%24timeout%28function%20%28%29%20%7B%0A%20%20%20%20%24scope.counter++%3B%0A%20%20%20%20console.log%28%22Counter%20incremented%21%22%29%3B%0A%20%20%7D%2C%202000%29%3B%0A%7D%3B%0A%60%60%60%0A%0A%23%23%202-way%2C%201-way%2C%20and%201-time%20binding%0A%21%5BAlt%20text%7C700x0%5D%28./1503653325764.png%29%0A

Edit

If the controller has been attached using the controller as syntax then the controller instance will be assigned to a property on the new scope.

首先,$scope是继承关系,当声明了ng-controller以后,Angular会从上一级controller的$scope或$rootScope构造出新的$scope。
其次,当使用controller as语法时,Angular会构造新的controller实例,并赋予新的$scope。参看下图:

此时原先的controller构造函数已经不能再用于HTML里了。

%23%20Angular%20JS%20-%20controller%20as%0A@%28myblog%29%5Bangular%2C%20javascript%5D%0A%0A%3EIf%20the%20controller%20has%20been%20attached%20using%20the%20controller%20as%20syntax%20then%20the%20controller%20instance%20will%20be%20assigned%20to%20a%20property%20on%20the%20new%20scope.%0A%0A%u9996%u5148%uFF0C%5C%24scope%u662F%u7EE7%u627F%u5173%u7CFB%uFF0C%u5F53%u58F0%u660E%u4E86ng-controller%u4EE5%u540E%uFF0CAngular%u4F1A%u4ECE%u4E0A%u4E00%u7EA7controller%u7684%5C%24scope%u6216%5C%24rootScope%u6784%u9020%u51FA%u65B0%u7684%5C%24scope%u3002%0A%u5176%u6B21%uFF0C%u5F53%u4F7F%u7528controller%20as%u8BED%u6CD5%u65F6%uFF0CAngular%u4F1A%u6784%u9020%u65B0%u7684controller%u5B9E%u4F8B%uFF0C%u5E76%u8D4B%u4E88%u65B0%u7684%5C%24scope%u3002%u53C2%u770B%u4E0B%u56FE%uFF1A%0A%21%5BAlt%20text%7C400x0%5D%28./1503651999428.png%29%21%5BAlt%20text%7C440x0%5D%28./1503652028318.png%29%0A%0A%u6B64%u65F6%u539F%u5148%u7684controller%u6784%u9020%u51FD%u6570%u5DF2%u7ECF%u4E0D%u80FD%u518D%u7528%u4E8EHTML%u91CC%u4E86%u3002