Java Mockito Spy - 调用构造函数之前的存根
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23598147/
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
Mockito Spy - stub before calling the constructor
提问by Matt3o12
I'm trying to spy on an Object and I want to stub a method that is called by the constructor before the constructor calls it.
My class looks like that:
我试图监视一个对象,我想在构造函数调用它之前存根一个由构造函数调用的方法。
我的课看起来像这样:
public class MyClass {
public MyClass() {
setup();
}
public void setup() {
}
}
The setup method mustn't be called. Well, how do I spy on this method (and stub setup so that it does nothing)?
It works fine with mocking the method but I want to unit test MyClass
and so I will need very other method.
不得调用 setup 方法。好吧,我如何监视此方法(以及存根设置以使其不执行任何操作)?
它可以很好地模拟该方法,但我想进行单元测试MyClass
,因此我需要非常其他的方法。
The reason why need to stub the setup method so that it does nothing:
I'm programing a Lego robot (lejos) and I put some code in setup that the robot needs to work. However, when I call it outside TinyVM (the VM that is installed on the robot), java crashes since it the VM hasn't been initialized properly (because the tests run on my PC). For unit-testing the setup isn't important.
I can't stub the classes/methods setup calls since some of them are public static final variables.
为什么需要存根 setup 方法以便它什么都不做的原因:
我正在编写一个乐高机器人(lejos)并且我在 setup 中放置了一些机器人需要工作的代码。但是,当我在 TinyVM(安装在机器人上的 VM)之外调用它时,java 崩溃了,因为 VM 没有正确初始化(因为测试在我的 PC 上运行)。对于单元测试,设置并不重要。
我无法存根类/方法设置调用,因为其中一些是公共静态最终变量。
采纳答案by Matt3o12
Thanks for the suggestions, but it was a little bit too complex.
I ended up mocking the method by extending the class and overwriting my setup method. This way the default constructor won't call its implementation of setup, it will call the overwritten method instead.
Here is the code:
感谢您的建议,但它有点太复杂了。
我最终通过扩展类并覆盖我的设置方法来模拟该方法。这样默认构造函数就不会调用它的 setup 实现,而是调用被覆盖的方法。
这是代码:
// src/author/MyClass.java
public class MyClass {
public MyClass() {
setup();
}
protected void setup() {
throw new Exception("I hate unit testing !");
}
public boolean doesItWork() {
return true;
}
}
// test/author/MyClass.java
public class MyClassTest {
private class MockedMyClass extends MyClass {
@Override
protected void setup() {
}
}
private MyClass instance;
@Before
public void setUp() { // Not to be confusing with `MyClass#setup()`!
instance = new MockedMyClass();
}
@Test
public void test_doesItWork() {
assertTrue(instance.doesItWork());
}
}
If you don't want MyTest's setup method to do called or overwritten by other subclasses except your test (because other developer might mess things up very badly by using the setup method), just change the visibility to default and only your classes will be able to call setup.
如果您不希望 MyTest 的 setup 方法被除您的测试以外的其他子类调用或覆盖(因为其他开发人员可能会使用 setup 方法将事情搞砸),只需将可见性更改为默认值,只有您的类才能调用设置。
If there is a simpler way, please answer the question because I'm not 100% content with my solution.
如果有更简单的方法,请回答这个问题,因为我对我的解决方案不是 100% 满意。
回答by Jeff Bowman
To answer your question directly, you cannot use Mockito to stub a method called from the constructor. Mockito needs an instance of the class before you can begin mocking, and you haven't given yourself a way to create an instance for testing.
要直接回答您的问题,您不能使用 Mockito 存根从构造函数调用的方法。Mockito 需要一个类的实例才能开始模拟,而您还没有给自己创建一个用于测试的实例的方法。
More generally, as mentioned in Effective Javaitem 17, you should not call overridable methods from constructors. If you do so, for instance, you could provide an override in a subclass that refers to a final
field but that runs before the final
field is set. It probably won't get you in trouble here, but it's a bad habit in Java.
更一般地说,如Effective Java 第17 条所述,您不应从构造函数调用可覆盖的方法。例如,如果您这样做,您可以在引用final
字段但在设置final
字段之前运行的子类中提供覆盖。这可能不会给您带来麻烦,但在 Java 中这是一个坏习惯。
Luckily, you can restructure your code to do this very easily:
幸运的是,您可以非常轻松地重构代码以完成此操作:
public class MyClass {
public MyClass() {
this(true);
}
/** For testing. */
MyClass(boolean runSetup) {
if (runSetup) {
setup();
}
}
/* ... */
}
To make it even more obvious, you can make the one-parameter MyClass
constructor private, and provide a public static
factory method:
为了让它更明显,您可以将单参数MyClass
构造函数设为私有,并提供一个public static
工厂方法:
/* ... */
public static MyClass createForTesting() {
return new MyClass(false);
}
private MyClass(boolean runSetup) {
/* ... */
Though some developers think it is a bad practice to write any code in methods that is used mostly for tests, remember that you are in charge of the design of your code, and tests are one of few consumers you absolutely know you will need to accommodate. Though it's still a good idea to avoid explicit test setup in "production" code, creating extra methods or overloads for the sake of testing will usually make your code cleaner overall and can drastically improve your test coverage and readability.
尽管一些开发人员认为在主要用于测试的方法中编写任何代码是一种不好的做法,但请记住,您负责代码的设计,而测试是您绝对知道需要适应的少数消费者之一. 尽管在“生产”代码中避免显式测试设置仍然是一个好主意,但为了测试而创建额外的方法或重载通常会使您的代码整体更干净,并可以大大提高您的测试覆盖率和可读性。
回答by fragorl
Use PowerMock.
After you've imported the libraries, set it up to manipulate the class you want to mock with the instance method that mustn't be called.
使用 PowerMock。
导入库后,将其设置为使用不能调用的实例方法来操作要模拟的类。
Like so:
像这样:
@RunWith(PowerMockRunner.class)
@PrepareForTest({<Other classes>, Myclass.class})
- Suppress the method at the start of your test.
- 在测试开始时禁止该方法。
Like so:
像这样:
suppress(method(Myclass.class, "setup"));
- Customize the behaviour of your setup() method as desired, in your test.
- 在测试中根据需要自定义 setup() 方法的行为。
Like so:
像这样:
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
// code here
return null;
}
}).when(Myclass.class, "setup");