@Test
public void testHappy() throws Exception {

    // SETUP - request
    final PeriodTypeDto periodType = PeriodTypeDto.DAILY;
    final ChannelDto channel = ChannelDto.ONE;
    final PeriodicMeterReadsRequestDto request = new PeriodicMeterReadsRequestDto(periodType,
            this.fromDateTime.toDate(), this.toDateTime.toDate(), channel);
    // SETUP - dlms objects
    final DlmsObject dlmsClock = new DlmsClock("0.0.1.0.0.255");
    final DlmsCaptureObject captureObject1 = new DlmsCaptureObject(dlmsClock, 2);
    final DlmsObject dlmsExtendedRegister = new DlmsExtendedRegister(DlmsObjectType.MBUS_MASTER_VALUE,
            "0.0.24.0.0.255", 0, RegisterUnit.M3, Medium.GAS);
    final DlmsCaptureObject captureObject2 = new DlmsCaptureObject(dlmsExtendedRegister, 2);
    final DlmsCaptureObject captureObject3 = new DlmsCaptureObject(dlmsExtendedRegister, 5);
    final List<DlmsCaptureObject> captureObjects = Arrays.asList(captureObject1, captureObject2, captureObject3);
    final DlmsProfile dlmsProfile = new DlmsProfile(DlmsObjectType.DAILY_LOAD_PROFILE, "1.2.3.4.5.6",
            captureObjects, ProfileCaptureTime.DAY, Medium.COMBINED);
    // SETUP - mock dlms object config to return attribute addresses
    final AttributeAddressForProfile attributeAddressForProfile = this.createAttributeAddressForProfile(dlmsProfile,
            captureObjects);
    final AttributeAddress attributeAddressScalerUnit = this.createAttributeAddress(dlmsExtendedRegister);
    when(this.dlmsObjectConfigService.findAttributeAddressForProfile(eq(this.device),
            eq(DlmsObjectType.DAILY_LOAD_PROFILE), eq(channel.getChannelNumber()), eq(this.fromDateTime),
            eq(this.toDateTime), eq(Medium.GAS))).thenReturn(Optional.of(attributeAddressForProfile));
    when(this.dlmsObjectConfigService.getAttributeAddressesForScalerUnit(eq(attributeAddressForProfile),
            eq(channel.getChannelNumber()))).thenReturn(Collections.singletonList(attributeAddressScalerUnit));
    final DlmsObject intervalTime = mock(DlmsObject.class);
    when(this.dlmsObjectConfigService.findDlmsObject(any(Protocol.class), any(DlmsObjectType.class),
            any(Medium.class))).thenReturn(Optional.of(intervalTime));
    // SETUP - mock dlms helper to return data objects on request
    final DataObject data0 = mock(DataObject.class);
    final DataObject data1 = mock(DataObject.class);
    final DataObject data2 = mock(DataObject.class);
    final DataObject bufferedObject1 = mock(DataObject.class);
    when(bufferedObject1.getValue()).thenReturn(asList(data0, data1, data2));
    final DataObject data3 = mock(DataObject.class);
    final DataObject data4 = mock(DataObject.class);
    final DataObject data5 = mock(DataObject.class);
    final DataObject bufferedObject2 = mock(DataObject.class);
    when(bufferedObject2.getValue()).thenReturn(asList(data3, data4, data5));
    final DataObject resultData = mock(DataObject.class);
    when(resultData.getValue()).thenReturn(asList(bufferedObject1, bufferedObject2));
    final String expectedDescription = "retrieve periodic meter reads for " + periodType + ", channel " + channel;
    final GetResult getResult = mock(GetResult.class);
    when(this.dlmsHelper.getAndCheck(eq(this.connectionManager), eq(this.device), eq(expectedDescription),
            eq(attributeAddressForProfile.getAttributeAddress()))).thenReturn(Collections.singletonList(getResult));
    when(this.dlmsHelper.getAndCheck(this.connectionManager, this.device, expectedDescription,
            attributeAddressScalerUnit)).thenReturn(Collections.singletonList(getResult));
    when(this.dlmsHelper.readDataObject(eq(getResult), any(String.class))).thenReturn(resultData);
    // SETUP - mock dlms helper to handle converting the data objects
    final String expectedDateTimeDescriptionLogTime = String.format("Clock from %s buffer", periodType);
    final String expectedDateTimeDescriptionCaptureTime = "Clock from mbus interval extended register";
    final CosemDateTimeDto cosemDateTime = new CosemDateTimeDto(this.fromDateTime);
    when(this.dlmsHelper.readDateTime(data0, expectedDateTimeDescriptionLogTime)).thenReturn(cosemDateTime);
    when(this.dlmsHelper.readDateTime(data3, expectedDateTimeDescriptionLogTime)).thenReturn(cosemDateTime);
    when(this.dlmsHelper.readDateTime(data2, expectedDateTimeDescriptionCaptureTime)).thenReturn(cosemDateTime);
    when(this.dlmsHelper.readDateTime(data5, expectedDateTimeDescriptionCaptureTime)).thenReturn(cosemDateTime);
    final DlmsMeterValueDto meterValue1 = mock(DlmsMeterValueDto.class);
    final DlmsMeterValueDto meterValue2 = mock(DlmsMeterValueDto.class);
    when(this.dlmsHelper.getScaledMeterValue(data1, null, "gasValue")).thenReturn(meterValue1);
    when(this.dlmsHelper.getScaledMeterValue(data4, null, "gasValue")).thenReturn(meterValue2);
    // CALL
    final PeriodicMeterReadGasResponseDto result = this.executor.execute(this.connectionManager, this.device,
            request);
    // VERIFY - the right functions should be called
    verify(this.dlmsMessageListener).setDescription(String.format(
            "GetPeriodicMeterReadsGas for channel ONE, DAILY from %s until %s, retrieve attribute: {%s,%s,%s}",
            new DateTime(this.from), new DateTime(this.to), dlmsProfile.getClassId(),
            dlmsProfile.getObisCodeAsString(), dlmsProfile.getDefaultAttributeId()));
    verify(this.dlmsHelper, times(2)).validateBufferedDateTime(any(DateTime.class),
            argThat(new DateTimeMatcher(this.from)), argThat(new DateTimeMatcher(this.to)));
    verify(this.dlmsObjectConfigService).findDlmsObject(any(Protocol.class), any(DlmsObjectType.class),
            any(Medium.class));
    // ASSERT - the result should contain 2 values
    final List<PeriodicMeterReadsGasResponseItemDto> periodicMeterReads = result.getPeriodicMeterReadsGas();
    assertThat(periodicMeterReads.size()).isEqualTo(2);
    assertThat(periodicMeterReads.stream().anyMatch(r -> r.getConsumption() == meterValue1)).isEqualTo(true);
    assertThat(periodicMeterReads.stream().anyMatch(r -> r.getConsumption() == meterValue2)).isEqualTo(true);
    assertThat(periodicMeterReads.stream().allMatch(r -> this.areDatesEqual(r.getLogTime(), cosemDateTime)))
            .isEqualTo(true);
    assertThat(periodicMeterReads.stream().allMatch(r -> this.areDatesEqual(r.getCaptureTime(), cosemDateTime)))
            .isEqualTo(true);
}