Seam和Mock有什么区别? [英] What is the difference between a Seam and a Mock?

查看:97
本文介绍了Seam和Mock有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

距离我使用Java旧代码已经几个月了,这是我正在处理的一些事情:

Its being a few months since I am working with java legacy code, this are some of the things I am dealing with:


  • 0%的测试覆盖率。

  • 在很多情况下,我甚至看到一些功能超过300行的代码。

  • 很多私有方法,有时还包括静态方法。

  • 高度紧密的耦合代码。

  • 0% test coverage.
  • Huge functions in occasions I even saw some with more than 300 lines of code.
  • Lots of private methods and in occasions static methods.
  • Highly tight coupled code.

一开始我很困惑,我发现很难在传统中使用TDD。经过数周的演练,并练习了单元测试和模拟技巧后,我的恐惧有所减轻,我感到更加自信。最近,我发现了一本书,名为:有效处理旧版内容,我没有看过它,只是看了目录,发现了一些对我来说是新的东西,接缝。显然,在旧版中工作时这非常重要。

At the beginning I was very confused, I found difficult to use TDD in the legacy. After doing katas for weeks and practicing my unit testing and mocking skills, my fear has decreased and I feel a bit more confident. Recently I discovered a book called: working effectivelly with legacy, I didn't read it, I just had a look at the table of contents and I discovered something that is new for me, The Seams. Apparently this is very important when working in the legacy.

我认为,这个Seams可以帮助我极大地打破依赖关系,并使我的代码具有可测试性,从而可以增加代码覆盖率并使单元测试更加精确。

I think that this Seams could help me alot in breaking dependencies and make my code testeable so I can increase the code coverage and make my unit testing more precise.

但是我有很多疑问:


  • 有人可以向我解释一下接缝和模拟吗?

  • 在测试之前,接缝是否在不涉及生产代码的方面违反了TDD规则?

  • 您能告诉我一些吗?一个简单的示例,比较一个接缝和一个模拟?

下面,我想粘贴一个我今天尝试打破一个依赖,目的是使代码可测试,并最终增加测试覆盖率。如果您发现一些错误,请允许我发表评论,我将不胜感激。

Below I would like to paste an example I did today where I tried to break a dependency with the goal of making the code testeable and finally increasing test coverage. I would appreciate if you could comment a bit if you see some mistakes?

这是旧代码在开始时的样子:

This is how the legacy code looked like at the beginning:

public class ABitOfLegacy
{
    private String sampleTitle;
    String output; 

    public void doSomeProcessing(HttpServletRequest request) {
    String [] values = request.getParameterValues(sampleTitle);
        if (values != null && values.length > 0)
        {
            output = sampleTitle + new Date().toString() + values[0];
        }

    }   
}

如果我只需添加一个调用该方法并断言该变量输出的单元测试,该单元输出在调用后具有某个值,那么我会犯一个错误,因为我不是单元测试,而是进行集成测试。因此,我需要做的是摆脱参数中的依赖项。为此,我将参数替换为接口:

If I just add a unit test that calls that method and asserts that variable output, has a certain value after the call,then I would be making a mistake, because I am not unit testing, I would be doing integration testing. So what I need to do, Is get rid of the dependency I have in the parameter. To do So, I replace the parameter with an interface:

public class ABitOfLegacy
{
    private String sampleTitle;
    String output; 

    public void doSomeProcessing(ParameterSource request) {
    String [] values = request.getParameters(sampleTitle);
        if (values != null && values.length > 0)
        {
            output = sampleTitle + new Date().toString() + values[0];
        }
    }

}

这是接口的外观:

public interface ParameterSource {
    String[] getParameters(String name);
}

接下来要做的是创建自己的接口实现,但是我包含HttpServletRequest作为全局变量,我使用HttpServletRequest的方法实现接口的方法:

The next thing I do, is create my own implementation of that interface but I include the HttpServletRequest as a global variable and I implement the method of the interface using the method/s of HttpServletRequest:

public class HttpServletRequestParameterSource implements ParameterSource {

    private HttpServletRequest request;

    public HttpServletRequestParameterSource(HttpServletRequest request) {
        this.request = request;
    }

    public String[] getParameters(String name) {
        return request.getParameterValues(name);
    }

}

直到这一点,我认为生产代码上的所有修改都是安全的。
现在,我在测试包中创建了Seam。如果我能很好地理解,现在就可以安全地更改Seam的行为了。这是我的方法:

