如何通过S表达式树来满足测试用例

问题描述

所以我有这个S表达式数组

  const condition = [
  'OR',[
    'AND',['==','$State','Alabama'],'$Profession','Software development']
  ],'$Undefined',''],'Texas']
  ],[
    'OR',[
      'OR','Tradesperson']
    ]
  ]
]

我有这个需要满足的测试用例和功能

const testCases = [
  [{'State': 'Alabama','Profession': 'Software development'},true],[{'State': 'Texas'},[{'State': 'Alabama','Profession': 'Gaming'},false],[{'State': 'Utah'},[{'Profession': 'Town crier'},[{'Profession': 'Tradesperson'},[{},false]
]

for (const [index,[context,expected]] of testCases.entries()) {
  console.log(
    evaluate(condition as Condition,context) === expected
      ? `${index} ok`
      : `${index} FAIL`
  )
}

到目前为止,我是

function evaluate (condition: Condition,context: Context): boolean {
  if (isLogicalCondition(condition)) {
    const [operator,...conditions] = condition

  }

  if (isComparisonCondition(condition)) {
    const [operator,variable,value] = condition
    
  }

  return false
}

函数,类型和变量是

type Context = {
  [k: string]: string | undefined
}

enum LogicalOperator {
  And = 'AND',Or = 'OR'
}

enum Comparisonoperator {
  Eq = '=='
}

type Operator = LogicalOperator | Comparisonoperator 
type LogicalCondition = [LogicalOperator,...Array<Condition>]
type Variable = string
type Value = string
type ComparisonCondition = [Comparisonoperator,Variable,Value]
type Condition = LogicalCondition | ComparisonCondition

function isLogicalCondition (condition: Condition): condition is LogicalCondition {
  return Object.values(LogicalOperator).includes(condition[0] as LogicalOperator)
}

function isComparisonCondition (condition: Condition): condition is ComparisonCondition {
  return Object.values(Comparisonoperator).includes(condition[0] as Comparisonoperator)
}

我的问题是,我该如何以一种足够抽象的方式考虑这个问题,以解决此问题,从而满足测试而又不对测试进行硬编码?我完全迷路了...

解决方法

您应该使用递归。 “或”运算符转换为some方法调用,而“与”运算符转换为every方法调用。

为了平等,您应该处理任何“ $”前缀,在这种情况下,您必须在上下文对象中查找值。最好不要假设第一个参数始终带有“ $” ...,因此我建议在两个参数上都使用一个映射器,每次处理潜在的“ $”。 / p>

您可以使用此功能:

function evaluate(condition,context) {
    let [operator,...arguments] = condition;
    if (operator === "==") {
        return arguments.map(arg => arg[0] === "$" ? context[arg.slice(1)] : arg)
                        .reduce(Object.is);
    }
    if (operator === "OR") {
        return arguments.some(argument => evaluate(argument,context));
    }
    if (operator === "AND") {
        return arguments.every(argument => evaluate(argument,context));
    }
    throw "unknown operator " + operator;
}

运行测试用例:

function evaluate(condition,context));
    }
    throw "unknown operator " + operator;
}


const condition = [
  'OR',[
    'AND',['==','$State','Alabama'],'$Profession','Software development']
  ],'$Undefined',''],'Texas']
  ],[
    'OR',[
      'OR','Tradesperson']
    ]
  ]
]

const testCases = [
  [{'State': 'Alabama','Profession': 'Software development'},true],[{'State': 'Texas'},[{'State': 'Alabama','Profession': 'Gaming'},false],[{'State': 'Utah'},[{'Profession': 'Town crier'},[{'Profession': 'Tradesperson'},[{},false]
]

for (const [index,[context,expected]] of testCases.entries()) {
  console.log(
    evaluate(condition,context) === expected
      ? `${index} ok`
      : `${index} FAIL`
  )
}