diff --git a/test/api_test_wifi_setters.c b/test/api_test_wifi_setters.c
index 28df6ce22fa4cc01c15e046e65b0532e0dc1c99d..5ccfae30b8565386fd230a257f0f0fe8c29b05fb 100644
--- a/test/api_test_wifi_setters.c
+++ b/test/api_test_wifi_setters.c
@@ -15,13 +15,7 @@
 #include <wifi.h>
 
 #include "wifimngr.h"
-
-#define FIVE_IFACE "test5"
-#define TWO_IFACE "test2"
-
-#define FIVE_CLIENT "50:31:32:33:34:35"
-
-#define PIN "24033848"
+#include "test_utils.h"
 
 struct test_ctx {
 	struct blob_buf bb;
@@ -101,69 +95,6 @@ int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req,
 	return 0;
 }
 
-char *multi_tok(char *input, char *delimiter) {
-    static char *string;
-	char *end, temp;
-
-    if (input)
-        string = input;
-
-    if (!string)
-        return string;
-
-    end = strstr(string, delimiter);
-
-    if (!end) {
-        char *temp = string;
-
-        string = NULL;
-        return temp;
-    }
-
-    temp = string;
-
-    *end = '\0';
-    string = end + strlen(delimiter);
-
-    return temp;
-}
-
-/* TODO: how to fix events without fopen and fclose every poll? */
-struct json_object *poll_test_log(struct test_ctx *ctx, const char *prefix)
-{
-	char line[256] = {0};
-	char msg[256] = {0};
-	struct json_object *obj = NULL;
-
-	//if (!ctx->fp) {
-		ctx->fp = fopen("/tmp/test.log", "r");
-		if (!ctx->fp) {
-			return NULL;
-		}
-	//}
-
-	while (fgets(line, 256, ctx->fp))  {
-		char *ptr, *s;
-		char appended[32];
-
-		snprintf(appended, sizeof(appended), "%s:", prefix);
-
-		ptr = strstr(line, appended);
-		if (!ptr)
-			continue;
-
-		s = multi_tok(ptr, appended);
-		s = multi_tok(NULL, appended);
-		strncpy(msg, s, sizeof(msg));
-	}
-
-	if (strlen(msg))
-		obj = json_tokener_parse(msg);
-
-	fclose(ctx->fp);
-	return obj;
-}
-
 static void test_api_radio_disconnect(void **state)
 {
 	struct test_ctx *ctx = (struct test_ctx *) *state;
@@ -175,7 +106,7 @@ static void test_api_radio_disconnect(void **state)
 	blobmsg_add_string(bb, "sta", FIVE_CLIENT);
 
 	sta_disconnect(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -204,7 +135,7 @@ static void test_api_req_bss_transition(void **state)
 	blobmsg_close_array(bb, arr);
 
 	nbr_transition(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -232,7 +163,7 @@ static void test_api_req_beacon_report(void **state)
 	blobmsg_add_string(bb, "ssid", "Test SSID 5Ghz");
 
 	nbr_request(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	/* TODO: not implemented? */
@@ -261,7 +192,7 @@ static void test_api_add_nbr(void **state)
 	blobmsg_add_u32(bb, "phy", 5);
 
 	nbr_add(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -300,7 +231,7 @@ static void test_api_wps_start(void **state)
 	blobmsg_add_string(bb, "role", "registrar");
 	blobmsg_add_string(bb, "pin", PIN);
 	wps_start(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -322,7 +253,7 @@ static void test_api_wps_start(void **state)
 	blobmsg_add_string(bb, "vif", "test2");
 	blobmsg_add_string(bb, "role", "enrollee");
 	wps_start(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -339,7 +270,7 @@ static void test_api_wps_start(void **state)
 
 	json_object_put(jobj);
 
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -364,7 +295,7 @@ static void test_api_wps_stop(void **state)
 	const char *prefix = "stop_wps";
 
 	wps_stop(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -386,7 +317,7 @@ static void test_api_add_vendor_ie(void **state)
 	blobmsg_add_string(bb, "oui", "112233");
 	blobmsg_add_string(bb, "data", "efa567");
 	vsie_add(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -415,7 +346,7 @@ static void test_api_del_vendor_ie(void **state)
 	blobmsg_add_string(bb, "oui", "112233");
 	blobmsg_add_string(bb, "data", "efa567");
 	vsie_del(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -441,7 +372,7 @@ static void test_api_del_neighbor(void **state)
 
 	blobmsg_add_string(bb, "bssid", "50:10:00:11:22:33");
 	nbr_del(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -464,7 +395,7 @@ static void test_api_monitor_sta(void **state)
 
 	blobmsg_add_string(bb, "sta", "00:00:00:11:22:33");
 	sta_monitor(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -489,7 +420,7 @@ static void test_api_scan(void **state)
 	blobmsg_add_string(bb, "ssid", "test");
 	blobmsg_add_u32(bb, "channel", 36);
 	wl_scan(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -523,7 +454,7 @@ static void test_set_wps_ap_pin(void **state)
 	blobmsg_add_string(bb, "ifname", FIVE_IFACE);
 	blobmsg_add_string(bb, "pin", PIN);
 	wps_set_ap_pin(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -565,7 +496,7 @@ static void test_api_acs(void **state)
 
 	blobmsg_add_string(bb, "ifname", FIVE_IFACE);
 	wl_autochannel(NULL, obj, NULL, NULL, bb->head);
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -596,7 +527,7 @@ static void test_api_events(void **state)
 
 	wifimngr_event_main("/opt/work/test/files/etc/wifi.json");
 
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -612,7 +543,7 @@ static void test_api_events(void **state)
 
 	prefix = "recv_event";
 
-	jobj = poll_test_log(ctx, prefix);
+	jobj = poll_test_log(ctx->fp, prefix);
 	assert_non_null(jobj);
 
 	tmp = json_object_get_by_string(jobj, "ifname");
@@ -660,7 +591,6 @@ int main(void)
 		cmocka_unit_test_setup(test_api_scan, setup),
 		cmocka_unit_test_setup(test_set_wps_ap_pin, setup),
 		cmocka_unit_test_setup(test_api_acs, setup),
-		cmocka_unit_test_setup(test_api_events, setup),
 	};
 
 	return cmocka_run_group_tests(tests, group_setup, group_teardown);
diff --git a/test/functional_test_wifi_event.c b/test/functional_test_wifi_event.c
new file mode 100644
index 0000000000000000000000000000000000000000..8d97c74dd939d51ede57ff8a90ef863d6e468765
--- /dev/null
+++ b/test/functional_test_wifi_event.c
@@ -0,0 +1,113 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <libubus.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/blobmsg.h>
+
+#include <json-validator.h>
+#include <json-c/json.h>
+#include <json-editor.h>
+
+#include <json-c/json_tokener.h>
+#include <wifi.h>
+
+#include "wifimngr.h"
+#include "test_utils.h"
+
+struct test_ctx {
+	FILE *fp;
+};
+
+int wifimngr_event_main(const char *evmap_file);
+
+int wifimngr_recv_event(const char *ifname, void *handle)
+{
+	int err;
+
+	for (;;) {
+		err = wifi_recv_event((char *)ifname, handle);
+		if (err < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static void test_api_events(void **state)
+{
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+	struct json_object *jobj, *tmp;
+	const char *prefix = "register_event";
+
+	wifimngr_event_main("/opt/work/test/files/etc/wifi.json");
+
+	jobj = poll_test_log(ctx->fp, prefix);
+	assert_non_null(jobj);
+
+	tmp = json_object_get_by_string(jobj, "ifname");
+	assert_string_equal(json_object_get_string(tmp), FIVE_IFACE);
+
+	tmp = json_object_get_by_string(jobj, "family");
+	assert_string_equal(json_object_get_string(tmp), "easysoc");
+
+	tmp = json_object_get_by_string(jobj, "group");
+	assert_string_equal(json_object_get_string(tmp), "notify");
+
+	json_object_put(jobj);
+
+	prefix = "recv_event";
+
+	jobj = poll_test_log(ctx->fp, prefix);
+	assert_non_null(jobj);
+
+	tmp = json_object_get_by_string(jobj, "ifname");
+	assert_string_equal(json_object_get_string(tmp), FIVE_IFACE);
+
+	json_object_put(jobj);
+}
+
+static int group_setup(void **state)
+{
+	struct test_ctx *ctx = calloc(1, sizeof(struct test_ctx));
+
+	if (!ctx)
+		return -1;
+
+	remove("/tmp/test.log");
+
+	*state = ctx;
+	return 0;
+}
+
+static int group_teardown(void **state)
+{
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+
+	free(ctx);
+	remove("/tmp/test.log");
+
+	/* TODO: fix event poll file */
+	//if (ctx->fp)
+		//fclose(ctx->fp);
+	return 0;
+}
+
+static int setup(void **state)
+{
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+
+	return 0;
+}
+
+int main(void)
+{
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test_setup(test_api_events, setup),
+	};
+
+	return cmocka_run_group_tests(tests, group_setup, group_teardown);
+}
diff --git a/test/test_utils.c b/test/test_utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..92922ac303f39b81e39df119b60ee3db97cc3ad3
--- /dev/null
+++ b/test/test_utils.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <json-c/json.h>
+
+#include "test_utils.h"
+
+char *multi_tok(char *input, char *delimiter)
+{
+    static char *string;
+	char *end, temp;
+
+    if (input)
+        string = input;
+
+    if (!string)
+        return string;
+
+    end = strstr(string, delimiter);
+
+    if (!end) {
+        char *temp = string;
+
+        string = NULL;
+        return temp;
+    }
+
+    temp = string;
+
+    *end = '\0';
+    string = end + strlen(delimiter);
+
+    return temp;
+}
+
+/* TODO: how to fix events without fopen and fclose every poll? */
+struct json_object *poll_test_log(FILE *fp, const char *prefix)
+{
+	char line[256] = {0};
+	char msg[256] = {0};
+	struct json_object *obj = NULL;
+
+	//if (!fp) {
+    fp = fopen("/tmp/test.log", "r");
+    if (!fp)
+        return NULL;
+	//}
+
+	while (fgets(line, 256, fp))  {
+		char *ptr, *s;
+		char appended[32];
+
+		snprintf(appended, sizeof(appended), "%s:", prefix);
+
+		ptr = strstr(line, appended);
+		if (!ptr)
+			continue;
+
+		s = multi_tok(ptr, appended);
+		s = multi_tok(NULL, appended);
+		strncpy(msg, s, sizeof(msg));
+	}
+
+	if (strlen(msg))
+		obj = json_tokener_parse(msg);
+
+	fclose(fp);
+	return obj;
+}
\ No newline at end of file
diff --git a/test/test_utils.h b/test/test_utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..22f4a54bde294e297d2ffc20761c6fcff40a08ce
--- /dev/null
+++ b/test/test_utils.h
@@ -0,0 +1,13 @@
+#ifndef TEST_UTILS_H
+#define TEST_UTILS_H
+
+#define FIVE_IFACE "test5"
+#define TWO_IFACE "test2"
+
+#define FIVE_CLIENT "50:31:32:33:34:35"
+#define PIN "24033848"
+
+char *multi_tok(char *input, char *delimiter);
+struct json_object *poll_test_log(FILE *fp, const char *prefix);
+
+#endif
\ No newline at end of file