用Java创建模拟对象的最佳框架是什么?为什么?每个框架的优点和缺点是什么?


我已经成功使用Mockito。

当我尝试学习JMock和EasyMock时,我发现学习曲线有点陡峭(尽管可能只有我这样)。

我喜欢Mockito,因为它的语法简单干净,我可以很快掌握。最小语法的设计是为了很好地支持常见的情况,尽管有几次我需要做一些更复杂的事情,但我发现我想要的东西是支持的,而且很容易掌握。

以下是Mockito主页的一个例子:

import static org.mockito.Mockito.*;

List mockedList = mock(List.class);
mockedList.clear();
verify(mockedList).clear();

没有比这更简单的了。

我能想到的唯一主要缺点是它不会模拟静态方法。


我们在工作中大量使用EasyMock和EasyMock类扩展,并且对它非常满意。它基本上能满足你所有的需求。看一下文档,有一个非常好的例子,它向您展示了EasyMock的所有特性。


我开始通过JMock使用mock,但最终过渡到使用EasyMock。EasyMock就是这样——更简单——并提供了一种感觉更自然的语法。从那以后我就没换过。


Mockito还提供了存根方法、匹配参数(如anyInt()和anyString())、验证调用次数(times(3)、atLeastOnce()、never())等选项。

我还发现Mockito简单干净。

我不喜欢Mockito的一点是你不能存根静态方法。


是的,Mockito是一个很棒的框架。我使用它与hamcrest和谷歌guice一起设置我的测试。


您还可以看看如何使用Groovy进行测试。在Groovy中,你可以使用'as'操作符轻松模拟Java接口:

def request = [isUserInRole: { roleName -> roleName == "testRole"}] as HttpServletRequest 

除了这些基本功能之外,Groovy在模拟方面还提供了更多的功能,包括强大的MockFor和StubFor类。

http://docs.codehaus.org/display/GROOVY/Groovy+Mocks


我已经成功地使用了JMockit。

它是相当新的,所以它有点原始,文档不足。它使用ASM动态地重新定义类字节码,因此它可以模拟所有方法,包括静态、私有、构造函数和静态初始化器。例如:

import mockit.Mockit;

...
Mockit.redefineMethods(MyClassWithStaticInit.class,
                       MyReplacementClass.class);
...
class MyReplacementClass {
  public void $init() {...} // replace default constructor
  public static void $clinit{...} // replace static initializer
  public static void myStatic{...} // replace static method
  // etc...
}

它有一个期望界面,允许记录/回放场景以及:

import mockit.Expectations;
import org.testng.annotations.Test;

public class ExpecationsTest {
  private MyClass obj;

  @Test
  public void testFoo() {
    new Expectations(true) {
      MyClass c;
      {
        obj = c;
        invokeReturning(c.getFoo("foo", false), "bas");
      }
    };

    assert "bas".equals(obj.getFoo("foo", false));

    Expectations.assertSatisfied();
  }

  public static class MyClass {
    public String getFoo(String str, boolean bool) {
      if (bool) {
        return "foo";
      } else {
        return "bar";
      }
    }
  }
}

缺点是它需要Java 5/6。


我喜欢JMock,因为您能够设置期望。这与检查在某些模拟库中是否调用了某个方法完全不同。使用JMock可以编写非常复杂的期望。看那个jmock骗子。


我是PowerMock的创建者,所以显然我必须推荐它!: -)

PowerMock扩展了EasyMock和Mockito,具有模拟静态方法、final方法甚至私有方法的能力。EasyMock支持已经完成,但是Mockito插件还需要一些工作。我们还计划添加JMock支持。

PowerMock并不打算取代其他框架,而是可以在其他框架不允许模拟的复杂情况下使用它。PowerMock还包含其他有用的特性,例如抑制静态初始化器和构造函数。


模拟的最佳解决方案是让机器通过自动化的基于规范的测试来完成所有工作。对于Java,请参阅函数式Java库中包含的ScalaCheck和Reductio框架。使用基于规范的自动化测试框架,您可以提供被测试方法的规范(关于它的属性应该为真),框架自动生成测试和模拟对象。

例如,下面的属性测试Math。求任意正数n方的平方根是否等于n。

val propSqrt = forAll { (n: Int) => (n >= 0) ==> scala.Math.sqrt(n*n) == n }

当您调用propSqrt.check()时,ScalaCheck会生成数百个整数,并为每个整数检查您的属性,还会自动确保边缘情况被很好地覆盖。

尽管ScalaCheck是用Scala编写的,并且需要Scala编译器,但是用它来测试Java代码很容易。Functional Java中的Reductio框架是相同概念的纯Java实现。


对于一些不同的东西,您可以使用JtestR中组合的JRuby和Mocha,以表达性和简洁的Ruby为您的Java代码编写测试。这里有一些有用的JtestR模拟示例。这种方法的一个优点是模拟具体类非常简单。


JMockit项目网站包含大量当前模拟工具包的比较信息。

特别是,请查看特性比较矩阵,其中包括EasyMock、jMock、Mockito、unitil Mock、PowerMock,当然还有JMockit。我尽量让它保持准确和最新。


我很早就使用了JMock。我在我的最后一个项目中尝试了Mockito,并喜欢它。更简洁,更干净。PowerMock涵盖了Mockito中没有的所有需求,比如模拟静态代码、模拟实例创建、模拟最终类和方法。这样我就有了完成工作所需的一切。


我开始使用EasyMock模拟。很容易理解,但重放步骤有点烦人。Mockito删除了这一点,也有一个更清晰的语法,因为可读性似乎是它的主要目标之一。我再怎么强调这一点也不为过,因为大多数开发人员会花时间阅读和维护现有代码,而不是创建它。

另一个好处是接口和实现类以相同的方式处理,不像在EasyMock中,您仍然需要记住(并检查)使用EasyMock类扩展。

我最近快速浏览了一下JMockit,虽然它的特性清单非常全面,但我认为这样做的代价是代码的易读性,并且必须编写更多的代码。

对我来说,Mockito达到了最佳的位置,易于编写和阅读,并处理大多数代码所需要的大多数情况。使用Mockito和PowerMock将是我的选择。

需要考虑的一件事是,如果您是独自开发,或者在一个小型紧密的团队中开发,那么对于拥有不同技能水平的开发人员的大公司来说,您所选择的工具可能不是最佳选择。对于后一种情况,需要更多地考虑可读性、易用性和简洁性。如果许多人最终不使用或不维护测试,那么获得最终的mock框架是没有意义的。