Java 在正在测试的同一类中模拟私有方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19699122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Mock private method in the same class that is being tested
提问by Kingand
I have a Java class named, MyClass
, that I want to test with JUnit. The public method, methodA
, that I want to test calls a private method, methodB
, in the same class to determine which conditional path to follow. My goal is to write JUnit tests for the different paths in methodA
. Also, methodB
calls a service, so I do not want it to actually be executed when I run the JUnit tests.
我有一个名为 的 Java 类MyClass
,我想用 JUnit 测试它。methodA
我要测试的公共方法调用methodB
同一类中的私有方法 ,以确定要遵循的条件路径。我的目标是为methodA
. 此外,methodB
调用服务,因此我不希望在运行 JUnit 测试时实际执行它。
What is the best way to mock methodB
and control its return so that I can test different paths for 'methodA'?
模拟methodB
和控制其返回以便我可以测试“methodA”的不同路径的最佳方法是什么?
I prefer to use JMockit when writing mocks, so I am specifically interested in any answer that applies to JMockit.
我在编写模拟时更喜欢使用 JMockit,所以我对任何适用于 JMockit 的答案特别感兴趣。
Here is my example class:
这是我的示例类:
public class MyClass {
public String methodA(CustomObject object1, CustomObject object2) {
if(methodB(object1, object2)) {
// Do something.
return "Result";
}
// Do something different.
return "Different Result";
}
private boolean methodB(CustomObject custObject1, CustomObject custObject2) {
/* For the sake of this example, assume the CustomObject.getSomething()
* method makes a service call and therefore is placed in this separate
* method so that later an integration test can be written.
*/
Something thing1 = cobject1.getSomething();
Something thing2 = cobject2.getSomething();
if(thing1 == thing2) {
return true;
}
return false;
}
}
This is what I have so far:
这是我到目前为止:
public class MyClassTest {
MyClass myClass = new MyClass();
@Test
public void test_MyClass_methodA_enters_if_condition() {
CustomObject object1 = new CustomObject("input1");
CustomObject object2 = new CustomObject("input2");
// How do I mock out methodB here to return true?
assertEquals(myClass.methodA(object1, object2), "Result");
}
@Test
public void test_MyClass_methodA_skips_if_condition() {
CustomObject object1 = new CustomObject("input1");
CustomObject object2 = new CustomObject("input2");
// How do I mock out methodB here to return false?
assertEquals(myClass.methodA(object1, object2), "Different Result");
}
}
Thanks!
谢谢!
采纳答案by Rogério
To give the answer you asked for (using JMockit's partial mocking):
给出您要求的答案(使用 JMockit 的部分模拟):
public class MyClassTest
{
@Tested MyClass myClass;
@Test
public void test_MyClass_methodA_enters_if_condition() {
final CustomObject object1 = new CustomObject("input1");
final CustomObject object2 = new CustomObject("input2");
new NonStrictExpectations(myClass) {{
invoke(myClass, "methodB", object1, object2); result = true;
}};
assertEquals("Result", myClass.methodA(object1, object2));
}
@Test
public void test_MyClass_methodA_skips_if_condition() {
final CustomObject object1 = new CustomObject("input1");
final CustomObject object2 = new CustomObject("input2");
new NonStrictExpectations(myClass) {{
invoke(myClass, "methodB", object1, object2); result = false;
}};
assertEquals("Different Result", myClass.methodA(object1, object2));
}
}
However, I would notrecommend doing it like that. In general, private
methods should not be mocked. Instead, mock the actual external dependency of your unit under test (the CustomObject
in this case):
但是,我不建议这样做。一般来说,private
方法不应该被嘲笑。相反,模拟被测单元的实际外部依赖项(CustomObject
在这种情况下):
public class MyTestClass
{
@Tested MyClass myClass;
@Mocked CustomObject object1;
@Mocked CustomObject object2;
@Test
public void test_MyClass_methodA_enters_if_condition() {
new NonStrictExpectations() {{
Something thing = new Something();
object1.getSomething(); result = thing;
object2.getSomething(); result = thing;
}};
assertEquals("Result", myClass.methodA(object1, object2));
}
@Test
public void test_MyClass_methodA_skips_if_condition() {
new NonStrictExpectations() {{
object1.getSomething(); result = new Something();
object2.getSomething(); result = new Something();
}};
assertEquals("Different Result", myClass.methodA(object1, object2));
}
}
回答by mikeslattery
Make methodB a member of a separate class, and have a private reference to that class within MyClass
.
使 methodB 成为单独类的成员,并在MyClass
.
public class MyClass {
private MyOtherClass otherObject = new MyOtherClass();
public String methodA(CustomObject object1, CustomObject object2) {
if(otherObject.methodB(object1, object2)) {
// Do something.
return "Result";
}
// Do something different.
return "Different Result";
}
}
class MyOtherClass {
public boolean methodB(CustomObject custObject1, CustomObject custObject2) {
// Yada yada code
}
}
Personally, I usually only test public methods and look at coverage reports to ensure that all paths have been visited in my private methods. If I really need to test a private method, that's a smell that requires a refactoring as I have above.
就我个人而言,我通常只测试公共方法并查看覆盖率报告,以确保在我的私有方法中访问了所有路径。如果我真的需要测试一个私有方法,那是一种需要重构的味道,就像我上面所说的那样。
You could also use reflection, but I'd feel dirty doing that. If you REALLY want the solution to that let me know and I'll add it to this answer.
你也可以使用反射,但我会觉得这样做很脏。如果您真的想要解决方案,请告诉我,我会将其添加到此答案中。
回答by Lifecube
To mock the private method, you need powermock
The sample code will be like this, but I haven't run it.
要mock私有方法,需要powermock
,示例代码会是这样,不过我没运行过。
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith (PowerMockRunner.class)
public class MyClassTest {
@Test
public void test_MyClass_methodA_enters_if_condition() {
final MyClass myClass = Mockito.mock (MyClass.class);
CustomObject object1 = new CustomObject("input1");
CustomObject object2 = new CustomObject("input2");
Mockito.when (myClass.methodB(object1, object2)).thenReturn (true);
Mockito.when (myClass.methodA(object1, object2)).thenCallRealMethod ();
assertEquals(myClass.methodA(object1, object2), "Result");
}
}
回答by Raedwald
Do not be tempted to mock private methods, even if you can engaging in trickery to do so using a mocking tool. Private members are implementation details, which you should be free to change. Instead use the non-private API to exercise the class. If this is troublesome, consider moving the troublesome code into a different class, if it is not there already, and use dependency injection to inject a mock implementation of the troublesome code.
不要试图模拟私有方法,即使您可以使用模拟工具进行欺骗。私有成员是实现细节,您应该可以自由更改。而是使用非私有 API 来练习该类。如果这很麻烦,考虑将麻烦的代码移到不同的类中,如果它已经不存在,并使用依赖注入来注入麻烦代码的模拟实现。
回答by javaPlease42
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ MyClass.class })
public class MyClassTest {
// Class Under Test
MyClass cut;
@Before
public void setUp() {
// Create a new instance of the service under test (SUT).
cut = new MyClass();
// Common Setup
// TODO
}
@Test
public void testMethodA() throws Exception {
/* Initialization */
CustomObject object2 = PowerMock.createNiceMock(CustomObject.class);
CustomObject object1 = PowerMock.createNiceMock(CustomObject.class);
MyClass partialMockCUT = PowerMock.createPartialMock(MyClass.class,
"methodB");
long response = 1;
/* Mock Setup */
PowerMock
.expectPrivate(partialMockCUT, "methodB",
EasyMock.isA(CustomObject.class),
EasyMock.isA(CustomObject.class)).andReturn(true)
.anyTimes();
/* Mock Setup */
/* Activate the Mocks */
PowerMock.replayAll();
/* Test Method */
String result = partialMockCUT.methodA(object1, object2);
/* Asserts */
Assert.assertNotNull(result);
PowerMock.verifyAll();
}
}