Mocking current time for functional tests - spring-boot

I am writing a functional test using cucumber for my Spring boot application. The logic which I want to test uses current time, based on that the result vary. Is there a way to mock the current time in functional tests

That's possible using PowerMock http://powermock.github.io/
#RunWith(PowerMockRunner.class)
// ... other annotations
public class SomeTest {
private final Date fixedDate = new Date(10000);
#Before
public void setUp() throws Exception {
PowerMockito.whenNew(Date.class).withNoArguments().thenReturn(fixedDate);
}
...
}
Another approach is to use some service that provides current time and mock this service in the test. Rough example
#Service
public class DateProvider {
public Date current() { return new Date(); }
}
#Service
public class CurrentDateConsumer {
#Autowired DateProvider dateProvider;
public void doSomeBusiness() {
Date current = dateProvider.current();
// ... use current date
}
}
#RunWith(Cucumber.class)
public class CurrentDateConsumerTest {
private final Date fixedDate = new Date(10000);
#Mock DateProvider dateProvider;
#Before
public void setUp() throws Exception {
when(dateProvider.current()).thenReturn(fixedDate);
}
}

Related

How to mock a ObjectProvider<XXX> that is autowired?

I am doing a migration from Spring 4.x to 5.x and am following the recommendation to wrap the object with an ObjectProvider to handle beans that return null: https://stackoverflow.com/a/49393682/10863988
This is the class set up I have:
class ConfigurationClass{
#Autowired
private ObjectProvider<MyObject> myObject;
public SomeOtherClass getSomeOtherClass() {
return new SomeOtherClass(myObject.getIfAvailable());
}
}
class TestSomeOtherClass {
#Mock
MyObject myObject;
#InjectMocks
ConfigurationClass;
SomeOtherClass someOtherClass;
public void setup() {
this.someOtherClass = spy(configuration.getSomeOtherClass());
}
}
The problem is when I run this test. the myObject in the ConfigurationClass returns a null pointer exception.
I've tried adding this to the TestSomeOtherClass but I still can't seem to mock the ObjectProvider<MyObject>:
class TestSomeOtherClass {
#Mock
MyObject myObject;
#Mock
ObjectProvider<MyObject> myObjectObjectProvider;
#InjectMocks
ConfigurationClass;
SomeOtherClass someOtherClass;
public void setup() {
doReturn(myObject).when(myObjectObjectProvider).getIfAvailable();
this.someOtherClass = spy(configuration.getSomeOtherClass());
}
}
Any advice on how to handle this?
You do not tell Mockito to handle it's annotations (#Mock, #InjectMocks) anywhere in your code, so they do not have any effect. By default all non-primitive fields in Java are initialized as null - that's where the NullPointerException comes from.
openMocks/initMocks method
Depending on the version of Mockito you're using, you need to call initMocks() or openMocks() static method from the MockitoAnnotations class:
AutoCloseable openMocks;
#BeforeEach
public void setup() {
// the line below is where the magic happens
openMocks = MockitoAnnotations.openMocks(this);
doReturn(myObject).when(myObjectObjectProvider)
.getIfAvailable();
someOtherClass = spy(configuration.getSomeOtherClass());
}
#AfterEach
void tearDown() throws Exception {
openMocks.close();
}
#Test
void test() {
assertNotNull(someOtherClass);
}
#ExtendWith(MockitoExtension.class)
You can also use the #ExtendWith(MockitoExtension.class) annotation over your class and it has the same effect as the methods described above.
You can find both approaches tested in a GitHub repository I've created (all tests pass).

How to initalize spring bean for separation between tests in junit?

