如何测试直接在外部API上运行的Java应用程序
从Ruby world加入后,我在用Java进行TDD时遇到了一些小问题。最大的问题是当我的应用程序只是与外部API通信时 假设我只想从谷歌日历中获取一些数据,或者从某个Twitter用户那里获取5条推文并显示出来 在Ruby中,我没有任何问题,因为我可以直接在测试中对API库进行猴子补丁,但在Java中我没有这样的选项 如果我从MVC的角度考虑这一点,我的模型对象是通过某个库直接访问API的。问题是,这个设计不好吗?我是否应该总是将任何API库包装在某个接口中,以便在Java中模拟/存根它? 因为当我想到这一点时,该接口的唯一目的是模拟(请不要因为我这么说而杀了我)猴子补丁。意味着每当我使用任何外部资源时,我都必须在接口中包装每一层,可以将其剔除如何测试直接在外部API上运行的Java应用程序,java,ruby,testing,junit,rspec,Java,Ruby,Testing,Junit,Rspec,从Ruby world加入后,我在用Java进行TDD时遇到了一些小问题。最大的问题是当我的应用程序只是与外部API通信时 假设我只想从谷歌日历中获取一些数据,或者从某个Twitter用户那里获取5条推文并显示出来 在Ruby中,我没有任何问题,因为我可以直接在测试中对API库进行猴子补丁,但在Java中我没有这样的选项 如果我从MVC的角度考虑这一点,我的模型对象是通过某个库直接访问API的。问题是,这个设计不好吗?我是否应该总是将任何API库包装在某个接口中,以便在Java中模拟/存根它?
# do I have to abstract everything just to do this in Java?
Twitter.stub!(:search)
现在你可能会说,我应该总是抽象掉接口,这样我就可以将底层更改为其他任何内容。但如果我在写twitter应用程序,我不会把它改成RSS阅读器
是的,我可以添加例如Facebook,然后它会有意义的界面。但是,当没有其他资源可以替代我正在使用的资源时,我仍然必须将所有内容包装在接口中,以使其可测试
是我遗漏了什么,还是这只是在Java世界中进行测试的一种方式?我知道您想要的是什么 正如您所描述的,生成对象的“测试版本”的方法之一是实现一个公共接口并使用它 但是,您缺少的是简单地扩展该类(前提是它未声明为
final
),并重写要模拟的方法。(注意:这样做的可能性是库声明其类为final被认为是不好的形式的原因——它会使测试变得相当困难。)
有许多Java库旨在促进模拟对象的使用—您可以查看或。包装外部API是我的方法 因此,正如您已经说过的,您将有一个接口和两个类:真实的和虚拟的实现 是的,从某些特定服务的角度来看,这似乎是不合理的,比如Twitter。但是,通过这种方式,您的构建过程不依赖于外部资源。依赖外部库并不是那么糟糕,但是让您的测试依赖于web上存在或不存在的实际数据可能会打乱构建过程
最简单的方法是用接口/类对包装API服务,并在整个代码中使用它。在Java中使用接口通常是很好的做法。有些语言有多重继承,有些有duck类型,Java有接口。这是语言的一个关键特性,它让我可以使用
转换语言的一个关键问题是你也必须转换思维方式。当您使用语言y思考时,无法有效地编程语言x。如果不使用指针,就无法有效地编程C,Ruby不能不使用duck类型,Java不能不使用接口。Mockito更方便,并且与Ruby Mock类似。您可以用Java“monkey patch”API。Java语言本身并没有提供具体的实现方法,但JVM和标准库提供了。在Ruby中,开发人员可以使用该库来实现这一点。在Java中,您可以使用这个库(由于旧的模拟工具的限制,我创建了这个库) 下面是一个示例JMockit测试,相当于
test\u应\u计算\u未发货订单的\u值\u
test,可在以下位置获得:
@测试
public void应计算未发货订单()的价值
{
最终订单号=新订单号();
最终列表订单=asList(订单号,新订单号(),新订单号());
新的非严格要求(Order.class)
{{
Order.findAll();结果=订单;
anOrder.getTotalCost();结果=10;
}};
assertEquals(30,Order.unshippedValue());
}
是的,我可以扩展它,但这需要另一个层将“API”对象(测试中的模拟版本)传递给模型。但是我想这是没有办法的。在这种情况下,您可以使用工厂方法创建对象,也可以使用反射替换模型中的私有字段。当然(很抱歉延迟)!我不认为有效Java中的“条目52”说开发人员应该为每个类创建单独的Java接口,正如您所暗示的那样。注意“如果存在适当的接口类型…”的部分。当它们不存在时,可以使用隐式类接口。硒
@Test
public void shouldCalculateValueOfUnshippedOrders()
{
final Order anOrder = new Order();
final List<Order> orders = asList(anOrder, new Order(), new Order());
new NonStrictExpectations(Order.class)
{{
Order.findAll(); result = orders;
anOrder.getTotalCost(); result = 10;
}};
assertEquals(30, Order.unshippedValue());
}