Skipping all unit tests but one in Python by using decorators and metaclasses

时间:2015-05-08 10:03:48

标签: python unit-testing decorator metaclass attributeerror

I am writing unit tests for an MCU that communicates commands through the USB port and checks their response. If one unit test fails it makes sense for me to do some debugging in the MCU. Therefore I would like to disable all unittests except for the one that I would like to debug on the MCU side because if I set a breakpoint somewhere it might get triggered by another unittest with different commands.

I went to the python docs and found this code which is a decorator that will skip all unittests that don't have an attribute.

func collectionView(_ collectionView: UICollectionView,
    didSelectItemAtIndexPath indexPath: NSIndexPath)
{
    hidemenu()
        self.view.endEditing(true)    
}

In order to make it more simple I deleted the attr argument and statically changed it to 'StepDebug' which is an attribute that I want to set in only one unittest in order to debug it.

So the next step is for me to apply this for all my class methods automatically. After reading around on the webs I found the following code that uses a metaclass in order to decorate all methods with the above decorator. https://stackoverflow.com/a/6308016/3257551

self.collectionView.backgroundView = [[UIView alloc] initWithFrame:self.collectionView.frame];
self.collectionView.backgroundView.userInteractionEnabled = YES;

UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];
self.collectionView.backgroundView.gestureRecognizers = @[tapRecognizer];

So my minimum working example is

def skipUnlessHasattr(obj, attr):
    if hasattr(obj, attr):
        return lambda func: func
return unittest.skip("{!r} doesn't have {!r}".format(obj, attr))

and the error that I get is:

def decorating_meta(decorator):
class DecoratingMetaclass(type):
    def __new__(self, class_name, bases, namespace):
        for key, value in list(namespace.items()):
            if callable(value):
                namespace[key] = decorator(value)
        return type.__new__(self, class_name, bases, namespace)

return DecoratingMetaclass

From what I read this is something that happens when instead of a class you have an instance but I don't quite understand how I can use this information to solve my problem.

Can someone please help?

2 个答案:

答案 0 :(得分:0)

好的,我找到了一种方法来获得我想要的功能。 我将装饰器更改为:

using namespace std;
setlocale(LC_ALL, "fa-IR");

time_t rawtime;
struct tm * timeinfo;

time(&rawtime);
timeinfo = localtime(&rawtime);

struct lconv * lc;
lc = localeconv();


LOGFONT * tFont;
tFont = GetFont();

CFont font;
VERIFY(font.CreateFont(
    tFont->lfHeight,                    // nHeight
    tFont->lfWidth,                     // nWidth
    0,                                  // nEscapement
    0,                                  // nOrientation
    tFont->lfWeight,                    // nWeight
    FALSE,                              // bItalic
    FALSE,                              // bUnderline
    0,                                  // cStrikeOut
    ARABIC_CHARSET,                     // nCharSet
    OUT_DEFAULT_PRECIS,                 // nOutPrecision
    CLIP_DEFAULT_PRECIS,                // nClipPrecision
    ANTIALIASED_QUALITY,                // nQuality
    DEFAULT_PITCH | FF_SWISS,           // nPitchAndFamily
    tFont->lfFaceName));            // lpszFacename 

const wchar_t yek = L'\u06F1';
const wchar_t dow = L'\u06F2';
const wchar_t seh = L'\u06F3';
const wchar_t chahar = L'\u06F4';
const wchar_t panj = L'\u06F5';
const wchar_t shish = L'\u06F6';
const wchar_t haft = L'\u06F7';
const wchar_t hasht = L'\u06F8';
const wchar_t noh = L'\u06F9';
const wchar_t sefr = L'\u06F0';

wchar_t wFATime[20];
wcscpy(wFATime, L"");

BOOL dayoftheweek = FALSE;
for (unsigned int i = 0; i<m_strText.GetLength(); i++)
{
    char c = m_strText[i]; //this is your character


    switch (c)
    {
    case '1':
        wcsncat(wFATime, &yek, 1);
        break;
    case '2':
        wcsncat(wFATime, &dow, 1);
        break;
    case '3':
        wcsncat(wFATime, &seh, 1);
        break;
    case '4':
        wcsncat(wFATime, &chahar, 1);
        break;
    case '5':
        wcsncat(wFATime, &panj, 1);
        break;
    case '6':
        wcsncat(wFATime, &shish, 1);
        break;
    case '7':
        wcsncat(wFATime, &haft, 1);
        break;
    case '8':
        wcsncat(wFATime, &hasht, 1);
        break;
    case '9':
        wcsncat(wFATime, &noh, 1);
        break;
    case '0':
        wcsncat(wFATime, &sefr, 1);
        break;
    case ':':
        wcsncat(wFATime, L":", 1);
        break;
    case '.':
        wcsncat(wFATime, L".", 1);
        break;
    case '/':
        wcsncat(wFATime, L"/", 1);
        break;
    default:
        dayoftheweek = TRUE;
    }
}

CFont* def_font = pdc->SelectObject(&font);
HDC hdc = pdc->GetSafeHdc();

int old = pdc->SetBkMode(TRANSPARENT);
if (dayoftheweek)
{
    CStringW strw(m_strText);
    ::TextOutW(hdc, m_poOffset.x, m_poOffset.y, strw, strw.GetLength());
}
else
{
    ::TextOutW(hdc, m_poOffset.x, m_poOffset.y, wFATime, wcslen(wFATime));
}



pdc->SelectObject(def_font);
pdc->SetBkMode(old);
// Done with the font.  Delete the font object.
font.DeleteObject();

现在我跳过所有测试,除了我添加了属性StepDebug的那些。

唯一的小问题是输出不会将所有其他测试报告为跳过,而是成功。

def skipUnlessHasattr(obj):
    if hasattr(obj, 'StepDebug'):
        def decorated(*a, **kw):
            return obj(*a, **kw)
        return decorated
    else:
        def decorated(*a, **kw):
            return unittest.skip("{!r} doesn't have {!r}".format(obj, 'StepDebug'))
        return decorated

P.S。为什么在缩进4个空格后复制输出时它不会进入代码块?我也尝试了8个空格,但它不起作用。最后我为每一行添加了4个空格。有更聪明的方法吗?

答案 1 :(得分:0)

https://stackoverflow.com/a/44804070/1587329使用装饰器,通过环境参数进行初始化

MCU = os.getenv('MCU', False)

并通过f.ex。

装饰要排除的测试类和/或方法
@unittest.skipIf(MCU)

这可以称为

MCU=1 python # test file etc
  

唯一的小问题是输出不会报告所有其他测试被跳过,而是成功。

这将跳过的测试标记为s