Monday, February 02, 2009

Writing CEEFIT class like a regular C++ class

If you need to write integrated tests that can take a table specifying input data and expected output data, and run the tests in a batch, then you should consider Fit: Framework for Integrated Test. If you develop applications with C++, then look to CEEFIT to satisfy your FIT needs.

CEEFIT is well documented, flexible and an excellent integrated test framework. In most cases you can use the Macro method to create your test-classes, but CEEFIT also provides a way to create test-classes manually from scratch. This second 'manually from scratch' method is what this post talks about.

Why would you want to write CEEFIT classes from scratch?
Well one reason for me was to ensure that I could still use Doxygen to document the classes and their methods and parameters i.e. make the code-documentation doxygen-visible. I have not been able to figure out how to have the documentation be doxygen visible when using the macros.

How to write CEEFIT class from scratch?
The example posted on CEEFIT's website is excellent and provides everything you need, but I found it a little too much. I simply wanted to write a class that inherits from COLUMNFIXTURE that looks like a regular C++ class - I did not want to mess with TABLE or DoRows(). Turned out to be very simple to do and here it is:

namespace swx_ceefit {
/** A CEEFIT class created from scratch without macros to test batch-mode processing of SWX API cases.
        \author GRI
        \date Dec 1 2008
    class TestSWX :
        inline ceefit_init_spec TestSWX();
        inline virtual ceefit_dtor_spec ~TestSWX();
         ceefit_init_spec TestSWX(const TestSWX&);
         TestSWX& ceefit_init_spec operator=(const TestSWX&);
        /** fit_var
        CEEFIT::STRING m_sldprt_name;
        /** fit_test
        double ceefit_call_spec nos_feats();
        /** where is this executable executing from?
        void ExecutingPath();
        /** Get the full path to the input file i.e. the *.sldprt file
            \author GRI
            \date Jan 8 2009
        void GetFullPath(std::wstring & wsFullPath);

CComPtr swApp;

namespace swx_ceefit
    ceefit_init_spec TestSWX::TestSWX()
        RegisterCeefitField(this, "sldprt_name", m_sldprt_name);
        RegisterCeefitTest(this, "nos_feats", &TestSWX::nos_feats);

        //cout << endl << "constructing";

    ceefit_dtor_spec TestSWX::~TestSWX() {
        //cout << endl << "destructing";

    double ceefit_call_spec TestSWX::nos_feats() {
        wstring wsFullPath;
        if( swApp ) {
            IModelDoc* swModel = NULL;
            if( swApp->put_Visible(FALSE) == S_OK ) {
                if( swApp->DocumentVisible(FALSE, swDocPART) == S_OK ) {
                    //wcout << "\nfull path in nos_feats = " << wsFullPath.c_str() << endl;
                    CComBSTR sFileName(wsFullPath.c_str());
                    CComBSTR sDefaultConfiguration(L"Default");
                    long fileerror;
                    if( swApp->IOpenDocSilent(sFileName.m_str, swDocPART, &fileerror, &swModel) == S_OK ) {
                        if( swModel != NULL ) {
                            IPartDoc *part;
                            HRESULT hres = swModel->QueryInterface(IID_IPartDoc, (LPVOID *)&part);
                            if( swModel->put_Visible(FALSE) == S_OK ) {
                                CComPtr swfeat;
                                if( part->IFirstFeature(&swfeat) == S_OK ) {
                                    CComBSTR name;
                                    wcout << endl << "1st feat name = " << name.m_str;
                                } else wcout << endl << "no first feature";
                            } else wcout << endl << "swModel still visible";
                        else {
                            wcout << endl << "swModel = NULL with fileerror = " << fileerror;
                    } else wcout << endl << "open doc not silent";
                } else wcout << endl << "document still visible";
        else wcout << endl << "ERROR: swApp = NULL" << "\tfile\t=\t" << __FILE__ << "\tline\t=\t" << __LINE__;
        VARIANT_BOOL ret;
        swApp->CloseAllDocuments(TRUE, &ret);
        return 1;

    void TestSWX::ExecutingPath() {
        wchar_t sExecPath[MAX_PATH];
        GetModuleFileName(NULL, sExecPath, MAX_PATH);
        //wcout << endl << "executable = " << sExecPath;

    void TestSWX::GetFullPath(wstring & wsFullPath) {
        wchar_t cFullPath[MAX_PATH] = TEXT("");
        LPTSTR  lpszFilePart = NULL;
        GetFullPathName(m_sldprt_name.GetBuffer(), MAX_PATH, cFullPath, & lpszFilePart);
        //wcout << endl << "full path = " << cFullPath;
        wsFullPath = cFullPath;

    static REGISTERFIXTURECLASS< TestSWX > FatTableFixtureRegistration("swx_ceefit::TestSWX", "AKA_TESTSWX"); 

If you are wondering why this class is called TestSWX (SWX = short for SolidWorks), then consider this: using the solution from a previous post of mine "Batch mode SolidWorks" we can start SolidWorks without GUI and using the above CEEFIT class, call SolidWorks API and run your integrated tests. But that my readers is possibly a separate post.

No comments:

Post a Comment