Explain @Mock and @Spy in Java?

huangapple go评论52阅读模式
英文:

Explain @Mock and @Spy in Java?

问题

最近我开始学习单元测试,我遇到了Mockito框架,我了解了@Mock和@Spy以及when()等等。

@Mock
Service service;

@Test
void test(){
   when(service.runCode(Mock.anyString()).thenReturn("Working");
}

我对上面这行代码的疑问是,runCode(String a)方法会被执行吗?还是一旦方法被调用,mock对象就会返回我在thenReturn()中指定的内容?

我正在尝试理解模拟对象。

英文:

I Recently Started Learning About unit testing i came across Mockito Framwork i read about @Mock and @Spy when() etc..

@Mock
Service service;

@Test
void test(){
   when(service.runCode(Mock.anyString()).thenReturn("Working");
}

What my doubt in the above line is dose the runCode(String a) will get excuted OR once the Method is invoked then what i given the thenReturn() part will given by the mock

I am trying to understand mock

答案1

得分: 0

由于您是新手单元测试,我将为您写一个关于如何使用“模拟对象(Mocks)”的简化解释。首先,重要的是要理解,“虚拟对象(dummies)”,“伪装对象(fakes)”,“模拟对象(mocks)”,“间谍对象(spies)”只是不同类型的测试对象,它们看起来像您在生产中使用的实际对象。它们可以被注入到您想要测试的组件中,以验证交互和/或返回一些测试数据。

要理解Mockito在底层执行的操作,您可以首先尝试根据公共接口编写自己的模拟对象:

public interface Service {
    String runCode(String arg);
}

例如,您可以创建这个Service接口的测试实现,它将始终返回相同的硬编码值,无论传入的参数是什么。这将是一个虚拟对象(Dummy)

public class DummyService implements Service {
	@Override
	public String runCode(String arg) {
		return "dummy_response";
	}
}

因此,您可以在测试中使用它,以避免运行实际的runCode方法。假设您有一个要测试的Controller类,它在内部使用这个Service,测试将如下所示:

@Test
void test() {
    Controller c = new Controller(new DummyService());
    var response = c.doSomething("abc");
    // 断言响应...
}

正如您所看到的,虚拟对象相当“愚蠢”。有时,您可能希望能够根据方法的输入返回不同的字符串。在这种情况下,您可以创建自己的模拟对象(Mock)

public class MockedService implements Service {
	private Map<String, String> mockedData = new HashMap<>();

	public void whenRunCodeThenReturn(String arg, String mockedResponse) {
		mockedData.put(arg, mockedResponse);
	}

	@Override
	public String runCode(String arg) {
		return mockedData.get(arg);
	}
}

现在,您必须实例化MockedService并根据您的测试需求指定一些行为:

@Test
void test() {
	MockedService mock = new MockedService();
	Controller c = new Controller(mock);
	mock.whenRunCodeThenReturn("abc", "mocked_response_for_abc");
	
	var response = c.doSomething("abc");
	// 断言响应...
}

当然,Mockito是一个功能强大的框架,提供更多的灵活性。它具有允许您为输入参数定义更复杂条件的功能,它在每次测试后轻松重置模拟对象,并且不需要公共接口来代理对象。

要了解有关虚拟对象(如伪装对象、间谍对象和模拟对象)的更详细解释,请查看这篇文章:https://medium.com/gitconnected/the-anatomy-of-mocks-in-unit-testing-3e6a78b2b5d3

如果您想深入了解该框架,这是它们的文档:https://site.mockito.org/

英文:

Since you are new to unit testing, I'll write a simplified explanation of how Mocks work. Firstly, it is important to understand that dummies, fakes, mocks, spies are just different types of test objects that look like the actual object you use in production. They can be injected into the components you want to test to either verify the interaction and/or return some test data.

To understand what mockito is doing under the hood, you can initially try to write your own mocks based on a public interface:

public interface Service {
    String runCode(String arg);
}

For example, you can create a test implementation of this Service interface, that will always return the same hardcoded value, no-matter the argument passed in. This will be a Dummy:

public class DummyService implements Service {
	@Override
	public String runCode(String arg) {
		return &quot;dummy_response&quot;;
	}
}

As a result, you'll be able to use this in your test to avoid running the actual runCode method. Assuming you have a Controller class you want to test, that internally uses this Service, the test will look like this:

@Test
void test() {
    Controller c = new Controller(new DummyService());
    var response = c.doSomething(&quot;abc&quot;);
    // assert response ...
}

As you can see, dummy objects are rather dumb. Sometimes you might want to be able to return different Strings based on the input of the method. In this case, you can create your own Mock:

public class MockedService implements Service {
	private Map&lt;String, String&gt; mockedData = new HashMap&lt;&gt;();

	public void whenRunCodeThenReturn(String arg, String mockedResponse) {
		mockedData.put(arg, mockedResponse);
	}

	@Override
	public String runCode(String arg) {
		return mockedData.get(arg);
	}
}

Now, you'll have to instantiate the MockedService and also specify some behavior, based on your test's needs:

@Test
void test() {
	MockedService mock = new MockedService();
	Controller c = new Controller(mock);
	mock.whenRunCodeThenReturn(&quot;abc&quot;, &quot;mocked_response_for_abc&quot;);
	
	var response = c.doSomething(&quot;abc&quot;);
	// assert response ...
}

Of course, mockito is a powerful framework that offers more flexibility. It has features that allow you to define more complex conditions for the input parameters, it easily resets the mock after each test and it does not require the public interface to proxy the object.

For a more detailed explanation, about test objects such as fakes, spies and mocks, take look at this article: https://medium.com/gitconnected/the-anatomy-of-mocks-in-unit-testing-3e6a78b2b5d3

If you want to dive deeper into the framework, here is their documentation: https://site.mockito.org/

huangapple
  • 本文由 发表于 2023年5月28日 23:24:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76352205.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定