Until this point, I think that all the modifications on the production code were safe. Now I create the Seam in my test package. If I understood well, now I am able to safely change the behavoir of the Seam. This is how I do it:

public class FakeParameterSource implements ParameterSource {

    public String[] values = {"ParamA","ParamB","ParamC"};

    public String[] getParameters(String name) {
        return values;
    }
}

最后一步,将是获得支持接缝,以测试该方法的原始行为。

And the final step, would be to get support from the Seam, to test the original behavoir of the method.

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import code.ABitOfLegacyRefactored;
import static org.hamcrest.Matchers.*;

public class ABitOfLegacySpecification {

    private ABitOfLegacy aBitOfLegacy;
    private String EMPTY = null;

    @Before
    public void initialize() {
        aBitOfLegacy = new ABitOfLegacy();
    }

    @Test
    public void
    the_output_gets_populated_when_the_request_is_not_empty
    () {
        FakeParameterSource fakeParameterSource = new FakeParameterSource();
        aBitOfLegacy.doSomeProcessing(fakeParameterSource);
        assertThat(aBitOfLegacy.output,not(EMPTY));
    }

    @Test(expected=NullPointerException.class)
    public void
    should_throw_an_exception_if_the_request_is_null
    () {
        aBitOfLegacy.doSomeProcessing(null);
    }   
}

这将给我100%的测试覆盖率。
我感谢您的想法:

This will give me 100% test coverage. I appreciate your thoughts:


  • 我是否正确打破了依赖性?

  • 单元测试是否缺少某些内容?

  • 有什么可以做得更好的呢?

  • 这个例子好吗?足以帮助我理解接缝和模拟物之间的区别?

  • 如果我不使用接缝,模拟将如何为我提供帮助?

  • Did I break the dependency correctly?
  • Are the unit tests missing something?
  • What could be done better?
  • Is this example good enough to help me understand the difference between a Seam and a Mock?
  • How could a mock help me here if I don't use the Seam?

推荐答案

接缝

接缝是允许您修改行为而无需修改的地方

A seam is a place that allows you to modify the behavior without modifying the code.

在您的示例中,以下是对象接缝的示例(如果我没有记错的话)。它允许您传递另一个对象,而无需更改代码。因此,这是一种接缝。

In your example, the following is an example of an Object seam (if i'm not mistaken). It allows you to pass in a different object without having to change the code. hence it is a type of seam.

public void doSomeProcessing(ParameterSource request) {..}

通过将参数设为抽象类型(而不是具体的类),引入了接缝。现在,接缝允许您修改方法的行为而无需编辑其代码-即在调用位置,我可以传入另一个对象,并使该方法执行其他操作。

By making the parameter an abstract type (instead of a concrete class), you have introduced a seam. The seam now allows you to modify the behavior of the method without editing its code - i.e. at the place of invokation, I can pass in a different object and make the method do something else.

模拟

现在,您可以使用Mock框架来创建自定义假(创建接口的子类型),而不是做这样的事情

Now instead of creating your custom fake (creating a subtype of the interface), you could using a Mock framework to do something like this

Mocks还支持断言是否调用了特定的方法,参数匹配以及测试要使用的其他漂亮功能。维护的测试代码更少。嘲笑主要用于断言正在对依赖项进行特定的调用。在您的示例中,您似乎需要存根,您只想返回一个固定值。

Mocks also support asserting whether specific methods were called on it, argument matching and other nifty functionality to be consumed by tests. Less test code to maintain. Mocks are primarily used to assert that a specific call is being made to a dependency. In your example, you seem to be in need of a Stub, you just want to return a canned value.

请原谅我生锈的JMock。.

Pardon my rusty JMock..

 @Test
    public void
    the_output_does_not_get_populated_when_the_request_is_empty
    () {
        Mockery context = new Mockery();
        final ParameterSource mockSource = context.mock(ParameterSource.class)

context.checking(new Expectations(){{
    oneOf(mockSource).getParameters(); 
            will(returnValue(new string[]{"ParamA","ParamB","ParamC"} );
}});
        aBitOfLegacy.populate(mockSource);
        assertThat(aBitOfLegacy.output,not(EMPTY));
    }



in .Net

var mockSource = new Mock<ParameterSource>();
mockSource.Setup(src => src.GetParameters())
          .Returns(new []{"ParamA","ParamB","ParamC"});

这篇关于Seam和Mock有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