本文是对《测试驱动的嵌入式 C 语言开发》第二章的实践。
这部分参考博文《基于Windows 的 VS Code C/C++ 编译环境搭建》。
安装 CMake ,在官网下载最新安装包。安装时选择对所有用户添加 CMake 到系统 PATH。
安装以下插件:
Unity 是一个单元测试框架。目标是保持它的小而实用。Unity 测试框架的核心是三个文件:一个 C 文件和几个头文件。
这些组合起来提供功能和宏以使测试更容易。
Unity 被设计为跨平台的。
它努力坚持 C 标准,同时仍为许多违反规则的嵌入式 C 编译器提供支持。
Unity 已与许多编译器一起使用,包括 GCC、IAR、Clang、Green Hills、Microchip 和 MS Visual Studio。
可在在Github拉取或下载源代码。
TDDUnityExample 。TDDUnityExample 文件夹下再创建一个文件夹,这里命名为 Unity 。src 和 extras 两个文件夹拷贝到 Unity 文件夹下。TDDUnityExample 文件夹下再创建一个文件夹,这里命名为 tests 。sprintf_s 函数为例。sprintf_s_test.c 、sprintf_s_runner.c 和 all_test.c 三个文件。其中, sprintf_s_test.c 内容为:#include <stdio.h>
#include "unity_fixture.h"
TEST_GROUP(sprintf);
TEST_SETUP(sprintf)
{}
TEST_TEAR_DOWN(sprintf)
{}
TEST(sprintf, NoFormatOperations)
{
char output[5];
TEST_ASSERT_EQUAL(4 , sprintf_s(output,4, "hey"));
TEST_ASSERT_EQUAL_STRING("hey", output);
}
TEST(sprintf, FormatOperations)
{
char output[20];
TEST_ASSERT_EQUAL(12, sprintf_s(output, 20, "Hello %s\n", "World"));
}
sprintf_s_runner.c 内容为:
#include "unity_fixture.h"
TEST_GROUP_RUNNER(sprintf)
{
RUN_TEST_CASE(sprintf, NoFormatOperations);
RUN_TEST_CASE(sprintf, FormatOperations);
}
all_test.c 内容为:
#include "unity_fixture.h"
static void RunAllTests(void)
{
RUN_TEST_GROUP(sprintf);
}
int main(int argc, const char *argv[])
{
return UnityMain(argc, argv, RunAllTests);
}
TDDUnityExample 文件夹下创建名为 CMakeLists.txt 的文件,该文件给 CMake使用,文件内容为:# 最低CMake版本要求
cmake_minimum_required(VERSION 3.5.1)
#将src工作目录的绝对路径赋给SRC_DIR
file(GLOB_RECURSE APP_SRC_DIR "tests/*.c")
file(GLOB_RECURSE UNITY_SRC_LIST "Unity/*.c")
# 项目名称
project(TDD_test)
# 头文件路径
include_directories("tests")
include_directories("Unity/src")
include_directories("Unity/extras/fixture/src")
include_directories("Unity/extras/memory/src")
#将所有源文件生成一个可执行文件
add_executable(TDD_test ${APP_SRC_DIR} ${UNITY_SRC_LIST})
TDDCppUTestExample 文件夹。单击下图所示红框中的三角形。
GCC
CMake 根据 CMakeLists.txt 文件内容生成 Makefile 文件,然后调用指定编译器完成源代码编译,如果生成可执行文件,则执行可执行文件。 Makefile 文件以及编译生成文件会保存在 TDDUnityExample 文件夹下的 build 文件夹下,该文件夹由 VSCode 自动创建。[proc] Executing command: "C:\Program Files\CMake\bin\cmake.EXE" --no-warn-unused-cli -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_C_COMPILER:FILEPATH=C:\msys64\mingw64\bin\gcc.exe -DCMAKE_CXX_COMPILER:FILEPATH=C:\msys64\mingw64\bin\g++.exe -Sc:/Users/jssh/Desktop/TDDUnityExample -Bc:/Users/jssh/Desktop/TDDUnityExample/build -G "Unix Makefiles"
[cmake] Not searching for unused variables given on the command line.
[cmake] -- The C compiler identification is GNU 12.2.0
[cmake] -- The CXX compiler identification is GNU 12.2.0
[cmake] -- Detecting C compiler ABI info
[cmake] -- Detecting C compiler ABI info - done
[cmake] -- Check for working C compiler: C:/msys64/mingw64/bin/gcc.exe - skipped
[cmake] -- Detecting C compile features
[cmake] -- Detecting C compile features - done
[cmake] -- Detecting CXX compiler ABI info
[cmake] -- Detecting CXX compiler ABI info - done
[cmake] -- Check for working CXX compiler: C:/msys64/mingw64/bin/g++.exe - skipped
[cmake] -- Detecting CXX compile features
[cmake] -- Detecting CXX compile features - done
[cmake] -- Configuring done
[cmake] -- Generating done
[cmake] -- Build files have been written to: C:/Users/jssh/Desktop/TDDUnityExample/build
[main] Building folder: TDDUnityExample TDD_test
[build] Starting build
[proc] Executing command: "C:\Program Files\CMake\bin\cmake.EXE" --build c:/Users/jssh/Desktop/TDDUnityExample/build --config Debug --target TDD_test -j 14 --
[build] [ 14%] [32mBuilding C object CMakeFiles/TDD_test.dir/tests/sprintf_s_runner.c.obj[0m
[build] [ 42%] [32mBuilding C object CMakeFiles/TDD_test.dir/tests/sprintf_s_test.c.obj[0m
[build] [ 42%] [32mBuilding C object CMakeFiles/TDD_test.dir/tests/all_teses.c.obj[0m
[build] [ 71%] [32mBuilding C object CMakeFiles/TDD_test.dir/Unity/extras/memory/src/unity_memory.c.obj[0m
[build] [ 71%] [32mBuilding C object CMakeFiles/TDD_test.dir/Unity/extras/fixture/src/unity_fixture.c.obj[0m
[build] [ 85%] [32mBuilding C object CMakeFiles/TDD_test.dir/Unity/src/unity.c.obj[0m
[build] [100%] [32m[1mLinking C executable TDD_test.exe[0m
[build] [100%] Built target TDD_test
[build] Build finished with exit code 0
终端 界面中,可以看到程序执行结果,也就是测试输出结果。
如果你想使用测试驱动开发,又对本文举例内容疑惑,可能是因为你还没看过《测试驱动的嵌入式 C 语言开发》这本书。
不要被书名欺骗到,这绝不仅仅只是教授你测试驱动方法的,它还教授 C 程序设计模式,如何改善设计、如何写出干净利索的代码,如何封装和模块化等等。
另外,本书是这么多年来我看过的嵌入式翻译书籍中,翻译最准确最清晰的一本。一股清流,值得大书特书。
读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)

很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我有一个围绕一些对象的包装类,我想将这些对象用作散列中的键。包装对象和解包装对象应映射到相同的键。一个简单的例子是这样的:classAattr_reader:xdefinitialize(inner)@inner=innerenddefx;@inner.x;enddef==(other)@inner.x==other.xendenda=A.new(o)#oisjustanyobjectthatallowso.xb=A.new(o)h={a=>5}ph[a]#5ph[b]#nil,shouldbe5ph[o]#nil,shouldbe5我试过==、===、eq?并散列所有无济于事。
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI