#RunWith(SpringRunner.class)
public class TestService {
#Test
#EnabledIf("Instant.now().atZone(ZoneId.of(\"Asia/Kolkata\")).getDayOfWeek() == DayOfWeek.MONDAY")
public void test() {
System.out.println("The day is Monday");
}
}
running using mvn -Dtest=TestService test
#EnabledIf is not working, it is executing the test even the day is not Monday
Your code has two big problems:
First, the test class is annotated with #RunWith(SpringRunner.class) and (because the test runs) the test method is annotated with #org.junit.Test
This means that the test is running using the JUnit4 testing framework and JUnit4 knows nothing about the #EnabledIf annotation and ignores it.
If you want to use the #EnabledIf annotation you must make your test into a JUnit5 test and for that you must change your test class to
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
public class TestService {
#Test
#EnabledIf("Instant.now().atZone(ZoneId.of(\"Asia/Kolkata\")).getDayOfWeek() == DayOfWeek.MONDAY")
public void test() {
System.out.println("The day is Monday");
}
}
Because of the second problem that doesn't completely solve your problem. The documentation for #EnabledIf states:
A container or test may be enabled or disabled based on the boolean return of a method via the #EnabledIf and #DisabledIf annotations. The method is provided to the annotation via its name.
That means that you cannot provide some arbitrary expression as the argument of the #EnabledIf annotation - you need to implement the expression in a method.
When adding that method your test class might look like this:
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.ZoneId;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
public class TestService {
#Test
#EnabledIf("onMonday")
public void test() {
System.out.println("The day is Monday");
}
static boolean onMonday() {
return Instant.now().atZone(ZoneId.of("Asia/Kolkata")).getDayOfWeek() == DayOfWeek.MONDAY;
}
}
If however you intended to use org.springframework.test.context.junit.jupiter.EnabledIf (instead of org.junit.jupiter.api.condition.EnabledIf - unfortunately your question is not clear about that) then you need to adjust the imports and provide a SpEL-Expression for the condition:
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.ZoneId;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.EnabledIf;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#ExtendWith(SpringExtension.class)
public class TestService {
#Test
#EnabledIf("#{T(java.time.Instant).now().atZone(T(java.time.ZoneId).of(\"Asia/Kolkata\")).getDayOfWeek() == T(java.time.DayOfWeek).MONDAY}")
public void test() {
System.out.println("The day is Monday");
}
}
Related
I am unable to run JUnit5 tests concurrently when they involve using SpringExtension. When I run sequentially, there are no issues. When I run concurrently, only one test in the class is able to successfully access any injected or autowired fields. Any other tests will throw a NullPointerException when attempting to reference injected or autowired fields. Since JUnit5 parallelization is using ForkJoinPool under the hood, I was under the impression one Spring test context would be created and threads could safely use a cached context. Am I missing something in configuring my testing suite?
Here's a simplified example where the problem can be observed:
DummyTest:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.inject.Inject;
#Execution(ExecutionMode.CONCURRENT)
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {DummyTestConfig.class})
public class DummyTest {
#Inject
private StringBuilder stringBuilder;
#Test
public void testA() {
Assertions.assertEquals(stringBuilder.toString(), "test");
}
#Test
public void testB() {
Assertions.assertEquals(stringBuilder.toString(), "test");
}
}
DummyTestConfig:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class DummyTestConfig {
public DummyTestConfig() {}
#Bean
public StringBuilder stringBuilder() {
return new StringBuilder("test");
}
}
I am getting
java.lang.NoSuchMethodError: org.junit.jupiter.api.extension.ExtensionContext.getRequiredTestInstances()Lorg/junit/jupiter/api/extension/TestInstances;
at org.mockito.junit.jupiter.MockitoExtension.beforeEach(MockitoExtension.java:143)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$null$0(TestMethodTestDescriptor.java:126)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
error for below code.
what the proper way of running below code as junit test?
package com.safnas.unittesting.unittesting.business;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import com.safnas.unittesting.unittesting.data.SomeDataService;
#ExtendWith(MockitoExtension.class)
class SomeBusinessMockTest {
#InjectMocks
SomeBusinessImpl business = new SomeBusinessImpl();
#Mock
SomeDataService dataServiceMock;
#Test
void calculateSumUsingDataService_basic() {
when(dataServiceMock.retrieveAllData()).thenReturn(new int[] {1,2,3});
assertEquals(6, business.calculateSumUsingDataService());
}
#Test
void calculateSum_empty() {
when(dataServiceMock.retrieveAllData()).thenReturn(new int[] {});
assertEquals(0, business.calculateSumUsingDataService());
}
#Test
void calculateSum_oneValue() {
when(dataServiceMock.retrieveAllData()).thenReturn(new int[] {5});
assertEquals(5, business.calculateSumUsingDataService());
}
}
I removed #ExtendWith(MockitoExtension.class) and used below
#BeforeEach
void beforeEach() {
MockitoAnnotations.openMocks(this);
}
it worked fine
as per javadocs
AutoCloseable org.mockito.MockitoAnnotations.openMocks(Object testClass)
Initializes objects annotated with Mockito annotations for given testClass:#org.mockito.Mock, #Spy, #Captor, #InjectMocks
See examples in javadoc for MockitoAnnotations class.
Parameters:testClass Returns:A closable to close when completing any tests in testClass.
one more observation.
without any change, my original question is working in intelliJ
eclipse is only having issue it seems
I have a RestController that I want to test:
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PetController implements PetApi {
#Autowired
PetRepository pr;
#Override
public ResponseEntity<Pet> addPet(#Valid Pet pet) {
pr.save(new PetBE(9L, "dummy"));
return new ResponseEntity<Pet>(pet, HttpStatus.OK);
}
}
import org.springframework.data.repository.CrudRepository;
public interface PetRepository extends CrudRepository<PetBE, Long> {
}
I want to mock PetRepository and test if the object passed is the object returned:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#SpringBootTest
public class PetControllerTest {
#InjectMocks
private PetController petController;
#MockBean
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
/*L35*/ ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
When I run this method as a gradle test, I get
com.example.petstore.backend.api.implementation.PetControllerTest > testAddPet() FAILED
java.lang.NullPointerException at PetControllerTest.java:35
which is petController.addPet(p1);.
My printlns in addPet are not displayed and I can't set any breakpoints there because it is mocked. The only reference in addPet that could be null is pr, but it works fine when I send a request with curl.
I've also tried adding
#BeforeAll
public void setup() {
MockitoAnnotations.initMocks(this);
}
because it was suggested here but that gave an InitializationException:
com.example.petstore.backend.api.implementation.PetControllerTest > initializationError FAILED
org.junit.platform.commons.JUnitException at LifecycleMethodUtils.java:57
How can I debug this?
How can I get this to work?
You're mixing annotations from various testing frameworks here. If you wish to use the Mockito annotation #InjectMocks then I'd recommend not using any Spring-related mocking annotations at all, but rather the #Mock annotation to create a mocked version of the bean you want to inject (into the #InjectMocks-annotated field). Also make sure you bootstrap the Mockito extension with #ExtendWith(MockitoExtension.class). Something like:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import com.example.petstore.backend.api.model.Pet;
import com.example.petstore.backend.db.PetRepository;
import com.example.petstore.backend.db.PetBE;
import static org.mockito.Mockito.when;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.AdditionalAnswers.returnsFirstArg;
#ExtendWith(MockitoExtension.class)
public class PetControllerTest {
#InjectMocks
private PetController petController;
#Mock
private PetRepository pr;
#Test
void testAddPet() {
when(pr.save(any(PetBE.class))).then(returnsFirstArg());
Pet p1 = new Pet().id(5L).name("Klaus");
assertNotNull(petController);
ResponseEntity<Pet> r = petController.addPet(p1);
assertEquals(new ResponseEntity<Pet>(p1, HttpStatus.OK), r);
}
}
EDIT: Calling MockitoAnnotations.initMocks(this) inside for example a #BeforeEach-annotated method is necessary if you don't want to use the MockitoExtension. They're essentially the same thing, but it's less necessary in JUnit Jupiter because you can extend a test class with multiple extensions, which was not possible in JUnit 4.x. So if you wanted to bootstrap your test with both a Spring context and Mockito, then you had to pick one of them and setup the other one yourself.
By using the #MockitoExtension extension from mockito-junit-jupiter, mocks are null in the test class constructor.
Used dependencies :
mockito-junit-jupiter:2.18.0
unit-jupiter-api:5.1.1
We could reproduce the issue with only a mock field but the use of the mock by the object under test constructor illustrates well the typical scenario :
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class FooTest {
#Mock
Bar mockBar;
Foo foo;
public FooTest() {
foo = new Foo(mockBar); // mockBar is null here
}
#Test
public void doThis() {
Mockito.when(mockBar.doThat())
.thenReturn(Mockito.anyInt());
// ...
}
}
As a result the mock object is walking around but not associated to the Foo object under test.
Why ? What workaround use ?
Solution 1 :
Replace the constructor initialization by #InjectMocks :
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class FooTest {
#Mock
Bar mockBar;
#InjectMocks
Foo foo;
#Test
public void doThis() {
Mockito.when(mockBar.doThat())
.thenReturn(Mockito.anyInt());
// ...
}
}
This works but using #InjectMocks is also a taste question because
if all injection strategy fail, then Mockito won't report failure.
This not required injection may annoy you. If it is the case, you can use the next one way.
Solution 2 :
Replace the constructor initialization by a method annotated with #BeforeEach :
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class FooTest {
#Mock
Bar mockBar;
Foo foo;
#BeforeEach
public void beforEach() {
foo = new Foo(mockBar);
}
#Test
public void doThis() {
Mockito.when(mockBar.doThat())
.thenReturn(Mockito.anyInt());
// ...
}
}
Is it a bug of Powermock or I'm doing sth wrong?
The following test should pass, but failed with:
trackBugPartialMockCountMore(com.xiaomi.finddevice.test.testcase.PowerMockBug)
org.mockito.exceptions.verification.TooManyActualInvocations:
classToMock.foo();
Wanted 1 time:
-> at com.xiaomi.finddevice.test.testcase.PowerMockBug.trackBugPartialMockCountMore(PowerMockBug.java:24)
But was 3 times. Undesired invocation:
-> at com.xiaomi.finddevice.test.testcase.PowerMockBug.trackBugPartialMockCountMore(PowerMockBug.java:22)
When I remove #PrepareForTest(ClassToMock.class), every thing goes well and the test get passed.
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
#RunWith(PowerMockRunner.class)
#PrepareForTest(ClassToMock.class)
public class PowerMockBug {
#Test
public void trackBugPartialMockCountMore() {
ClassToMock mock = mock(ClassToMock.class);
when(mock.foo()).thenCallRealMethod();
mock.foo();
verify(mock).foo();
}
}
class ClassToMock {
public int foo() { return 0x10; }
}
VERSION: powermock-mockito-junit-1.6.3
In your example you don't need to use PowerMock because you are not mocking/spying a final or static method. You can safely remove both #RunWith and #PrepareForTest annotations. Only mockito is needed for your purposes