您可以使用任何mock库来模拟协议。在底层,每个协议都使用Java接口作为实现细节,您可以直接对该接口进行mock。
尽管如此,请不要这样做!由于反射、保护级别、最终类等原因,Java中的Mocking非常复杂。每当您需要一个实现协议的Clojure对象时,只需调用reify即可,例如:
(defprotocol Foo (method-a [_]) (method-b [_]))
-> Foo
(let [stub (reify Foo (method-a [_] :stubbed))]
(method-a stub))
-> :stubbed
请注意,您不需要为您不打算调用的方法编写存根。
首先,我想指出,当将大型程序拆分成组件(例如,使用类似Stuart Sierra的组件库的依赖注入库进行组装)时,这种模拟非常有用。如果我有一些组件将一组副作用函数隔离到一个概念组件中,我肯定希望有一个测试框架,可以让我注入一个替代组件,以便我可以:
您可以使用Mockito或其他库,但我认为这种解决方案不会特别优雅。
不幸的是,协议和记录生成的类Midje无法像函数那样轻松地进入其中...因此您确实需要稍微修改代码:
(defrecord-openly SideEffectThing [connection]
ISideEffect
(persist [this thing] :unfinished)
(read [this] :unfinished)
)
请查看Midje的生产模式文档,了解如何使此修改不影响您的生产运行时。
通过使用defrecord-openly定义组件,您可以使用Midje的“provided”机制指定组件方法的行为:
(fact "you can test in terms of a record's methods"
(let [obj (->SideEffectThing :fake-connection)]
(user-of-side-effect-thing obj) => 0
(provided
(read obj) => -1)
)
)
interface ISideEffect { int read(); void write(Object something); }
class SideEffectThing implements ISideEffect { ... }
// in test sources:
public class SomeOtherComponentSpec {
private ISideEffect obj;
@Before
public void setup() { obj = mock(ISideEffect.class); }
@Test
public void does_something_useful() {
when(obj.read()).thenReturn(-1);
OtherComponent comp = new OtherComponent(obj);
int actual = comp.doSomethingUseful();
assertEquals(0, actual);
verify(obj).read();
}
(fact "you can test in terms of a record's methods"
(let [obj (->SideEffectThing :fake-connection)]
(user-of-side-effect-thing obj) => 0
(provided
(read obj) => -1
(read obj) => 6
(read obj) => 99
)
)
)
这表示您期望调用三次read,并且它应返回指定的值序列。有关更多详细信息,请参见 先决条件文档,包括如何在提供的单个函数规范中指定确切的调用次数以及如何指示该函数永远不会被调用。