美文网首页
[习题30]自动化测试

[习题30]自动化测试

作者: AkuRinbu | 来源:发表于2018-12-29 23:31 被阅读9次

使用教材

《“笨办法” 学C语言(Learn C The Hard Way)》
https://www.jianshu.com/p/b0631208a794

  • 完整源码 : learn-c-the-hard-way-lectures/ex28/

https://github.com/zedshaw/learn-c-the-hard-way-lectures/tree/master/ex28

  • 说明:ex28 文件夹里面的文件是一直用到ex30的;

一、查看文件结构(执行 make install 之前)

$ ls  *
LICENSE  Makefile  Makefile~  README.md

bin:

src:
dbg.h  libex29.c

tests:
libex29_tests.c  minunit.h  runtests.sh

二、完整源码

Makefile

  • 直接用配书代码仓库里的Makefile编译会出错的话,Ubuntu可以用下面这个:
CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LDFLAGS=$(OPTLIBS)
PREFIX?=/usr/local

SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))

TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))

TARGET=build/libex29.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))


LDLIBS=-ldl

# The Target Build
all: $(TARGET) $(SO_TARGET) tests

dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all

$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
    ar rcs $@ $(OBJECTS)
    ranlib $@

$(SO_TARGET): $(TARGET) $(OBJECTS)
    $(CC) -shared -o $@ $(OBJECTS)

build:
    @mkdir -p build
    @mkdir -p bin


# The Unit Tests
.PHONY: tests
tests: LDLIBS += $(TARGET)
tests: $(TESTS)
    sh ./tests/runtests.sh

valgrind:
    VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)

# The Cleaner
clean:
    rm -rf build $(OBJECTS) $(TESTS)
    rm -f tests/tests.log 
    find . -name "*.gc*" -exec rm {} \;
    rm -rf `find . -name "*.dSYM" -print`

# The Install
install: all
    install -d $(DESTDIR)/$(PREFIX)/lib/
    install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/

# The Checker
check:
    @echo Files with potentially dangerous functions.
    @egrep '[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true

/tests/libex29_tests.c

  • 代码修改,修改 lib_file 变量的值;
代码仓库下载下来的 libex29_tests.c 文件里
char *lib_file = "build/libYOUR_LIBRARY.so";
要修改成
char *lib_file = "build/libex29.so";

三、执行 make install 操作

命令行操作

$ make install
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o 
  src/libex29.o src/libex29.c

ar rcs build/libex29.a src/libex29.o
ranlib build/libex29.a
cc -shared -o build/libex29.so src/libex29.o

cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG   
  tests/libex29_tests.c  -ldl build/libex29.a -o tests/libex29_tests

sh ./tests/runtests.sh
Running unit tests:
----
RUNNING: ./tests/libex29_tests
A STRING: Hello
HELLO
hello
ALL TESTS PASSED
Tests run: 4
tests/libex29_tests PASS

对输出信息的说明

  • 生成 .o的命令是

cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG -fPIC -c -o src/libex29.o src/libex29.c

  • 如果要查看.o里面有什么函数,可以使用nm命令
$ nm ./src/libex29.o
                 U __ctype_tolower_loc
                 U __ctype_toupper_loc
00000000000000d0 T fail_on_purpose
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 r .LC0
0000000000000080 T lowercase
0000000000000000 T print_a_message
                 U __printf_chk
                 U putchar
0000000000000030 T uppercase
  • 生成 .so的命令是

cc -shared -o build/libex29.so src/libex29.o

  • 生成loder program 的命令是

cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG tests/libex29_tests.c -ldl build/libex29.a -o tests/libex29_tests

  • 跑单元测试用例的命令是

sh ./tests/runtests.sh

四、再次查看,文件结构(执行 make install 之后)

$ ls *
LICENSE  Makefile  Makefile~  README.md

bin:

build:
libex29.a  libex29.so

src:
dbg.h  libex29.c  libex29.o

