During interface testing, in addition to the common http interface, there is another common one, socket interface. Today, I will explain how to test socket interface with Python's own socket library.
1, socket interface
Socket, also known as socket, can be understood as the address of an application and the key to network communication. We can find a host through IP and an application on the host through the port of the host.
In this way, the communication between two applications can be carried out through socket. The specific implementation is to implement a server that has been listening at one end, and send a request to it at the other end and obtain a response. The server processes different requests and returns them. This is the socket interface.
Let's implement a socket interface and test it.
2, Implement a socket server interface
Create a mock folder under test and create a mock folder in it_ socket_ Server.py file:
# -*- coding: utf-8 -*- """ @author: lucas @Function: @file: mock_socket_server.py @time: 2021/9/24 11:26 morning """ """ socket server of mock. Two interfaces, add and sub receive: { "action": "add", "params": {"a": 1, "b": 2} } return: { "action": "add", "result": 3 } """ import socket import json def add(a, b): return a + b ip_port = ('127.0.0.1', 8080) server = socket.socket() server.bind(ip_port) server.listen(5) while True: conn, addr = server.accept() data = conn.recv(1024) try: ddata = json.loads(data) action = ddata.get('action') params = ddata.get('params') if action == 'add': res = add(**params) conn.send(b'{"action": "add", "result": %d}' % res) else: conn.send(b'{"code":-2, "message":"Wrong Action"}') except (AttributeError, ValueError): conn.send(b'{"code":-1, "message":"Data Error"}') finally: conn.close()
We implemented a simple socket server with an interface add. The interface document developed for you may be as follows:
Interface type: socket
Interface address: 127.0.0.1
Port: 8080
Interface name: addition
action name: add
Input:
name | type | Is it necessary |
---|---|---|
a | int | yes |
b | int | yes |
Output reference:
name | type | meaning |
---|---|---|
result | int | result |
Input example:
{ "action": "add", "params": {"a": 1, "b": 2} }
Reference example:
{ "action": "add", "result": 3 }
error code:
code | message |
---|---|
-1 | Data Error |
-2 | Wrong Action |
After getting the interface document, how should we test it?
3. Test socket interface
First, we need a general client class to encapsulate the general methods of socket interface testing, so as to avoid having to write a pile every time. Add TCPClient in client.py
class TCPClient(object): """For testing socket request""" def __init__(self, domain, port, timeout=30, max_receive=102400): self.domain = domain self.port = port self.connected = 1 # Set to 1 after connection self.max_receive = max_receive self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.settimeout(timeout) def connect(self): """Connection assignment IP,port""" if not self.connected: try: self._sock.connect((self.domain, self.port)) except socket.error as e: logger.exception(e) else: self.connected = 1 logger.debug('TCPClient connect to {0}:{1} success.'.format(self.domain, self.port)) def send(self, data, dtype='str', suffix=''): """Send to server send_stringļ¼And return information. If an error is reported, return None""" if dtype == 'json': send_string = json.dumps(data) + suffix else: send_string = data + suffix self.connect() if self.connected: try: self._sock.send(send_string.encode()) logger.debug('TCPClient Send {0}'.format(send_string)) except socket.error as e: logger.exception(e) try: rec = self._sock.recv(self.max_receive).decode() if suffix: rec = rec[:-len(suffix)] logger.debug('TCPClient received {0}'.format(rec)) return rec except socket.error as e: logger.exception(e) def close(self): """Close connection""" if self.connected: self._sock.close() logger.debug('TCPClient closed.')
Then add the basic configuration of socket interface in config.yml:
socket: ip: 127.0.0.1 port: 8080
Then create our test in the interface_ socket.py:
# -*- coding: utf-8 -*- """ @author: lucas @Function: @file: test_socket.py @time: 2021/9/24 11:37 morning """ from utils.client import TCPClient import unittest from utils.config import Config from utils.extractor import JMESPathExtractor je = JMESPathExtractor() class TestAdd(unittest.TestCase): def setUp(self): socket=Config().get('socket') ip = socket.get('ip') port = socket.get('port') self.client = TCPClient(ip, port) def tearDown(self): self.client.close() def test_add(self): data = { 'action': 'add', 'params': {'a': 1, 'b': 2} } res = self.client.send(data, dtype='json') self.assertEqual(je.extract('result', res), 3) self.assertEqual(je.extract('action', res), 'add') def test_wrong_action(self): data = { 'action': 'sub', 'params': {'a': 1, 'b': 2} } res = self.client.send(data, dtype='json') self.assertEqual(je.extract('code', res), -2) self.assertEqual(je.extract('message', res), 'Wrong Action') def test_wrong_data(self): data = 'xxxxx' res = self.client.send(data) self.assertEqual(je.extract('code', res), -1) self.assertEqual(je.extract('message', res), 'Data Error') if __name__ == '__main__': unittest.main(verbosity=2)
After the simple test case is completed, execute it (remember to run mock_socket_server.py first):
{"action": "add", "params": {"a": 1, "b": 2}} b'{"action": "add", "params": {"a": 1, "b": 2}}' {"action": "sub", "params": {"a": 1, "b": 2}} b'{"action": "sub", "params": {"a": 1, "b": 2}}' xxxxx b'xxxxx' Ran 3 tests in 0.030s OK
Of course, the interface can't be so simple, and the use cases can't be just these. Here's just a chestnut that can be applied. No matter how complex the socket interface is, it looks the same.
Here, we use our own mock server. When the real interface is developed, we can directly execute the test by changing the ip and port in config.yml.