Simple Metric Test

Hi guys…I’m Ilaria, a new user of this community, and I have a question for you:
I actually have a simple metric (ChInRevproMeActiveEnergy) which returns value of billed active energy consumption and I made on it some tests.
According to you, with these tests am I respecting the c3 best practices??
On below I will attach respectively metric’s description and the script with tests.

-METRIC’S DESCRIPTION:

{
“srcType”: “ChInRevproTdServicePoint”,
“id”: “ChInRevproMeActiveEnergy_ChInRevproTdServicePoint” ,
“name”: “ChInRevproMeActiveEnergy”,
“tsDecl”: {
“data”: “billing”,
“treatment”: “SUM”,
“overlapHandling”: “SUM”,
“start”: “startPeriod”,
“end”: “endPeriod”,
“value”: “billedConsumption”,
“filter”: “intersects(readingKey, [‘I’, ‘N’, ‘V’, ‘S’]) && intersects(consumptionType, [‘ACHP’, ‘ACLL’, ‘ACTI’, ‘ACVA’, ‘ACVN’, ‘ADIC’, ‘BASE’, ‘BINV’])”
},
“description”: “Billed active energy consumption.”
}

-SCRIPT WITH TESTS:

var filename = “test_ChInRevproMeActiveEnergy”;

// Describe is a function in the Jasmine testing framework. It simply describes the suite of test cases enumerated by the “it” functions
describe(filename, function() {

//function to create the timeseries
function addServicePoints(ctx, servicePointId, startDate, endDate, quantity){

    var servicePoint = ChInRevproTdServicePoint.make({
        id: servicePointId
    });
    TestApi.createEntity(ctx, 'ChInRevproTdServicePoint', servicePoint);
  
   //time series creation
    var rawObjs = [];
    for (var m = 0; m < quantity.length; m++) {
        var point = ChInRevproTdBilling.make({
            id: "m",
            servicePointId: servicePointId,
            startPeriod: startDate.toISOString(),
            endPeriod: endDate.toISOString(),
            readingKey: 'N',
            consumptionType: 'ACHP',
            billedConsumption: quantity[m]
        });
        rawObjs.push(point);
        startDate.addDays(1);
        endDate.addDays(1);
       
    }

    //addition of the observation to test the overlap
    // for the date range 2018/01/02-2018/01/03
    var point = ChInRevproTdBilling.make({
            id: "overlap",
            servicePointId: servicePointId,
            startPeriod: DateTime.deserialize('2018-01-02T06:00:00.000Z'),
            endPeriod: DateTime.deserialize('2018-01-02T18:00:00.000Z'),
            readingKey: 'N',
            consumptionType: 'ACHP',
            billedConsumption: 1
        });
    rawObjs.push(point);

    TestApi.createBatchEntity(ctx, 'ChInRevproTdBilling', rawObjs);
}


 beforeAll(function() {
    this.context = TestApi.createContext(filename);
});

it("setup", function() {

    // Create test Service Point
    addServicePoints(this.context, 'jasmine_test', 
        DateTime('2018-01-01T00:00:00.000Z'), 
        DateTime('2018-01-01T23:00:00.000Z'), [2,2,2,2]);

    expect(this.context).toBeDefined();
});

 it("general test with no overlap (one observation in a day)", function() {
    var result = ChInRevproTdServicePoint.evalMetric({
        id: "jasmine_test",
        expression: "ChInRevproMeActiveEnergy",
        start: DateTime.deserialize("2018-01-01"),
        end: DateTime.deserialize("2018-01-02"),
        interval: "DAY"
    });

    expect(result.data()[0]).toEqual(2);
    expect(result.missing()[0]).toEqual(4);
});



it("test overlap (2 different observations)", function() {
    var result = ChInRevproTdServicePoint.evalMetric({
        id: "jasmine_test",
        expression: "ChInRevproMeActiveEnergy",
        start: DateTime.deserialize("2018-01-02"),
        end: DateTime.deserialize("2018-01-03"),
        interval: "DAY"
    });

    expect(result.data()[0]).toBeCloseTo(3,1);
    expect(result.missing()[0]).toEqual(4);
});

});

Thank you all in advance.

Ilaria :slight_smile:

Hey there,

in general, this looks pretty good. a few (minor) comments:

  1. you shoudl put your “setup” in the beforeAll() block. Generally you don’t need to check that your context is defined either, thats kind of a given.

  2. I recommend not setting the “id” property on your test objects when possible. Instead follow the pattern of using the ‘this’ context and passing objects from block to block: Better Jasmine Tests With this

  3. I’m interested to know the definition of your types. From what i can see it looks like you don’t have to use a “tsdecl” metric… the data you’re looking at appears to be TimeSeries data, in which case you can use a regular metric. Note the tsDecl metrics are more flexible than “regular” metrics, but they are also slower and more error prone.

  4. Finally, you forgot to cleanup! You shoudl put an afterAll() block where you TestApi.teardown() to clean up before the next test runs.

Firstly, thank you so much for your advices.

  • But what do you exactly mean with “definition of my types”?
  • Besides, I had in my script the code to clean up my environment but it didn’t work…I don’t know why. Now I will attach on below the afterAll() block where my TestApi.teardown() :

{
afterAll(function() {
this.context.setWaitForTeardown(0);
TestApi.removeEntitiesByFilter(this.ctx, filter, {moduleName: ‘structure’, typeName: ‘Facility’});
TestApi.teardown(this.context, null);
});
}

Is it correct or can you identify some errors?

For the teardown, this shoudl be sufficient no?

afterAll(function() {
  TestApi.teardown(this.ctx)
})

As for “definition of types”, i mean can i see the type: ChInRevproTdServicePoint and its “billedConsumption” field?

Well, On below I will do a little recap of your points and I will attach some scripts:

1. " you should put your “setup” in the beforeAll() block. Generally you don’t need to check that your context is defined either, thats kind of a given."
1. Done :white_check_mark:

2. "I recommend not setting the “id” property on your test objects when possible. Instead follow the pattern of using the ‘this’ context and passing objects from block to block: Better Jasmine Tests With this 2"
2. This point isn’t enough clear to me. I read some papers about it but I can’t apply it on my particular situation.
Could you give me an example on my script?

3. "I’m interested to know the definition of your types. From what i can see it looks like you don’t have to use a “tsdecl” metric… the data you’re looking at appears to be TimeSeries data, in which case you can use a regular metric. Note the tsDecl metrics are more flexible than “regular” metrics, but they are also slower and more error prone."
3. Unluckily I can’t use regular metrics in this case because, as you can see from the definition’s page on below, my types are External, not Time series.

- ChInRevproTdServicePoint :

entity type ChInRevproTdServicePoint mixes External, NoSystemCols, MetricEvaluatable schema name ‘servicepoint’ {
id: ~ schema name ‘servicepointid’
servicePointNumber: int schema name ‘servicepointnumber’
companyId: int schema name ‘companyid’
isInBlackList: int schema name ‘isinblacklist’

billing:                  [ChInRevproTdBilling](servicePointId)
inspection:               [ChInRevproTdInspection](servicePointId)
customer:                 [ChInRevproTdCustomer](servicePointId)
apparatus:                [ChInRevproTdApparatus](servicePointId)
workOrder:                [ChInRevproTdWorkOrder](servicePointId)
geographic:               [ChInRevproTdGeographic](id)

- ChInRevproTdBilling
entity type ChInRevproTdBilling mixes External, NoSystemCols schema name ‘billing’ {
id: ~ schema name ‘consumptionid’
servicePointId: string schema name ‘servicepointid’
servicePointNumber: long int schema name ‘servicepointnumber’
companyId: int schema name ‘companyid’
consumptionType: string schema name ‘consumptiontype’
billedConsumption: double schema name ‘billedconsumption’
startPeriod: datetime schema name ‘startperiod’
endPeriod: datetime schema name ‘endperiod’
readingKey: string schema name ‘readingkey’
connectionStatus: string schema name ‘connectionstatus’
connectionType: string schema name ‘connectiontype’
spliceType: string schema name ‘splicetype’
spliceDescription: string schema name ‘splicedescription’
availablePower: double schema name ‘availablepower’
voltageLevel: string schema name ‘voltagelevel’
tariff: string schema name ‘tariff’
tariffCode: string schema name ‘tariffcode’
}

4. "Finally, you forgot to cleanup! You shoudl put an afterAll() block where you TestApi.teardown() to clean up before the next test runs."
4.The teardown block doesn’t work because actually I’m not able to remove datas from external table. There’s something to do with table configuration on Cloudera, I am working on it.

Thank you again :slight_smile:

Got it. Maybe this doesn’t apply to you given that everything is external, but for #2 i meant something like:

function addServicePoints(ctx, startDate, endDate, quantity){
    var servicePoint =  TestApi.createEntity(ctx, 'ChInRevproTdServicePoint', servicePoint);
  
   //time series creation
    var rawObjs = [];
    for (var m = 0; m < quantity.length; m++) {
        var point = ChInRevproTdBilling.make({
            id: "m",
            servicePointId: servicePointId,
            startPeriod: startDate.toISOString(),
            endPeriod: endDate.toISOString(),
            readingKey: 'N',
            consumptionType: 'ACHP',
            billedConsumption: quantity[m]
        });
        rawObjs.push(point);
        startDate.addDays(1);
        endDate.addDays(1);
       
    }

    //addition of the observation to test the overlap
    // for the date range 2018/01/02-2018/01/03
    var point = ChInRevproTdBilling.make({
            id: "overlap",
            servicePointId: servicePointId,
            startPeriod: DateTime.deserialize('2018-01-02T06:00:00.000Z'),
            endPeriod: DateTime.deserialize('2018-01-02T18:00:00.000Z'),
            readingKey: 'N',
            consumptionType: 'ACHP',
            billedConsumption: 1
        });
    rawObjs.push(point);

    TestApi.createBatchEntity(ctx, 'ChInRevproTdBilling', rawObjs);

  return servicePoint;
}

beforeAll(function() {
    this.context = TestApi.createContext(filename);

    // Create test Service Point
   this.servicePoint =  addServicePoints(this.context, 
        DateTime('2018-01-01T00:00:00.000Z'), 
        DateTime('2018-01-01T23:00:00.000Z'), [2,2,2,2]);
});

 it("general test with no overlap (one observation in a day)", function() {
    var result = ChInRevproTdServicePoint.evalMetric({
        id: this.servicePoint,
        expression: "ChInRevproMeActiveEnergy",
        start: DateTime.deserialize("2018-01-01"),
        end: DateTime.deserialize("2018-01-02"),
        interval: "DAY"
    });

TestApi.creatEntity will fail if you try to create an object with an id that already exists. By not setting the ‘id’ at all you allow the system to generate one for you. In this way you could concievably run this test multiple times in parallel, and you will protect yourself from old data (which you could not remove) breaking your test. ALL THAT BEING SAID since you cannot remove data from the external table this coudl result in creation of extra data.

1 Like