tests:
libex29_tests  libex29_tests.c  minunit.h  runtests.sh  tests.log
  • 可以看到生成了一个新的文件夹build等等;

五、单元测试

1、 单元测试的代码就是写在 /tests/libex29_tests.c 里面的

/*****        /tests/libex29_tests.c       *******/
char *test_functions()
{
    mu_assert(check_function("print_a_message", "Hello", 0),
            "print_a_message failed.");
    mu_assert(check_function("uppercase", "Hello", 0),
            "uppercase failed.");
    mu_assert(check_function("lowercase", "Hello", 0),
            "lowercase failed.");

    return NULL;
}

2、可以和手工进行测试的过程进行对比

[手工过程] https://www.jianshu.com/p/ced4a8bed720

3、 单元测试代码 xxxx_tests.c文件一贯的结构

  • 函数名会变,每一个函数XXXX,都是先有一个 char *test_XXXX(),后面再用mu_run_test(test_XXXX);的结构;
#include "minunit.h"

char *test_dlopen()
{
     return NULL;
}

char *test_functions()
{
    return NULL;
}

char *test_failures()
{
    return NULL;
}

char *test_dlclose()
{
    return NULL;
}

char *all_tests()
{
    mu_suite_start();

    mu_run_test(test_dlopen);
    mu_run_test(test_functions);
    mu_run_test(test_failures);
    mu_run_test(test_dlclose);

    return NULL;
}

RUN_TESTS(all_tests);

4、 单元测试的框架

  • 本质就是一堆宏替换
  • 文件路径 : learn-c-the-hard-way-lectures/ex28/c-skeleton/tests/minunit.h
#undef NDEBUG
#ifndef _minunit_h
#define _minunit_h

#include <stdio.h>
#include <dbg.h>
#include <stdlib.h>

#define mu_suite_start() char *message = NULL

#define mu_assert(test, message) if (!(test)) {\
    log_err(message); return message; }
#define mu_run_test(test) debug("\n-----%s", " " #test); \
    message = test(); tests_run++; if (message) return message;

#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
    argc = 1; \
    debug("----- RUNNING: %s", argv[0]);\
    printf("----\nRUNNING: %s\n", argv[0]);\
    char *result = name();\
    if (result != 0) {\
        printf("FAILED: %s\n", result);\
    }\
    else {\
        printf("ALL TESTS PASSED\n");\
    }\
    printf("Tests run: %d\n", tests_run);\
    exit(result != 0);\
}

int tests_run;

#endif
  • mu_run_test(test_dlopen);举例
(位于 minunit.h) 首先是宏替换的定义 :

#define mu_run_test(test) debug("\n-----%s", " " #test); \
    message = test(); tests_run++; if (message) return message;

经过替换之后,会变成:
debug("\n-----%s", " " #test_dlopen); 
message = test_dlopen(); 
if (message) return message;

debug 是配书自带的调试库里的函数,就是一个输出函数;
重点是这一句 message = test_dlopen();  
这是什么?这可是调用函数 test_dlopen()啊!
并且将函数的结果返回给 message!

这就是为什么明明 mu_run_test(test_dlopen) 里面只是写了函数名,
不带()括号,却也能使得测试函数被调用, 因为在宏替换里面加上了();

5、自动测试,运行一个脚本文件 runtests.sh

  • learn-c-the-hard-way-lectures/ex28/c-skeleton/tests/runtests.sh
echo "Running unit tests:"

for i in tests/*_tests
do
    if test -f $i
    then
        if $VALGRIND ./$i 2>> tests/tests.log
        then
            echo $i PASS
        else
            echo "ERROR in test $i: here's tests/tests.log"
            echo "------"
            tail tests/tests.log
            exit 1
        fi
    fi
done

echo ""

相关文章

网友评论

      本文标题:[习题30]自动化测试

      本文链接:https://www.haomeiwen.com/subject/prjmlqtx.html