I have multiple production classes "NaiveHandler", "SmartHandler", "AnotherHandler" that implement the same interface "Handler" and share a lot of code. To reduce code duplication, I extracted an abstract class "AbstractHandler" that contains most of the code, and let the sub-classes inherit and reuse it.
Similarly, the test classes "NaiveHandlerTest", "SmartHandlerTest", "AnotherHandlerTest" for those production classes held tons of duplicate test methods. I moved all these #Test-annotated methods and the test setup out into an abstract class called "HandlerTestBase".
abstract class HandlerTestBase {
protected Handler cut;
protected SomeDependency x;
protected abstract Handler getCutInstance(SomeDependency x);
#BeforeEach
void setup() {
x = Mockito.mock(SomeDependency.class);
cut = getCutInstance(x);
}
#Test
void providesTheRightThing() {
when(x.getSomeValue()).thenReturn("zzz");
var result = cut.doSomething("a");
assertThat(result).isEqualTo("b");
}
// more tests that verify the shared code in AbstractHandler
}
This abstract class cannot (and should not) run on its own. The "~Base" at the end prevents Maven's tools from picking it up and running it. Instead, Maven picks up the individual test classes "NaiveHandlerTest", "SmartHandlerTest", "AnotherHandlerTest", and runs those.
class SmartHandlerTest extends HandlerTestBase {
#Override
protected Handler getCustInstance(SomeDependency x) {
return new SmartHandler(x);
}
// here more tests that only relate to SmartHandler, not the shared code, e.g.
#Test
void specificClassDoesTheRightThing() {
when(x.getSomeValue()).thenReturn("12345");
var result = cut.doSomething("a");
assertThat(result).isEqualTo("y");
}
}
All of this works beautifully.
As one consequence however, one of the test sub-classes, "NaiveHandlerTest" is now empty. It inherits the tests from the abstract class and runs those, but doesn't add any specific tests of its own. For me, this is not a problem.
class NaiveHandlerTest extends HandlerTestBase {
#Override
protected Handler getCustInstance(SomeDependency x) {
return new NaiveHandler(x);
}
}
However, SonarQube picks out this class and reports the code smell "Add some tests to this class", saying that "There's no point in having a JUnit TestCase without any test methods." For other cases, I would agree, but in this pattern SonarQube does not recognize that the test class may look empty but inherits and runs test methods from its super-class.
I could set this finding to "False Positive" to remove it. I could also annotate the seemingly empty test class with #java.lang.SuppressWarnings("java:S2187") to switch off this check here. Is there a still better way to get rid of this finding, such as a tiny tweak to the design that makes SonarQube aware of the existing tests?
Related
If I have an arbitrary class that defines a JUnit test method, and a child class that overrides test behavior but not the actual test method itself, is it possible to run the ChildClass test directly in IntelliJ?
class ParentClass
{
#Test
public void testSomeBehavior()
{
doTest();
}
protected void doTest()
{
// ParentClass specific behavior
}
}
class ChildClass extends ParentClass
{
#Override
protected void doTest()
{
// ChildClass specific behavior
}
}
My current work around is to kick off a run at the class level (ChildClass), kill the run, and then manually select my test from the Run/Debug screen. But this is not ideal.
I do know if my parent class was defined as abstract then IntelliJ will give me a list of possible implementing classes that can run the given test. But I do not see that option when parent class is instantiable.
I don't understand why could you want to do it...
It is not recommended. But if you are interesting to create nested tests using JUNIT5 maybe you can use this documentation.
https://junit.org/junit5/docs/current/user-guide/#writing-tests-nested
I've tried to avoid duplicate code in JUnit test, but I'm kind of stuck.
This is my first test, for the second one it has exactly the same methods but different service (different input). instead of the TestCaseResourceTest1 I have TestCaseResourceTest2. Now what could be the proper way to test both? I want to have a separate file for test number 2, how should I avoid the duplicate code? (ex. use the beforeFileTest() method)
public class TestCaseResourceTest1 {
#Mock
private TestService testService;
#Mock
private AreaService areaService;
private TestCaseService1 testCaseService1; // is changed in test2
#Before
public void before() throws Exception{
testCaseService1 = mock(TestCaseService1.class); // is changed in test2
MockitoAnnotations.initMocks(this);
beforeFileTest();
}
private void beforeFileTest() throws Exception{
doReturn(true).when(areaService).chechExists(any(String.class), eq(false));
}
#Test
public void verifyFileExists() throws Exception{
verifyOtherArea(testCaseService1); // is changed in test2
doReturn(false).when(areaService).chechExists(any(String.class), eq(false));
}
}
just lines with comment is changed in test2 are differences.
Tnx
Given this excerpt from your question:
… instead of the TestCaseResourceTest1 I have TestCaseResourceTest2 … I want to have a separate file for test number 2
… the standard ways of sharing code between test cases are:
Create a Test Suite and include the shared code in the test suite (typically in #BeforeClass and #AfterClass methods). This allows you to (1) run setup code once (per suite invocation); (2) encapsulate shared setup/teardown code and (3) easily add more tests cases later. For example:
#RunWith(Suite.class)
#Suite.SuiteClasses({
TestCaseResourceTest1.class,
TestCaseResourceTest2.class
)}
public class TestSuiteClass {
#BeforeClass
public void setup() {
beforeFileTest();
}
private void beforeFileTest() throws Exception {
// ...
}
}
Create an abstract class which parents TestCaseResourceTest1 and TestCaseResourceTest2 and let those test cases call the shared code in the parent (typically via super() calls). With this approach you can declare default shared code in the parent while still allowing sub classes to (1) have their own behaviour and (2) selectively override the parent/default behaviour
Create a custom JUnit runner, define the shared behaviour in this runner and then annotate the relevant test cases with #RunWith(YourCustomRunner.class). More details on this approach here
Just to reiterate what some of the other posters have said; this is not a common first step so you may prefer to start simple and only move to suites or abstract classes or custom runners if your usage provides a compelling reason to do so.
I had the such situation and it was a sign about wrong implementation design. We are talking about pure unit tests where we test exactly what is implemented in the production classes. If we need duplicated tests it means we probably have duplication in implementation.
How did I resolve it in my project?
Extracted common logic into parent service class and implemented unit tests for it.
For child services I implemented tests only for particular implemented code there. No more.
Implemented an integration tests on real environment were both services were involved and tested completely.
Assuming you want to have the exact same test run for 2 different classes (and not mocking it as in your example code), you can create an abstract test class, that has abstract method that returns an instance of the class to be tested.
Something in the vein of:
public abstract class TestCaseResourceTest {
protected abstract TestCaseService1 getServiceToTest();
#Before
public void before() throws Exception {
testCaseService1 = getServiceToTest();
MockitoAnnotations.initMocks(this);
beforeFileTest();
}
#Test
public void test() {
// do your test here
}
}
public class ConcreteTest extends TestCaseResourceTest {
protected TestCaseService1 getServiceToTest() {
return new TestCaseService();
}
}
public class ConcreteTest2 extends TestCaseResourceTest {
protected TestCaseService1 getServiceToTest() {
return new DifferentService();
}
}
Have you considered using JUnit 5 with its http://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests ?
It allows you to re-use your tests with different input. This is an example from the documentation which illustrates what you can do now with JUnit 5:
#ParameterizedTest
#ValueSource(strings = { "Hello", "World" })
void testWithStringParameter(String argument) {
assertNotNull(argument);
}
But you can also create your methods which return the input data:
#ParameterizedTest
#MethodSource("stringProvider")
void testWithSimpleMethodSource(String argument) {
assertNotNull(argument);
}
static Stream<String> stringProvider() {
return Stream.of("foo", "bar");
}
Here I am using just strings, but you can really use any objects.
If you are using Maven, you can add these dependencies to start using JUnit 5:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.0.0-RC2</version>
<scope>test</scope>
</dependency>
The only annoying thing about JUnit 5 is that it is not released yet.
When going from one test to two tests, you don't know what will be duplicate code, so I find it useful to put everything into one test method. In this case, start by putting the contents of the #Before and beforeFileTest methods inline in the test.
Then you can see that it is just te service that needs changing, so you can extract everything except that into a helper method that is called from two tests.
Also, after you have two tests that are calling the same helper method and are happy with that test coverage, you could look into writing parameterized tests. For example with JunitParams: https://github.com/Pragmatists/junitparams/wiki/Quickstart
Are the any ways to create method/contractor that could be used only in Junit ( test purpose only ) ?
Maybe there is an annotation?
For methods that are only used for testing... why not make them part of the actual test-code? At least in build-systems such as Maven, test code is not included in packaged jars, and is only distributed as part of the sources. In that sense, it cannot be called from normal classes, since it is simply not included in the final .jar (or .war).
I very frequently write such methods to make my test-code more maintainable.
To clarify:
src/
main/
java/
my/package/
MyClass.java <-- leave necessary protected accessors here
test/
java/
my/package/
MyClassTest.java <-- implement test-code here
And in MyClassTest...
public class MyClassTest {
...
private static Foo doSomethingCoolButTesty(MyClass instance) {
// access protected or package-private MyClass code here
}
}
MyClassTest.doSomethingCoolButTesty will be kept separate from the main code, and will obviously only be available to test code. Yes, it is somewhat uglier than including it as a method of the main code, but I find a fair price to pay.
For what purpose do you need this method?
(J)UnitTests should verify the behavior of the class by using its public interface. No "special" method in the tested code should be used in unit tests.
But Unittests should replace the dependencies of the tested code with test doubles (aka fakes and mocks). The preferred way to provide those test doubles is dependency injection (DI).
Sometimes its to much effort to introduce DI to your code. In that case it is acceptable to introduce low visibility getter methods as a seam where the dependency can be replaced by the mock.
class CodeUnderTest{
private final SomeOtherClass dependency = new SomeOtherClass();
SomeOtherClass getDependency(){ // package private getter
return dependency;
}
public void doSomething(){
dependency.expectedMethodCalled();
}
}
class TestInSamePackage{
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Mock
private SomeOtherClass testDouble;
#Spy
private CodeUnderTest cut;
#Before
public void setup(){
doReturn(testDouble).when(cut).getDependency();
}
#Test
public void shouldDoSomething() {
// configure testDouble
cut.doSomething();
verify(testDouble).expectedMethodCalled();
}
}
There is nothing that would prevent to call methods "outside" of a junit test case.
My pragmatic answer: make the method package protected and add a simple comment like "unit test only" as javadoc. And educate your team to honor such statements.
And ideally: design your production code in a way that does not require such "tricks" in order to make it testable!
Given the comments on the question: it might be technically possible to somehow acquire stack trace information; to then search for the presence of #Test annotations on the corresponding methods. But that seems to be absolute overkill - and it would mean to add even more "test only" code into the production code.
And it would also be the wrong approach - as it tries to solve a "social" problem using technical means: if you don't want that people are calling a certain method - then make sure they understand that.
I have a requirement to write many tests. I have extended Suite Runner of JUnit in order to be able to add new annotations where I can mention several Prerequisite classes which will be executed before any of the tests or setups get executed. My Typical test looks like this.
#RunWith(CustomSuiteRunner.class)
#BeforeSuite(Prerequisite.class)
#AfterSuite(CleanupOperations.class)
#Suite.SuiteClasses({
SimpleTests.class,
WeatherTests.class
})
public class SimpleSuite {
}
I have overridden public void run(final RunNotifier notifier) to add code the required code to trigger prerequisites and cleanup operations mentioned in BeforeSuite and AfterSuite annotation.
Now, I'm trying to find out how I can achieve the same by extending BlockJUnit4Runner? I can't find any method equivalent to run that starts the execution to override the behaviour. There is runChild which gets triggered before a child gets executed.
The reason I'm looking for this is I'm trying created several rules in an Interface and make my tests implement that so that they will be available, however as Interface elements are static and final JUnit is ignoring these. In another Question I asked today I got answer that I can make JUnit consider rules mentioned in an Interface by extending BlockJUnit4Runner and overriding getTestRules().
So, Here is what I'm trying find out.
Is it possible to extend BlockJUnit4Runner to make it take a list of tests and run them as suite and also run some code before any tests get execute and after all tests are executed?
How can I extend Suite Runner to consider TestRules defined in an implemented interface?
It is pretty much possible to extend BlockJUnit4Runner and make it take a list of tests and run them as suite with required test dependencies handled within the extended runChild() method
public class CustomRunner extends BlockJUnit4ClassRunner {
private List<String> testsToRun = Arrays.asList(new String[] { “sample1” });
public CustomRunner(Class<?> klass) throws InitializationError {
super(klass);
}
public void runChild(FrameworkMethod method, RunNotifier notifier) {
//Handle any dependency logic by creating a customlistener registering notifier
super.runChild(method,notifier);
}
}
I have a number of test cases in JUnit. All of them need the same code to be executed in their #BeforeClass static method. It's a code duplication and I'm trying to get rid of it. A dirty way of doing this is by inheritance. Are there any other mechanisms in JUnit, that may help?
PS. I wrote this blog post about this very subject: http://www.yegor256.com/2015/05/25/unit-test-scaffolding.html
The JUnit way to compose reusable code (instead of inheriting from it) are Rules.
See https://github.com/junit-team/junit/wiki/Rules
Here is a dumb sample, but you'll get the point.
import org.junit.rules.TestRule;
import org.junit.runners.model.Statement;
import org.junit.runner.Description;
public class MyTestRule implements TestRule {
#Override
public Statement apply(final Statement statement, Description description) {
return new Statement() {
public void evaluate() throws Throwable {
// Here is BEFORE_CODE
try {
statement.evaluate();
} finally {
// Here is AFTER_CODE
}
}
};
}
}
You can then use your TestRule like this:
import org.junit.Rule;
public class MyTest {
#Rule
public MyTestRule myRule = new MyTestRule();
}
BEFORE_CODE and AFTER_CODE will then be executed around each of your test methods.
If you need to run your code only once per class, use your TestRule as a #ClassRule:
import org.junit.ClassRule;
public class MyTest {
#ClassRule
public static MyTestRule myRule = new MyTestRule();
}
Now, BEFORE_CODE and AFTER_CODE will be executed around each of your test class.
#Rule field is not static, #ClassRule field is.
A #ClassRule can be declared in a Suite too.
Note that you can declare several rules in a single test class, that's how you compose test lifecycles at test-suites, test-classes and test-methods levels.
A Rule is an object that you instanciate in your test classes (statically or not). You can add contructor parameters if needed.
HTH
If the method is some kind of utility, then separate it out to a different class with a static method and call that method in your #BeforeClass.
I emphasize on the fact that don't use inheritance just because it solves your problem, use it when doing so creates sense in your class hierarchy.
You may create test runner
public class MyTestRunner extends BlockJUnit4ClassRunner {
#Override
protected Object createTest() throws Exception {
Object test = super.createTest();
doStuff();
}
public void doStuff(){
//common code
}
}
#RunWith(MyTestRunner.class)
public class MyTest1{
#Test
public void test1(){
//test method
}
}
Static methods aren't inherited, so inheritance isn't an option by default. If you mean you're moving the method to a common parent class, then that seems a poor choice since you only get one parent in Java. A test support class of some sort would seem more appropriate. It's also possible that you're seeing a need for a parameterized test.
There is absolutely nothing wrong with inheritance in this case, it's actually the only way to avoid repeating this code in each subclass. The fact that #BeforeClass methods have to be declared static in JUnit is unfortunate, but that shouldn't stop you. Extend the class and you have the initialization code automatically run for you without having to do anything.
If each and every class needs to have a #BeforeClass annotated method that is exactly the same as every other, then inheritance does not feel that wrong to me. If each of these initializing methods simply share some code, you could make a TestUtil class with some shared behavior and make calls to this shared behavior from each of the #BeforeClass methods.
I think if the classes has "is-a" relation, inheritance is reasonable.
If the base class is MyBeforeClass which defines #BeforeClass method, and MyTestClass1 "is-a" MyBeforeClass, MyTestClass1 extends MyBeforeClass is OK.
Depending on the nature of the setup code, you can potentially put all your tests in a test suite and have the setup code run there. The downside to this is that you cannot run tests individually (since the test depends on the setup code).