GDPR Cookie Consent by Free Privacy Policy Generator
post thumb
Asset Control
by Matthias Hanitzsch-Moonlight/ on 02 Apr 2018

Integration Testing - Timeseries Validation Functions

Here, I demonstrate how to write integration tests for timeseries validation functions.

The validation function

For this demo, I will choose the Asset Control out-of-the-box percentage check function. You apply it to an ADO in a given tree and if a price change is above the specified percentage threshold the new record gets flagged with a suspect status of 2.

See below for a screenshot from the AC Desktop:

Percentage Timeseries Validation

Test implementation

For the test implementation, I will split the presentation of the source code into several parts.

First, see the familiar wiring and setting up of test resources:

package io.terrafino.ac;

import io.terrafino.api.ac.AcException;
import io.terrafino.api.ac.ado.Ado;
import io.terrafino.api.ac.attribute.Attributes;
import io.terrafino.api.ac.service.AcConnection;
import io.terrafino.api.ac.service.AcService;
import io.terrafino.api.ac.timeseries.TsRecord;
import io.terrafino.api.ac.timeseries.TsRecords;
import io.terrafino.api.ac.validation.TsValidationPercentage;
import io.terrafino.api.ac.value.Values;
import org.junit.*;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class PercentageCheckIntegrationTest {

    private static AcConnection conn;
    private static AcService ac;

    private Ado ado;

    @BeforeClass
    public static void setUp() throws Exception {
        conn = AcConnection.getDefaultConnection();
        ac = new AcService(conn);
    }

    @AfterClass
    public static void tearDown() throws Exception {
        conn.disconnect();
    }

    @Before
    public void setUpTest() throws Exception {
        ado = ac.testAdoWithPrefix("C0.TFN").withTemplate("C0_IN_T001_LSA").createInAc();
    }

    @After
    public void tearDownTest() throws Exception {
        if (ado != null && ado.existsInAc()) {
            ado.deleteTimeseries(TREE);
            ado.delete();
        }
    }
    
    // See rest of article ...

}

The above allows me to connect to and disconnect from Asset Control and it creates and deletes a test ADO for each test that I am going to run.

Please note that this also takes care of deleting the timeseries of the test ADO! While in Asset Control it is possible to delete an ADO but leave its timeseries behind, this is bad practice and you should always make sure that when deleting an ADO you also delete its timeseries.

In the next step, I am adding a few methods to make my tests concise and readable:

    private static String TREE = "CONSOLIDATION_C0";
    private static Attributes ATTRIBUTES = new Attributes("CLOSE");
    private static int NORMAL = 1;
    private static int SUSPECT = 2;

    private void givenTheClosingPrices(double price1, double price2) throws AcException {
        ado.storeTimeseries(TREE,
                new TsRecords(
                        new TsRecord(20180723, 235959, ATTRIBUTES, new Values(price1)),
                        new TsRecord(20180724, 235959, ATTRIBUTES, new Values(price2))
                )
        );
    }

    private void whenWeRevalidateWithAThresholdOf(double percent) throws AcException {
        ado.addTimeseriesValidation(TREE, new TsValidationPercentage(percent));
        ado.revalidate(TREE, 20180723, 20180724);
    }

    private void thenTheNewPriceHasStatus(int status) throws AcException {
        TsRecords records = ado.loadTimeseries(TREE, 20180724, 20180724, ATTRIBUTES);
        assertThat(records.get(0).get(0).getStatus(), is(status));
    }

Here is a breakdown of it:

  • givenTheClosingPrices allows me to create two timeseries records. All I have to provide are the two prices I am testing with.
  • whenWeRevalidateWithAThresholdOf takes care of applying the percentage check with the given percentage and revalidating the timeseries.
  • thenTheNewPriceHasStatus will check that the status of the second record is indeed the expected one.

These methods now allow me to write my tests following a pattern of Given - When - Then:

    @Test
    public void doesNotFlagIfNoChange() throws Exception {
        givenTheClosingPrices(100.0, 100.0);
        whenWeRevalidateWithAThresholdOf(5.0);
        thenTheNewPriceHasStatus(NORMAL);
    }

    @Test
    public void doesNotFlagIfNewPriceIsSmallerAndWithinThreshold() throws Exception {
        givenTheClosingPrices(100.0, 99.0);
        whenWeRevalidateWithAThresholdOf(5.0);
        thenTheNewPriceHasStatus(NORMAL);
    }

    @Test
    public void doesNotFlagIfNewPriceIsGreaterAndWithinThreshold() throws Exception {
        givenTheClosingPrices(100.0, 101.0);
        whenWeRevalidateWithAThresholdOf(5.0);
        thenTheNewPriceHasStatus(NORMAL);
    }

    @Test
    public void doesFlagIfNewPriceIsGreaterAndAboveThreshold() throws Exception {
        givenTheClosingPrices(100.0, 110.0);
        whenWeRevalidateWithAThresholdOf(5.0);
        thenTheNewPriceHasStatus(SUSPECT);
    }

    @Test
    public void doesFlagIfNewPriceIsSmallerAndUnderThreshold() throws Exception {
        givenTheClosingPrices(100.0, 90.0);
        whenWeRevalidateWithAThresholdOf(5.0);
        thenTheNewPriceHasStatus(SUSPECT);
    }

Now you have readable, executable tests and you will not have to test the percentage check manually ever again.

Thank you for reading!

If you are interested in implementing even more human-readable tests, please have a look at the series of articles about BDD and Cucumber.

comments powered by Disqus