I'm using Junit5 and Spring for test.
I want to initalize spring bean for each test because I don't want different tests to change the other results of tests.
I'm knowing that a new instance of the test class is created before running each test method by default. under result of test codes is true,because the instance variable number is initalized for each test by junit5.
public class TestInstanceVaribale{
int number = 0;
#Test
public void test1() {
number += 3;
Assertions.assertEquals(3, number);
}
#Test
public void test2() {
number += 5;
Assertions.assertEquals(5, number);
}
}
but, this code is failed because spring bean is not initalized.
#Component
public class Car {
public String name = "myCar";
}
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
#SpringBootTest
#TestMethodOrder(OrderAnnotation.class)
public class TestSpringVariable {
#Autowired
Car car;
#Test
#Order(1)
public void test1() {
car.name = "testCar";
Assertions.assertEquals("testCar", car.name);
}
#Test
#Order(2)
public void test2() {
// this is fail. expected: <myCar> but was: <testCar>
// but I want expected: <myCar> but was: <myCar>
Assertions.assertEquals("myCar", car.name);
}
}
How to initalize spring bean for separation between tests in junit?
#SpringBootTest
#initalizeSpringBeanPerMethod <-- I want like this
public class TestSpringVariable2 {
#Autowired
Car car;
#BeforeEach
public void initalize() { <-- I want like this
SpirngBean.initalize()
}
}
Take a look at DirtiesContext
Probably adding this to your class should work. It's telling Spring to reset it's state after/before (depending on how you set it) each test
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)

Spring batch + repository Testing

