2015-05-12 2 views
4

Следующий код не скомпилирует, но иллюстрирует, что я хотел бы сделать: totalTests должен содержать количество времени, которое вызывается assertEquals() (assertEquals(), вероятно, должен быть макросом для этого быть может, но я еще не знаком с этим аспектом Нима).Счет вызовов времени компиляции в Nim

Любая идея, как этот код следует изменить, чтобы включить следующий код для печати [1/2] и [2/2] в начале каждой строки отчета об испытаниях?

from strutils import format 

var countTested = 0 
var countFailed = 0 
var countPassed = 0 
let totalTests = 0 # <-- need let or other compile-time type here (?) 

# using proc here... macro may be needed to be able to count calls (?) 
proc assertEquals*[T](testName: string, expected: T, p: (proc(): T)) = 
    countTested += 1 
    totalTests += 1 # <-- compilation error (how can we increase each time it is called?) 
    write(stdout, format("[$num/$total] $name: ", "num", countTested, "total", totalTests, "name", testName)) 
    var val = p() 
    if val == expected: 
    write(stdout, "passed\n") 
    countPassed += 1 
    else: 
    write(stdout, "failed\n") 
    countFailed += 1 

when isMainModule: 
    assertEquals("test #A", 12, proc(): int = 14-2) 
    assertEquals("test #B", 12, proc(): int = 12-2) 

Редактирование: добавлены вопросы в коде

+1

var totalTests вместо let. Пусть, вероятно, непреложный. Я ничего не знаю об этом языке. – luqui

+0

Вы правы luqui, здесь я использовал let, чтобы проиллюстрировать намерение (вычисление времени вычисления, Ним, похоже, хорошо выполняет операции компиляции, поэтому может быть способ сделать это). Но на самом деле пример не компилируется. – x2f

+0

Кстати, вы заметили модуль unittest (http://nim-lang.org/docs/unittest.html)? Одна интересная информация о Nim заключается в том, что ей не нужен большой набор функций assert, используемых на других языках (assertEquals, assertGreater и т. Д.). Причина этого заключается в том, что вы можете написать один макрос assert, который будет проверять переданное выражение и распознать используемый оператор (==, <и т. Д.), Чтобы распечатать сообщение с ошибкой с правильным утверждением. В модуле unittest это макрос 'check'. – zah

ответ

4

Вот один из способов сделать это. Вы можете выполнить код во время компиляции с помощью макроса или оператора static. Обратите внимание, что до сих пор нет возможности надежно подсчитать их на нескольких модулях.

import macros, strutils 

proc beginTests() 

var countTested = 0 
var countFailed = 0 
var countPassed = 0 
var totalTests = 0 
var totalTestsCT {.compiletime.} = 0 

macro endTests(): stmt = 
    quote do: 
    proc beginTests() = 
     totalTests = `totalTestsCT` 

proc assertEqualsImpl*[T](testName: string, expected: T, p: (proc(): T)) = 
    countTested += 1 
    write(stdout, format("[$num/$total] $name: ", 
     "num", countTested, "total", totalTests, "name", testName)) 
    var val = p() 
    if val == expected: 
    write(stdout, "passed\n") 
    countPassed += 1 
    else: 
    write(stdout, "failed\n") 
    countFailed += 1 

macro assertEquals*[T](testName: string, expected: T, p: (proc(): T)): stmt = 
    totalTestsCT += 1 
    quote do: 
    assertEqualsImpl(`testName`, `expected`, `p`) 

when isMainModule: 
    beginTests() 
    assertEquals("test #A", 12, proc(): int = 14-2) 
    assertEquals("test #B", 12, proc(): int = 12-2) 
    endTests() 

Альтернативная реализация заключалась бы в том, чтобы внедрить тесты в пользовательский оператор блока, например.

testSuite: 
    assertEquals("test #A", 12, proc(): int = 14-2) 
    assertEquals("test #B", 12, proc(): int = 12-2) 

testSuite макрос будет затем подсчитать утверждения во встроенном коде и инициализировать переменную соответствующим образом.

Еще одно решение - не выполнять тесты напрямую, а хранить их в списке и выполнять их только в конце.

+0

Спасибо Reimer. Ваше последнее предложение работало лучше всего для меня. Я отредактировал ваш ответ, чтобы добавить код для этого. Ваше решение на основе макросов является хорошим и точным, но кажется, что макросы, вероятно, не лучший способ сделать это. Особенно это не срабатывало при вызове из другого модуля (как вы упомянули в своем ответе). В этом случае компилятор жалуется, что beginTests() не реализуется, даже если в этом модуле вызывается endTests(). В любом случае, спасибо за ваш ответ! – x2f

+0

На самом деле мое редактирование было отклонено, поэтому реализация добавляется как отдельный ответ ... – x2f

+0

Для чего это стоит, я даже не видел редактирование до того, как оно было отклонено, и это выглядит хорошо для меня. –

2

Вот реализация третьего предложения Реймера, который работал лучше всего для меня.

import macros, strutils 

type 
    TestSuiteObj = object 
    countTested: int 
    countFailed: int 
    countPassed: int 
    totalTests: int 
    tests: seq[(proc (self: TestSuite))] 
    TestSuite* = ref TestSuiteObj 


proc newTestSuite*(): TestSuite = 
    new(result) 
    result.countTested = 0 
    result.countFailed = 0 
    result.countPassed = 0 
    result.totalTests = 0 
    result.tests = @[] 

proc assertEquals*[T](self: TestSuite, testName: string, expected: T, p: (proc(): T)) = 
    self.totalTests += 1 

    var testProc = proc(self: TestSuite) = 
    self.countTested += 1 
    write(stdout, format("[$num/$total] $name: ", "num", self.countTested, "total", self.totalTests, "name", testName)) 
    var val = p() 
    if val == expected: 
     write(stdout, "passed\n") 
     self.countPassed += 1 
    else: 
     write(stdout, "failed\n") 
     self.countFailed += 1 

    self.tests.add(testProc) 


proc run*(self: TestSuite) = 
    self.totalTests = self.tests.len 
    for p in self.tests: 
    p(self) 

    var verdict = case (self.countTested == self.countPassed) 
    of true: "PASSED" 
    of false: "FAILED" 
    echo format("$verdict. Passed [$passed/$total] tests.", "verdict", verdict, "passed", self.countPassed, "total", self.countTested) 

    # Sanity 
    assert(self.countTested == (self.countFailed+self.countPassed)) 
    assert(self.countTested == self.totalTests) 


when isMainModule: 
    var suite = newTestSuite() 
    suite.assertEquals("test #A", 12, proc(): int = 14-2) 
    suite.assertEquals("test #B", 12, proc(): int = 12-2) 
    suite.run() 
Смежные вопросы