Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to add tests dynamically to python unittest

I'm wanting to set up a unittest TestCase where some of the cases are added dynamically. The methods are added as shown from my test_nothing, but, unittest doesn't take them into account as it runs only one test. It's like I build my test_xxxx too late and they are not seen. Is setUpClass executed too late in the game? Should I put this in __init__ build my methods, and then call super().__init__ ?

import unittest
import blognodes

class Test_base62(unittest.TestCase):
  testset = { 0: '0', 10: 'a', 61: 'Z', 62: '10', 3844: '100'}

  @classmethod
  def setUpClass(cls):
    cls.testme = 5
    print("i am the setUp function")
    for d, b62 in cls.testset.items():
      print("building the first set")
      cls.build_test_base62_values(d, b62)
      print("building the second set")
      cls.build_test_int_values(d, b62)


  @classmethod
  def build_test_base62_values(cls, d, b62):
    def f(cls):
      target = blognodes.base62(d)
      cls.assertEqual(target.str(), b62)
    fname = "test_base62_value_{}".format(d)
    setattr(cls, fname, f)

  @classmethod
  def build_test_int_values(cls, d, b62):
    def f(cls):
      target = blognodes.base62(d)
      cls.assertEqual(target.int(), d)
    fname = "test_int_value_{}".format(d)
    setattr(cls, fname, f)

  def test_nothing(self):
    print("i'm test nothing")
    t = dir(self)
    print(t)
    self.assertEqual(5, self.testme)

Thanks.

like image 336
Yves Dorfsman Avatar asked May 22 '12 03:05

Yves Dorfsman


1 Answers

The issue is that the loader from unittest runs a dir() on the class before it instantiate it, therefore, it doesn't matter where you create your methods, __new__, __init__, setUpClass etc... the methods get created too late.

There are two ways to work around that, write your own run() method, or use a metaclass. The former means you have to re-write the discovery that's already written in unittest. The metaclass isn't actually that complicated to implement. Here's what I did:

import unittest
import blognodes


class meta_Test_base62(type):
  testset = { 0: '0', 10: 'a', 61: 'Z', 62: '10', 3844: '100'}

  @classmethod
  def __prepare__(mcls, name, bases):
    d = dict()
    d['testme'] = 5
    for b10, b62 in mcls.testset.items():
      fname = "test_base62_value_{}".format(b10)
      d[fname] = mcls.build_test_base62_values(b10, b62)
      fname = "test_int_value_{}".format(b10)
      d[fname] = mcls.build_test_int_values(b10, b62)
    return d

  @classmethod
  def build_test_base62_values(cls, b10, b62):
    def f(self):
      target = blognodes.base62(b10)
      self.assertEqual(target.str(), b62)
    return f

  @classmethod
  def build_test_int_values(cls, b10, b62):
    def f(self): 
      target = blognodes.base62(b10)
      self.assertEqual(target.int(), b10)
    return f




class Test_base62(unittest.TestCase, metaclass=meta_Test_base62):

  def test_nothing(self):
    self.assertEqual(5, self.testme)
like image 111
Yves Dorfsman Avatar answered Oct 26 '22 11:10

Yves Dorfsman