Как создать и запустить TCP-сервер через UnitTesting с помощью [ClassInitialize]?

113
3

То, что я делаю, это TCP-клиент-сервер, выполняемый в виде программы расчета. "Клиент" вводит математическую команду и два числа, а сервер выполняет расчет для него и возвращает результат (exp. Добавить 5 5; Результат - 10). Для этого необходимо запустить и запустить сервер, а затем клиент должен попытаться подключиться. Сервер и клиент - это два разных консольных приложения.

Теперь, чтобы протестировать программу, я перехожу клиентский код в тестовый проект и запускаю сервер из его обычного проекта. Это работает и проходит тесты:

 [TestClass]
public class UnitTest1
{
static TcpClient connectionSocket = new TcpClient("localhost", 5678);
static Stream ns = connectionSocket.GetStream();
static StreamReader sr = new StreamReader(ns);
static StreamWriter sw = new StreamWriter(ns);
[TestMethod]
public void TestMethodAdd()
{
int response = GetResponce("Add 5 5");
Assert.AreEqual(response, 10);
}

[TestMethod]
public void TestMethodSubstract()
{
int response = GetResponce("Substract 5 5");
Assert.AreEqual(response, 0);
}

[TestMethod]
public void TestMethodMultiply()
{
int response = GetResponce("Multiply 5 5");
Assert.AreEqual(response, 25);
}

[TestMethod]
public void TestMethodDivide()
{
int response = GetResponce("Divide 5 5");
Assert.AreEqual(response, 1);
}

[TestMethod]
public void TestMethodPercent()
{
int response = GetResponce("Percent 100 5");
Assert.AreEqual(response, 5);
KillConnection();
}

private static void KillConnection()
{
connectionSocket.Close();
ns.Close();
}

private static int GetResponce(string request)
{
sw.AutoFlush = true;
sw.WriteLine(request);
string message = sr.ReadLine();
return Convert.ToInt32(message);
}
}

Следующий шаг, который я должен выполнить, заключается в том, чтобы сделать единичный тест самодостаточным, путем его кодирования для создания, запуска и остановки сервера самостоятельно через [ClassInitialize] (или каким другим способом холодный я использую?).

Как мне это сделать?

ЭТО НЕ РАБОТАЕТ. Моя попытка состояла в том, чтобы переместить код сервера в тестовый класс, а затем запустить метод, запускающий его с помощью [ClassInitialize]:

        class TcpChatServerA
{
public void StartServer()
{
TcpListener listener = new TcpListener(5678);
//Console.WriteLine("Server is started.");
listener.Start();
TcpClient connectionSocket = listener.AcceptTcpClient();
ChatService service = new ChatService(connectionSocket);
service.ServiceHandler();
listener.Stop();
}
}

class ChatService
{
private TcpClient connectionSocket;
public ChatService(TcpClient connectonSocketEntry)
{
connectionSocket = connectonSocketEntry;
}
public void ServiceHandler()
{

//Console.WriteLine("Server is activated");
Stream ns = connectionSocket.GetStream();
StreamReader sr = new StreamReader(ns);
StreamWriter sw = new StreamWriter(ns);
sw.AutoFlush = true;
string message = sr.ReadLine();
char[] deliminerChars = { ' ', ',', '.', '/', ':', ';' };
string[] equationMembers = new string[5];
int result = 0;
while (message != null)
{
equationMembers = message.Split(deliminerChars);
switch (equationMembers[0])
{
case "Add":
message = Convert.ToString(Convert.ToInt32(equationMembers[1]) + Convert.ToInt32(equationMembers[2]));
break;
case "Substract":
message = Convert.ToString(Convert.ToInt32(equationMembers[1]) - Convert.ToInt32(equationMembers[2]));
break;
case "Multiply":
message = Convert.ToString(Convert.ToInt32(equationMembers[1]) * Convert.ToInt32(equationMembers[2]));
break;
case "Divide":
if (equationMembers[1] == "0")
{
message = "You attempt to divide by zero please try again";
}
else if (equationMembers[2] == "0")
{
message = "You attempt to divide by zero please try again";
}
else
{
message = Convert.ToString(Convert.ToDecimal(equationMembers[1]) / Convert.ToDecimal(equationMembers[2]));
;
}
break;
case "Percent":
message = Convert.ToString(((Convert.ToDecimal(equationMembers[2])) / 100) * Convert.ToDecimal(equationMembers[1]));
break;
default:
Console.WriteLine("You did not enter a valid equation command. Valid commands are " +
"'Add', 'Substract', 'Multiply', 'Divide', Percent (y% (from) x)");
break;
}
//Console.WriteLine("Calculating...");
sw.WriteLine(message);
message = sr.ReadLine();
}
ns.Close();
connectionSocket.Close();
}
}

