模拟 Web 服务的策略

发布于 2024-07-18 11:13:54 字数 1821 浏览 5 评论 0原文

我正在实现一个使用网络服务的客户端。 我想减少依赖性并决定模拟 Web 服务。
我使用 mockito,与 EasyMock 相比,它的优势在于能够模拟类,而不是只是接口。 但这不是重点。

在我的测试中,我得到了以下代码:

// Mock the required objects
Document mDocument = mock(Document.class);
Element mRootElement = mock(Element.class);
Element mGeonameElement = mock(Element.class);
Element mLatElement = mock(Element.class);
Element mLonElement = mock(Element.class);

// record their behavior
when(mDocument.getRootElement()).thenReturn(mRootElement);
when(mRootElement.getChild("geoname")).thenReturn(mGeonameElement);
when(mGeonameElement.getChild("lat")).thenReturn(mLatElement);
when(mGeonameElement.getChild("lon")).thenReturn(mLonElement);
// A_LOCATION_BEAN is a simple pojo for lat & lon, don't care about it!
when(mLatElement.getText()).thenReturn(
    Float.toString(A_LOCATION_BEAN.getLat()));
when(mLonElement.getText()).thenReturn(
    Float.toString(A_LOCATION_BEAN.getLon()));

// let it work!
GeoLocationFetcher geoLocationFetcher = GeoLocationFetcher
    .getInstance();
LocationBean locationBean = geoLocationFetcher
    .extractGeoLocationFromXml(mDocument);

// verify their behavior
verify(mDocument).getRootElement();
verify(mRootElement).getChild("geoname");
verify(mGeonameElement).getChild("lat");
verify(mGeonameElement).getChild("lon");
verify(mLatElement).getText();
verify(mLonElement).getText();

assertEquals(A_LOCATION_BEAN, locationBean);

我的代码显示的是我“微测试”了消费对象。 就像我会在测试中实现我的高效代码一样。 结果 xml 的示例是 GeoNames 上的伦敦。 在我看来,它太细粒度了。

但是我如何在不给出每一步的情况下模拟网络服务呢? 我应该让模拟对象只返回一个 XML 文件吗?

这与代码无关,而是与方法有关。

我正在使用 JUnit 4.x 和 Mockito 1.7

I'm implementing a client consuming a webservice. I want to reduce dependencies and decided to mock the webservice.
I use mockito, it has the advantage vs. EasyMock to be able to mock classes, not just interfaces. But that's not the point.

In my test, I've got this code:

// Mock the required objects
Document mDocument = mock(Document.class);
Element mRootElement = mock(Element.class);
Element mGeonameElement = mock(Element.class);
Element mLatElement = mock(Element.class);
Element mLonElement = mock(Element.class);

// record their behavior
when(mDocument.getRootElement()).thenReturn(mRootElement);
when(mRootElement.getChild("geoname")).thenReturn(mGeonameElement);
when(mGeonameElement.getChild("lat")).thenReturn(mLatElement);
when(mGeonameElement.getChild("lon")).thenReturn(mLonElement);
// A_LOCATION_BEAN is a simple pojo for lat & lon, don't care about it!
when(mLatElement.getText()).thenReturn(
    Float.toString(A_LOCATION_BEAN.getLat()));
when(mLonElement.getText()).thenReturn(
    Float.toString(A_LOCATION_BEAN.getLon()));

// let it work!
GeoLocationFetcher geoLocationFetcher = GeoLocationFetcher
    .getInstance();
LocationBean locationBean = geoLocationFetcher
    .extractGeoLocationFromXml(mDocument);

// verify their behavior
verify(mDocument).getRootElement();
verify(mRootElement).getChild("geoname");
verify(mGeonameElement).getChild("lat");
verify(mGeonameElement).getChild("lon");
verify(mLatElement).getText();
verify(mLonElement).getText();

assertEquals(A_LOCATION_BEAN, locationBean);

What my code shows is that I "micro-test" the consuming object. It's like I would implement my productive code in my test. An example for the result xml is London on GeoNames.
In my opinion, it's far too granular.

But how can I mock a webservice without giving everystep? Should I let the mock object just return a XML file?

It's not about the code, but the approach.

I'm using JUnit 4.x and Mockito 1.7

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦途 2024-07-25 11:13:54

我认为这里真正的问题是你有一个调用并创建 Web 服务的单例,因此很难插入模拟服务。

您可能必须添加(可能是包级别)对单例类的访问权限。 例如,如果构造函数看起来像这样,

private GeoLocationFactory(WebService service) {
   ...
}

您可以将构造函数设为包级别,然后使用模拟的 Web 服务创建一个构造函数。

或者,您可以通过添加 setter 方法来设置 Web 服务,尽管我不喜欢可变的单例。 同样在这种情况下,您必须记住事后取消设置网络服务。

如果 Web 服务是在方法中创建的,您可能必须使 GeoLocationFactory 可扩展以替换模拟服务。

您还可以考虑删除单例本身。 网上有文章,可能这里有关于如何做到这一点的文章。

I think the real problem here is that you have a singleton that calls and creates the web service so it is difficult to insert a mock one.

You may have to add (possibly package level) access to the singleton class. For example if the constructor looks something like

private GeoLocationFactory(WebService service) {
   ...
}

you can make the constructor package level and just create one with a mocked web service.

Alternatively you can set the webservice by adding a setter method, although I don't like mutable Singletons. Also in that case you have to remember to unset the webservice afterwards.

If the webservice is created in a method you might have to make the GeoLocationFactory extensible to substitute the mock service.

You may also look into remove the singleton itself. There are articles online and probably here on how to do that.

她说她爱他 2024-07-25 11:13:54

您确实想将从 Web 服务返回的结果模拟到将使用该结果的代码。 在上面的示例代码中,您似乎在模拟 mDocument,但您确实希望传入从 webservice 的模拟实例返回的 mDocument 实例,并断言从 geoLocationFetcher 返回的 locationBean 与 A_LOCATION_BEAN 的值匹配。

you really want to be mocking the results returned from the webservice to the code that will be using the result. In your example code above you seem to be mocking mDocument but you really want to pass in an instance of mDocument that has been returned from a mocked instance of your webservice and assert that the locationBean returned from the geoLocationFetcher matches the value of A_LOCATION_BEAN.

梨涡少年 2024-07-25 11:13:54

最简单的选择是模拟 WebService 客户端,

when(geoLocationFetcher.extractGeoLocationFromXml(anyString()))
    .thenReturn("<location/>");

您可以修改代码以从文件系统读取响应 xml。

示例代码可以在这里找到: 使用 Mockito 模拟 .NET WebServices

The easiest option would be to mock the WebService client,

when(geoLocationFetcher.extractGeoLocationFromXml(anyString()))
    .thenReturn("<location/>");

You can modify the code to read the response xml from the file system.

Sample code can be found here: Mocking .NET WebServices with Mockito

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文