在我所涉及的嵌入式编程类型中,运行代码的确定性和透明性受到高度重视.我透露的意思是,例如,能够查看内存的任意部分并知道存储的变量.因此,正如我确信嵌入式程序员所期望的那样,如果可能的话,应该避免使用new,如果无法避免,则仅限于初始化.
我理解这种需要,但不同意我的同事这样做的方式,也不知道更好的选择.
我们拥有几个全局结构数组和一些全局类.有一个用于互斥锁的结构数组,一个用于信号量,一个用于消息队列(这些是在main中初始化的).对于每个运行的线程,拥有它的类是全局变量.
我遇到的最大问题是单元测试.当我想测试的类#includes全局变量时,我如何插入模拟对象?
这是伪代码的情况:
foo.h中
- #include "Task.h"
- class Foo : Task {
- public:
- Foo(int n);
- ~Foo();
- doStuff();
- private:
- // copy and assignment operators here
- }
bar.h
- #include <pthread.h>
- #include "Task.h"
- enum threadIndex { THREAD1 THREAD2 NUM_THREADS };
- struct tThreadConfig {
- char *name,Task *taskptr,pthread_t threadId,...
- };
- void startTasks();
bar.cpp
- #include "Foo.h"
- Foo foo1(42);
- Foo foo2(1337);
- Task task(7331);
- tThreadConfig threadConfig[NUM_THREADS] = {
- { "Foo 1",&foo1,... },{ "Foo 2",&foo2,{ "Task",&task,... }
- };
- void FSW_taskStart() {
- for (int i = 0; i < NUMBER_OF_TASKS; i++) {
- threadConfig[i].taskptr->createThread( );
- }
- }
如果我想要更多或更少的任务怎么办? foo1的构造函数中有一组不同的参数?我想我必须有一个单独的bar.h和bar.cpp,这似乎比必要的工作要多得多.
解决方法
如果你想先对这些代码进行单元测试,我建议你阅读
Working Effectively With Legacy Code另见
this.
基本上使用链接器插入模拟/伪对象和函数应该是最后的手段,但仍然是完全有效的.
但是,您也可以使用控制反转,如果没有框架,这可能会对客户端代码产生一些责任.但它确实有助于测试.例如,测试FSW_taskStart()
- tThreadConfig threadConfig[NUM_THREADS] = {
- { "Foo 1",%foo1,%foo2,%task,... }
- };
- void FSW_taskStart(tThreadConfig configs[],size_t len) {
- for (int i = 0; i < len; i++) {
- configs[i].taskptr->createThread( );
- }
- }
- void FSW_taskStart() {
- FSW_taskStart(tThreadConfig,NUM_THREADS);
- }
- void testFSW_taskStart() {
- MockTask foo1,foo2,foo3;
- tThreadConfig mocks[3] = {
- { "Foo 1",&foo3,... }
- };
- FSW_taskStart(mocks,3);
- assert(foo1.started);
- assert(foo2.started);
- assert(foo3.started);
- }
现在你可以将你的线程的模拟版本传递给’FSW_taskStart’,以确保该函数确实根据需要启动线程.不幸的是,您必须依赖原始FSW_taskStart传递正确参数的事实,但您现在正在测试更多代码.