И запуск сервера:

[TestClass]
public class UnitTest1
{
[ClassInitialize]
static void Main(string[] args)
{
TcpChatServerA server = new TcpChatServerA();
server.StartServer();
}

//the rest is the test class as seen in the first code block

//static TcpClient connectionSocket = new TcpClient("localhost", 5678);
//static Stream ns = connectionSocket.GetStream();
//static StreamReader sr = new StreamReader(ns);
//static StreamWriter sw = new StreamWriter(ns);

//[TestMethod]
//public void TestMethodAdd()
//{
//int response = GetResponce("Add 5 5");
//Assert.AreEqual(response, 10);
//}
//...

Таким образом, весь код будет:

using System;
using System.IO;
using System.Net.Sockets;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MathTCPServerTest
{
class TcpChatServerA
{
public void StartServer()
{
TcpListener listener = new TcpListener(5678);
//Console.WriteLine("Server is started.");
listener.Start();
TcpClient connectionSocket = listener.AcceptTcpClient();
ChatService service = new ChatService(connectionSocket);
service.ServiceHandler();
listener.Stop();
}
}

class ChatService
{
private TcpClient connectionSocket;
public ChatService(TcpClient connectonSocketEntry)
{
connectionSocket = connectonSocketEntry;
}
public void ServiceHandler()
{

//Console.WriteLine("Server is activated");
Stream ns = connectionSocket.GetStream();
StreamReader sr = new StreamReader(ns);
StreamWriter sw = new StreamWriter(ns);
sw.AutoFlush = true;
string message = sr.ReadLine();
char[] deliminerChars = { ' ', ',', '.', '/', ':', ';' };
string[] equationMembers = new string[5];
int result = 0;
while (message != null)
{
equationMembers = message.Split(deliminerChars);
switch (equationMembers[0])
{
case "Add":
message = Convert.ToString(Convert.ToInt32(equationMembers[1]) + Convert.ToInt32(equationMembers[2]));
break;
case "Substract":
message = Convert.ToString(Convert.ToInt32(equationMembers[1]) - Convert.ToInt32(equationMembers[2]));
break;
case "Multiply":
message = Convert.ToString(Convert.ToInt32(equationMembers[1]) * Convert.ToInt32(equationMembers[2]));
break;
case "Divide":
if (equationMembers[1] == "0")
{
message = "You attempt to divide by zero please try again";
}
else if (equationMembers[2] == "0")
{
message = "You attempt to divide by zero please try again";
}
else
{
message = Convert.ToString(Convert.ToDecimal(equationMembers[1]) / Convert.ToDecimal(equationMembers[2]));
;
}
break;
case "Percent":
message = Convert.ToString(((Convert.ToDecimal(equationMembers[2])) / 100) * Convert.ToDecimal(equationMembers[1]));
break;
default:
Console.WriteLine("You did not enter a valid equation command. Valid commands are " +
"'Add', 'Substract', 'Multiply', 'Divide', Percent (y% (from) x)");
break;
}
//Console.WriteLine("Calculating...");
sw.WriteLine(message);
message = sr.ReadLine();
}
ns.Close();
connectionSocket.Close();
}
}

[TestClass]
public class UnitTest1
{
[ClassInitialize]
static void Main(string[] args)
{
TcpChatServerA server = new TcpChatServerA();
server.StartServer();
}

static TcpClient connectionSocket = new TcpClient("localhost", 5678);
static Stream ns = connectionSocket.GetStream();
static StreamReader sr = new StreamReader(ns);
static StreamWriter sw = new StreamWriter(ns);

[TestMethod]
public void TestMethodAdd()
{
int response = GetResponce("Add 5 5");
Assert.AreEqual(response, 10);
}

[TestMethod]
public void TestMethodSubstract()
{
int response = GetResponce("Substract 5 5");
Assert.AreEqual(response, 0);
}

[TestMethod]
public void TestMethodMultiply()
{
int response = GetResponce("Multiply 5 5");
Assert.AreEqual(response, 25);
}

[TestMethod]
public void TestMethodDivide()
{
int response = GetResponce("Divide 5 5");
Assert.AreEqual(response, 1);
}

[TestMethod]
public void TestMethodPercent()
{
int response = GetResponce("Percent 100 5");
Assert.AreEqual(response, 5);
KillConnection();
}

private static void KillConnection()
{
connectionSocket.Close();
ns.Close();
}

private static int GetResponce(string request)
{
sw.AutoFlush = true;
sw.WriteLine(request);
string message = sr.ReadLine();
return Convert.ToInt32(message);
}
}
}

Это дает мне ошибку:

Имя теста: TestMethodPercent
Test FullName: MathTCPServerTest.UnitTest1.TestMethodPercent
Источник теста: Мои каталоги \MathTCPServerTest\UnitTest1.cs: строка 132
Результат теста: сбой
Продолжительность теста: 0:00:00

Результат StackTrace:
в System.Net.Sockets.TcpClient..ctor (имя узла String, порт Int32)
в MathTCPServerTest.UnitTest1..cctor() в моих каталогах \MathTCPServerTest\UnitTest1.cs: строка 97
--- Конец внутренней проверки стека исключений ---
at MathTCPServerTest.UnitTest1..ctor() Сообщение результата: Не удалось создать экземпляр класса MathTCPServerTest.UnitTest1. Ошибка: System.TypeInitializationException: инициализатор типа для "MathTCPServerTest.UnitTest1" сделал исключение. ---> System.Net.Sockets.SocketException: соединение не может быть выполнено, потому что целевой компьютер активно отказался от него 127.0.0.1:5678.

Я не понимаю [ClassInitialize] правильно, не так ли?

PS: Я открыт для предложений как для улучшения кода, так и для того, чтобы лучше представить свои проблемы и quesitons в stackoverflow.

спросил(а) 2020-04-04T02:13:04+03:00 6 месяцев, 3 недели назад
1
Решение
99

Как создавать и запускать TCP-сервер для каждого модульного теста

Следующий шаг, который я должен выполнить, заключается в том, чтобы сделать единичный тест самодостаточным, путем его кодирования для создания, запуска и остановки сервера самостоятельно через [ClassInitialize] (или каким другим способом холодный я использую?).

Как мне это сделать?

Поскольку вы хотите начать и остановить на единичный тест и иметь несколько из них в одном [TestClass], [ClassInitialize] здесь бесполезен. Один из способов сделать это - обернуть среду тестирования в класс, реализующий IDisposable а затем использовать его в блоке using в модульном тесте. Следующий код делает это и отвечает на вопрос о том, как создавать и запускать и удалять TCP-сервер для каждого модульного теста:

using System;
using System.Net.Sockets;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Net;

namespace TCPServerTest
{
class TestEnvironment : IDisposable
{
TcpListener TcpServer;

readonly public int TcpServerAddress;

public TestEnvironment(int TcpServerAddress = 5678)
{
this.TcpServerAddress = TcpServerAddress;

TcpServer = new TcpListener(IPAddress.Loopback, TcpServerAddress);

TcpServer.Start();
}

public void Dispose()
{
TcpServer.Stop();
}
}

[TestClass]
public class TestContainer
{
[TestMethod]
public void TestUsingTcpServer()
{
using (var TestEnvironment = new TestEnvironment())
{
var TcpClient = new TcpClient();

TcpClient.Connect(IPAddress.Loopback, TestEnvironment.TcpServerAddress);

Assert.IsTrue(TcpClient.Connected, "TcpClient.Connected");
}
}
}
}

в [ClassInitialize]

Я не понимаю [ClassInitialize] правильно, не так ли?

Это кажется вероятным с учетом кода, который вы опубликовали. Это то, что в [ClassInitialize] говорится о методе, который имеет [ClassInitialize]:

Метод должен быть статическим, общедоступным, не возвращать значение и должен принимать один параметр типа TestContext.

Но даже если вы использовали подпись, требуемую для [ClassInitialize] вы все равно получите одно и то же исключение, потому что ваш статический инициализатор поля (где возникает исключение) выполняется раньше.

ответил(а) 2020-04-04T02:27:11.689570+03:00 6 месяцев, 3 недели назад
41

Я думаю, вы поняли [ClassInitialize] достаточно хорошо, но проблема в том, что вы получили исключение инициализации типа, и это обычно вызывается, когда статическое поле класса не может быть инициализировано. Я не думаю, что вы можете сделать TcpClient и Stream static - они должны быть инициализированы где-то внутри метода. Я бы предложил удалить все статические поля в классе UnitTest1 и просто перенести их в метод GetResponse следующим образом:

private static int GetResponce(string request)
{
TcpClient connectionSocket = new TcpClient("localhost", 5678);
Stream ns = connectionSocket.GetStream();
StreamReader sr = new StreamReader(ns);
StreamWriter sw = new StreamWriter(ns);

sw.AutoFlush = true;
sw.WriteLine(request);
string message = sr.ReadLine();
return Convert.ToInt32(message);
}


Это должно хорошо работать :)

ответил(а) 2020-04-04T02:13:04+03:00 6 месяцев, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема