Java JUnit:仅使用静态方法测试辅助类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9700179/
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
JUnit: testing helper class with only static methods
提问by Mateusz Chromiński
I am testing a helper class with only static methods with JUnit4 and Cobertura. Testing methods was easy task and is done already.
我正在测试一个仅使用 JUnit4 和 Cobertura 的静态方法的辅助类。测试方法是一项简单的任务,并且已经完成。
However, cobertura shows that the class is not covered by tests completely, as it is not instantiated anywhere.
但是,cobertura 表明该类并未完全被测试覆盖,因为它没有在任何地方实例化。
I don't want to create an instance of this class (it is a helper class), so first solution is to hide constructor (which is generally good approach for helper class).
我不想创建这个类的实例(它是一个帮助类),所以第一个解决方案是隐藏构造函数(这通常是帮助类的好方法)。
Then cobertura complains that the empty private constructor is not covered by tests.
然后 cobertura 抱怨空的私有构造函数没有被测试覆盖。
Is there any solution to achieve 100% code coverage for such situation?
对于这种情况,是否有任何解决方案可以实现 100% 的代码覆盖率?
Code coverage is required from top level management (in this case), so for me obtaining 100% for this particular class is quite helpful.
顶层管理需要代码覆盖率(在这种情况下),所以对我来说,获得 100% 的这个特定类是非常有帮助的。
采纳答案by Aaron Digulla
There are several solutions:
有几种解决方案:
You can add a public constructor and call it from a test. While it doesn't make sense, it also doesn't hurt (much).
Create a dummy static instance (you can call the private constructor here). Ugly but you can give the field a name to communicate your intent (
JUST_TO_SILENCE_COBERTURA
is a good name).You can let your test extendthe helper class. That will intrinsically call the default constructor but your helper class can't be
final
anymore.
您可以添加一个公共构造函数并从测试中调用它。虽然它没有意义,但它也没有伤害(很多)。
创建一个虚拟静态实例(您可以在此处调用私有构造函数)。丑陋但您可以为该字段命名以传达您的意图(这
JUST_TO_SILENCE_COBERTURA
是一个好名字)。您可以让您的测试扩展助手类。这将本质上调用默认构造函数,但您的助手类不能
final
再调用了。
I suggest the last approach especially because the class can't be final
anymore. If a consumer of your code wants to add another helper method, they can now extend the existing class and receive one handle to get to all helper methods. This creates a coupling of the helper methods which communicates the intent (these belong together) - which is impossible if the helper class is final
我建议采用最后一种方法,特别是因为该类不能再存在final
了。如果您的代码的使用者想要添加另一个辅助方法,他们现在可以扩展现有类并接收一个句柄以访问所有辅助方法。这创建了传达意图的辅助方法的耦合(它们属于一起) - 如果辅助类是final
If you want to prevent users to accidentally instantiate the helper class, make it abstract
instead of using a hidden constructor.
如果您想防止用户意外实例化辅助类,请使用它abstract
而不是使用隐藏的构造函数。
回答by oers
No.
不。
Unless you call the private constructor explicitly (which would be bad code) you won't be able to cover those lines.
除非您显式调用私有构造函数(这将是糟糕的代码),否则您将无法覆盖这些行。
回答by manub
Obtaining 100% coverage in all cases it's good, but there are some cases in which this is not possible. Of course if you have a class that is never instantiated, Cobertura will get this as a not complete test coverage, because those lines of code are actually in the class, but they're not tested.
在所有情况下都获得 100% 的覆盖率很好,但在某些情况下这是不可能的。当然,如果您有一个从未实例化的类,Cobertura 会将其视为不完整的测试覆盖率,因为这些代码行实际上在类中,但未经过测试。
Fact is you'll never call a private constructor (I'm assuming you've hidden the constructor by making it private), so I wouldn't bother. Test should be about getting what you're expecting, and while I agree 100% coverage is good, in some cases (like this) this isn't useful.
事实是您永远不会调用私有构造函数(我假设您通过将其设为私有来隐藏构造函数),所以我不会打扰。测试应该是关于获得你所期望的,虽然我同意 100% 的覆盖率是好的,但在某些情况下(像这样)这没有用。
Have a look at 100% Code Coverageas well.
也看看100% 代码覆盖率。
回答by matt
If you absolutely need to achieve 100% code coverage - the merits of that can be debated elsewhere :) - you can achieve it using reflection in your tests. As habit, when I implement a static-only utility class, I add in a private constructor to ensure that instances of the class can't be created. For example:
如果您绝对需要实现 100% 的代码覆盖率 - 可以在其他地方讨论其优点:) - 您可以在测试中使用反射来实现它。习惯上,当我实现一个仅限静态的实用程序类时,我会添加一个私有构造函数以确保无法创建该类的实例。例如:
/**
* Constructs a new MyUtilities.
* @throws InstantiationException
*/
private MyUtilities() throws InstantiationException
{
throw new InstantiationException("Instances of this type are forbidden.");
}
Then your test might look something like this:
那么您的测试可能如下所示:
@Test
public void Test_Constructor_Throws_Exception() throws IllegalAccessException, InstantiationException {
final Class<?> cls = MyUtilties.class;
final Constructor<?> c = cls.getDeclaredConstructors()[0];
c.setAccessible(true);
Throwable targetException = null;
try {
c.newInstance((Object[])null);
} catch (InvocationTargetException ite) {
targetException = ite.getTargetException();
}
assertNotNull(targetException);
assertEquals(targetException.getClass(), InstantiationException.class);
}
Basically, what you're doing here is getting the class by name, finding the constructors on that class type, setting it to public (the setAccessible
call), calling the constructor with no arguments, and then ensuring that the target exception that is thrown is an InstantiationException
.
基本上,您在这里所做的是按名称获取类,找到该类类型的构造函数,将其设置为 public(setAccessible
调用),不带参数调用构造函数,然后确保抛出的目标异常是一个InstantiationException
。
Anyway, as you said, the 100% code coverage requirement here is kind of a pain, but it sounds like it's out of your hands, so there's little you can do about it. I've actually used approaches similar to the above in my own code, and I did find it beneficial, but not from a testing perspective. Rather, it just helped me learn a little bit more about reflection than I knew before :)
无论如何,正如您所说,这里的 100% 代码覆盖率要求有点痛苦,但听起来似乎超出了您的掌控,因此您无能为力。我实际上在我自己的代码中使用了与上述类似的方法,我确实发现它很有用,但不是从测试的角度来看。相反,它只是帮助我比以前了解的更多关于反射的知识:)
回答by Eagle
You may skip 100% coverage for those. It should not harm at all. However, if you are working on a very strict product, targeting 100% coverage, then you may use the strategy below:
您可以跳过 100% 的覆盖范围。它根本不应该受到伤害。但是,如果您正在开发一个非常严格的产品,目标是 100% 的覆盖率,那么您可以使用以下策略:
In your case, the missing coverage is the constructor: You should test the expected behavior of the constructor.
在您的情况下,缺少的覆盖范围是构造函数:您应该测试构造函数的预期行为。
If you do not define a constructor, then you allow the class to be instantiated (through the default constructor). You should test the default constructor behavior: In the tests, call the constructor and test the results, e.g. no error is thrown, or a valid instance is returned.
如果没有定义构造函数,则允许类被实例化(通过默认构造函数)。你应该测试默认的构造函数行为:在测试中,调用构造函数并测试结果,例如没有抛出错误,或者返回一个有效的实例。
If, instead, as a utility class practice, you also define a constructor as private
and throw an UnsupportedOperationException
in the constructor. In the tests, you may assert that behavior. :
相反,如果作为实用程序类实践,您还定义了一个构造函数 asprivate
并UnsupportedOperationException
在构造函数中抛出 an 。在测试中,您可以断言该行为。:
public class HelperClassTest {
@Test(expected = IllegalAccessException.class)
public void ctorShouldBePrivate() throws InstantiationException, IllegalAccessException {
HelperClass.class.newInstance();
}
@Test
public void whenCtorIsCalledThroughReflectionUnsupportedOperationExceptionShouldBeThrown() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Constructor<HelperClass> constructor = HelperClass.class.getDeclaredConstructor();
constructor.setAccessible(true);
try {
constructor.newInstance();
fail("Exception is expected");
} catch (InvocationTargetException e) {
assertThat(e.getCause(), is(instanceOf(UnsupportedOperationException.class)));
}
}
...
}
public class HelperClass{
private HelperClass() {
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
}
...
}