This is my reader that work in job and step i'u using a repository to get users(public interface UserRepository extends JpaRepository<User,Long>):
#Slf4j
public class Reader implements ItemReader<User> {
#Autowired
UserRepository userRepository;
private Iterator<User>userIterator;
#BeforeStep
public void before(StepExecution execution){
userIterator=userRepository.findAll().iterator();
}
#Override
public User read() {
if (userIterator != null && userIterator.hasNext()) {
User user=userIterator.next();
log.info("User-->"+user.toString());
return user;
} else {
return null;
}
}
}
This is my test class:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringBatchApplication.class, BatchTestConfiguration.class})
public class SpringBatchApplicationTest {
#Autowired
private JobLauncherTestUtils testUtils;
#Autowired
private BatchConfig config;
#Test
public void testEntireJob() throws Exception {
final JobExecution result = testUtils.getJobLauncher().run(config.processJob(), testUtils.getUniqueJobParameters());
Assert.assertNotNull(result);
Assert.assertEquals(BatchStatus.COMPLETED, result.getStatus());
}
#Test
public void testSpecificStep() {
Assert.assertEquals(BatchStatus.COMPLETED, testUtils.launchStep("orderStep1").getStatus());
}
}
When i`m running my test i got a :
Caused by: java.lang.IllegalStateException: Cannot determine embedded database for tests. If you want an embedded database please put a supported one on the classpath.
What do i need to add to make determine of my database. Do i need to place application properties somewhere or something else?
There is how my test situate in project enter image description here

Zoned time gets converted to UTC in Spring Integration test

I'm trying to verify that my reactive rest controller transfers the correct data. This data contains a ZonedDateTime field I need to retain. However, when querying the rest controller with a WebTestClient, my verification fails because the received time is suddenly in UTC.
#Data
public class SimpleData {
ZonedDateTime zdt;
}
#RestController
class SimpleDataController {
#Autowired SimpleDataService service;
#GetMapping("/simple")
List<SimpleData> getData() {
return service.getTimes();
}
}
#Service
class SimpleDataService {
public static final SimpleData DATA = new SimpleData();
static {
DATA.setZdt(ZonedDateTime.now(ZoneId.of("Europe/Berlin")));
}
public List<SimpleData> getTimes() {
return List.of(DATA);
}
}
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ActiveProfiles("test")
class ApplicationTests {
#Test
void simpleDataTest() {
List<SimpleData> fromRest = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build()
.get().uri("/simple").exchange()
.expectBodyList(SimpleData.class)
.returnResult().getResponseBody();
assertThat(fromRest).containsAll(Collections.singletonList(SimpleDataService.DATA));
}
}
This fails with
Expecting ArrayList:
<[SimpleData(zdt=2020-08-05T09:30:40.291415300Z[UTC])]> to contain:
<[SimpleData(zdt=2020-08-05T11:30:40.291415300+02:00[Europe/Berlin])]>
but could not find the following element(s):
<[SimpleData(zdt=2020-08-05T11:30:40.291415300+02:00[Europe/Berlin])]>
The time itself is correct - the time zone difference is substracted from the hour field - but it fails the equals obviously. Funnily enough, if you query the url with a client, the JSON contains the correct data:
[{"zdt":"2020-08-05T11:44:10.4740259+02:00"}]
It seems to be the TestWebClient converting the time.
Is this intended? Can I change this behaviour somehow?
In summary, ADJUST_DATES_TO_CONTEXT_TIME_ZONE alone is not sufficient, WebTestClient should not be created manually and instead it should use #AutoConfigureWebTestClient and autowire the client.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ActiveProfiles("test")
#AutoConfigureWebTestClient
class ApplicationTests {
#Autowired
private WebTestClient client;
#Test
void simpleDataTest() {
List<SimpleData> fromRest = client.
.get().uri("/simple").exchange()
.expectBodyList(SimpleData.class)
.returnResult().getResponseBody();
assertThat(fromRest)
.containsAll(singletonList(SimpleDataService.DATA));
}
}
application.properties
spring.jackson.deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE = false
I couldn't sleep without getting a solution for this. So here you go! A working solution to your problem. :)
#JsonComponent
class ZonedDateTimeJsonSerializer extends JsonSerializer<ZonedDateTime> {
static DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
#Override
public void serialize(ZonedDateTime zdt, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException,
JsonProcessingException {
jsonGenerator.writeString(zdt.format(formatter));
}
}
#JsonComponent
class ZonedDateTimeJsonDeserializer extends JsonDeserializer<ZonedDateTime> {
static DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME;
#Override
public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
return ZonedDateTime.parse(p.getValueAsString(),formatter);
}
}
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ActiveProfiles("test")
#AutoConfigureWebTestClient
class ApplicationTests {
#Autowired
private WebTestClient client; // important! as the #jsonComponent has to be picked up
#Test
void simpleDataTest() {
List<SimpleData> fromRest = client.
.get().uri("/simple").exchange()
.expectBodyList(SimpleData.class)
.returnResult().getResponseBody();
assertThat(fromRest)
.containsAll(singletonList(SimpleDataService.DATA));
}
}

How do I use a different Spring datasource depending on the name of the unit test?

)
I have a rather unusual requirement for a series of unit tests I'm fixing. Basically, a different datasource/transaction manager needs to be used depending on the method name of the unit test.
For example, if the test name ends with UseDB2, then we use the DB2 data source, if it's UseH2 then we use the H2 datasource.
I thought the way to go about this was to use the AbstractRoutingDatasource provided by the Spring framework.
public class TestRoutingDatasSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
Using a context holder to select the required datasource:
public class DatabaseContextHolder {
private static final ThreadLocal<DType> contextHolder = new ThreadLocal<DType>();
public static void setDatabaseType(DType databaseType) {
contextHolder.set(databaseType);
}
public static DType getDatabaseType() {
return (DType) contextHolder.get();
}
public static void clearDatabaseType() {
contextHolder.remove();
}
}
I was then going to use the name of the test to set the context; something like this:
public class MyDBUnitTestCase extends
AbstractTransactionalDataSourceSpringContextTests {
protected DataSource dataSource;
protected String schemaName;
public void setDataSource(DataSource aDataSource) {
this.dataSource = aDataSource;
}
public void setSchemaName(String aSchemaName) {
this.schemaName = aSchemaName;
}
protected void onSetUp() throws Exception {
super.onSetUp();
if (getName().endsWith("UsingDB2")) {
DatabaseContextHolder.setDatabaseType(DType.DB2);
}
else {
DatabaseContextHolder.setDatabaseType(DType.H2);
}
}
But of course, this won't work because by the time I've come to check the name of the test, Spring has already configured all the beans (doh!).
Is there some other mechanism I can use to get this to work?
Thanks very much for your time.

Resources