Skip to content

Commit db2f509

Browse files
Copilotnnhy
andcommitted
Refactor ModbusSlaveIntegrationTests to use IClassFixture and OS-assigned free port
Co-authored-by: nnhy <506367+nnhy@users.noreply.github.com>
1 parent d1e7181 commit db2f509

1 file changed

Lines changed: 76 additions & 53 deletions

File tree

XUnitTest/ModbusSlaveIntegrationTests.cs

Lines changed: 76 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,79 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Linq;
7+
using System.Net;
8+
using System.Net.Sockets;
79
using System.Threading;
810
using Xunit;
911

1012
namespace XUnitTest;
1113

12-
/// <summary>ModbusSlave集成测试 - 覆盖更多场景</summary>
13-
public class ModbusSlaveIntegrationTests : IDisposable
14+
/// <summary>ModbusSlave集成测试的共享fixture,服务器仅启动一次</summary>
15+
public class ModbusSlaveFixture : IDisposable
1416
{
15-
private readonly ModbusSlave _slave;
16-
private readonly Int32 _port;
17+
public ModbusSlave Slave { get; }
18+
public Int32 Port { get; }
1719

18-
public ModbusSlaveIntegrationTests()
20+
public ModbusSlaveFixture()
1921
{
20-
_port = 1507;
21-
_slave = new ModbusSlave
22+
// 使用OS分配的空闲端口,避免端口冲突
23+
var listener = new TcpListener(IPAddress.Loopback, 0);
24+
listener.Start();
25+
Port = ((IPEndPoint)listener.LocalEndpoint).Port;
26+
listener.Stop();
27+
28+
Slave = new ModbusSlave
2229
{
23-
Port = _port,
30+
Port = Port,
2431
Registers = Enumerable.Range(0, 20)
2532
.Select(i => new RegisterUnit { Address = i, Value = (UInt16)(200 + i) })
2633
.ToList(),
2734
Coils = Enumerable.Range(0, 32)
2835
.Select(i => new CoilUnit { Address = i, Value = (Byte)(i % 2) })
2936
.ToList(),
3037
};
31-
_slave.Start();
38+
Slave.Start();
3239
Thread.Sleep(200);
3340
}
3441

35-
public void Dispose() => _slave.Dispose();
42+
public void Dispose() => Slave.Dispose();
43+
}
44+
45+
/// <summary>ModbusSlave集成测试 - 覆盖更多场景</summary>
46+
/// <remarks>
47+
/// 使用IClassFixture共享服务器实例,避免每个测试重复启动/停止。
48+
/// 读取初始值的测试使用寄存器0-9和线圈0-15(不被写测试修改);
49+
/// 写入测试使用寄存器10-19和线圈16-31(自包含,写后立即读回验证)。
50+
/// </remarks>
51+
public class ModbusSlaveIntegrationTests : IClassFixture<ModbusSlaveFixture>
52+
{
53+
private readonly ModbusSlaveFixture _fixture;
54+
55+
public ModbusSlaveIntegrationTests(ModbusSlaveFixture fixture)
56+
{
57+
_fixture = fixture;
58+
}
3659

3760
private ModbusTcp CreateClient()
3861
{
3962
var client = new ModbusTcp
4063
{
41-
Server = $"tcp://localhost:{_port}",
64+
Server = $"tcp://localhost:{_fixture.Port}",
4265
Timeout = 3000,
4366
};
4467
client.Open();
4568
return client;
4669
}
4770

48-
#region 读取寄存器
71+
#region 读取寄存器(使用地址0-9,不被写测试修改)
4972
[Fact]
5073
public void ReadRegister_AllRegisters()
5174
{
5275
using var client = CreateClient();
53-
var rs = client.ReadRegister(1, 0, 20);
76+
var rs = client.ReadRegister(1, 0, 10);
5477
Assert.NotNull(rs);
55-
Assert.Equal(20, rs.Length);
56-
for (var i = 0; i < 20; i++)
78+
Assert.Equal(10, rs.Length);
79+
for (var i = 0; i < 10; i++)
5780
Assert.Equal((UInt16)(200 + i), rs[i]);
5881
}
5982

@@ -72,10 +95,10 @@ public void ReadRegister_MiddleRange()
7295
public void ReadRegister_LastRegister()
7396
{
7497
using var client = CreateClient();
75-
var rs = client.ReadRegister(1, 19, 1);
98+
var rs = client.ReadRegister(1, 9, 1);
7699
Assert.NotNull(rs);
77100
Assert.Single(rs);
78-
Assert.Equal((UInt16)219, rs[0]);
101+
Assert.Equal((UInt16)209, rs[0]);
79102
}
80103

81104
[Fact]
@@ -89,15 +112,15 @@ public void ReadRegister_FirstRegister()
89112
}
90113
#endregion
91114

92-
#region 读取线圈
115+
#region 读取线圈(使用地址0-15,不被写测试修改)
93116
[Fact]
94117
public void ReadCoil_AllCoils()
95118
{
96119
using var client = CreateClient();
97-
var rs = client.ReadCoil(1, 0, 32);
120+
var rs = client.ReadCoil(1, 0, 16);
98121
Assert.NotNull(rs);
99-
Assert.Equal(32, rs.Length);
100-
for (var i = 0; i < 32; i++)
122+
Assert.Equal(16, rs.Length);
123+
for (var i = 0; i < 16; i++)
101124
Assert.Equal(i % 2 == 1, rs[i]);
102125
}
103126

@@ -143,37 +166,37 @@ public void ReadCoil_PartialByte()
143166
}
144167
#endregion
145168

146-
#region 写入寄存器
169+
#region 写入寄存器(使用地址10-19,自包含写后读回)
147170
[Fact]
148171
public void WriteRegister_ThenRead()
149172
{
150173
using var client = CreateClient();
151-
var rs = client.WriteRegister(1, 0, 0x9999);
174+
var rs = client.WriteRegister(1, 10, 0x9999);
152175
Assert.Equal(0x9999, rs);
153176

154-
var regs = client.ReadRegister(1, 0, 1);
177+
var regs = client.ReadRegister(1, 10, 1);
155178
Assert.Equal(0x9999, regs[0]);
156179
}
157180

158181
[Fact]
159182
public void WriteRegister_MaxValue()
160183
{
161184
using var client = CreateClient();
162-
var rs = client.WriteRegister(1, 1, 0xFFFF);
185+
var rs = client.WriteRegister(1, 11, 0xFFFF);
163186
Assert.Equal(0xFFFF, rs);
164187

165-
var regs = client.ReadRegister(1, 1, 1);
188+
var regs = client.ReadRegister(1, 11, 1);
166189
Assert.Equal(0xFFFF, regs[0]);
167190
}
168191

169192
[Fact]
170193
public void WriteRegister_ZeroValue()
171194
{
172195
using var client = CreateClient();
173-
var rs = client.WriteRegister(1, 2, 0x0000);
196+
var rs = client.WriteRegister(1, 12, 0x0000);
174197
Assert.Equal(0, rs);
175198

176-
var regs = client.ReadRegister(1, 2, 1);
199+
var regs = client.ReadRegister(1, 12, 1);
177200
Assert.Equal(0, regs[0]);
178201
}
179202

@@ -182,10 +205,10 @@ public void WriteRegisters_ThenRead()
182205
{
183206
using var client = CreateClient();
184207
var values = new UInt16[] { 0xAAAA, 0xBBBB, 0xCCCC, 0xDDDD };
185-
var rs = client.WriteRegisters(1, 10, values);
208+
var rs = client.WriteRegisters(1, 13, values);
186209
Assert.Equal(4, rs);
187210

188-
var regs = client.ReadRegister(1, 10, 4);
211+
var regs = client.ReadRegister(1, 13, 4);
189212
Assert.Equal(0xAAAA, regs[0]);
190213
Assert.Equal(0xBBBB, regs[1]);
191214
Assert.Equal(0xCCCC, regs[2]);
@@ -196,27 +219,27 @@ public void WriteRegisters_ThenRead()
196219
public void WriteRegisters_SingleValue()
197220
{
198221
using var client = CreateClient();
199-
var rs = client.WriteRegisters(1, 15, new UInt16[] { 0x1111 });
222+
var rs = client.WriteRegisters(1, 17, new UInt16[] { 0x1111 });
200223
Assert.Equal(1, rs);
201224

202-
var regs = client.ReadRegister(1, 15, 1);
225+
var regs = client.ReadRegister(1, 17, 1);
203226
Assert.Equal(0x1111, regs[0]);
204227
}
205228
#endregion
206229

207-
#region 写入线圈
230+
#region 写入线圈(使用地址16-31,自包含写后读回)
208231
[Fact]
209232
public void WriteCoil_ThenRead()
210233
{
211234
using var client = CreateClient();
212-
// 线圈0初始为0,写入1
213-
client.WriteCoil(1, 0, 0xFF00);
214-
var rs = client.ReadCoil(1, 0, 1);
235+
// 写入ON
236+
client.WriteCoil(1, 16, 0xFF00);
237+
var rs = client.ReadCoil(1, 16, 1);
215238
Assert.True(rs[0]);
216239

217240
// 再关闭
218-
client.WriteCoil(1, 0, 0x0000);
219-
rs = client.ReadCoil(1, 0, 1);
241+
client.WriteCoil(1, 16, 0x0000);
242+
rs = client.ReadCoil(1, 16, 1);
220243
Assert.False(rs[0]);
221244
}
222245

@@ -226,10 +249,10 @@ public void WriteCoils_ThenRead()
226249
using var client = CreateClient();
227250
// 写入8个线圈:全开
228251
var values = new UInt16[] { 0xFF00, 0xFF00, 0xFF00, 0xFF00, 0xFF00, 0xFF00, 0xFF00, 0xFF00 };
229-
var rs = client.WriteCoils(1, 0, values);
252+
var rs = client.WriteCoils(1, 16, values);
230253
Assert.Equal(8, rs);
231254

232-
var coils = client.ReadCoil(1, 0, 8);
255+
var coils = client.ReadCoil(1, 16, 8);
233256
for (var i = 0; i < 8; i++)
234257
Assert.True(coils[i]);
235258
}
@@ -240,10 +263,10 @@ public void WriteCoils_AllOff_ThenRead()
240263
using var client = CreateClient();
241264
// 全关
242265
var values = new UInt16[] { 0, 0, 0, 0, 0, 0, 0, 0 };
243-
var rs = client.WriteCoils(1, 0, values);
266+
var rs = client.WriteCoils(1, 16, values);
244267
Assert.Equal(8, rs);
245268

246-
var coils = client.ReadCoil(1, 0, 8);
269+
var coils = client.ReadCoil(1, 16, 8);
247270
for (var i = 0; i < 8; i++)
248271
Assert.False(coils[i]);
249272
}
@@ -254,10 +277,10 @@ public void WriteCoils_NonMultipleOf8_ThenRead()
254277
using var client = CreateClient();
255278
// 写5个线圈(非8的倍数)
256279
var values = new UInt16[] { 0xFF00, 0x0000, 0xFF00, 0x0000, 0xFF00 };
257-
var rs = client.WriteCoils(1, 16, values);
280+
var rs = client.WriteCoils(1, 24, values);
258281
Assert.Equal(5, rs);
259282

260-
var coils = client.ReadCoil(1, 16, 5);
283+
var coils = client.ReadCoil(1, 24, 5);
261284
Assert.True(coils[0]);
262285
Assert.False(coils[1]);
263286
Assert.True(coils[2]);
@@ -280,20 +303,20 @@ public void WriteCoils_Pattern_ThenRead()
280303
}
281304
#endregion
282305

283-
#region 读写组合场景
306+
#region 读写组合场景(使用写区域地址,自包含)
284307
[Fact]
285308
public void ReadWrite_RegisterRoundTrip()
286309
{
287310
using var client = CreateClient();
288311

289-
// 写入一系列值,然后读回
312+
// 写入一系列值,然后读回(使用寄存器10-19)
290313
for (UInt16 i = 0; i < 10; i++)
291314
{
292315
var val = (UInt16)(i * 1000);
293-
client.WriteRegister(1, i, val);
316+
client.WriteRegister(1, (UInt16)(10 + i), val);
294317
}
295318

296-
var regs = client.ReadRegister(1, 0, 10);
319+
var regs = client.ReadRegister(1, 10, 10);
297320
for (var i = 0; i < 10; i++)
298321
Assert.Equal((UInt16)(i * 1000), regs[i]);
299322
}
@@ -303,14 +326,14 @@ public void ReadWrite_CoilRoundTrip()
303326
{
304327
using var client = CreateClient();
305328

306-
// 逐个设置线圈
329+
// 逐个设置线圈(使用线圈24-31)
307330
for (var i = 0; i < 8; i++)
308331
{
309332
var value = (UInt16)(i % 3 == 0 ? 0xFF00 : 0x0000);
310-
client.WriteCoil(1, (UInt16)i, value);
333+
client.WriteCoil(1, (UInt16)(24 + i), value);
311334
}
312335

313-
var coils = client.ReadCoil(1, 0, 8);
336+
var coils = client.ReadCoil(1, 24, 8);
314337
for (var i = 0; i < 8; i++)
315338
Assert.Equal(i % 3 == 0, coils[i]);
316339
}
@@ -341,8 +364,8 @@ public void Slave_DefaultPort()
341364
[Fact]
342365
public void Slave_RegistersAndCoils()
343366
{
344-
Assert.Equal(20, _slave.Registers.Count);
345-
Assert.Equal(32, _slave.Coils.Count);
367+
Assert.Equal(20, _fixture.Slave.Registers.Count);
368+
Assert.Equal(32, _fixture.Slave.Coils.Count);
346369
}
347370

348371
[Fact]

0 commit comments

Comments
 (0)