C# 如何在MSTest中测试具有控制台I/O的类
如何在MSTest中向类的方法输入数据。我有以下代码,但在进行单元测试时无法输入C# 如何在MSTest中测试具有控制台I/O的类,c#,tdd,mstest,C#,Tdd,Mstest,如何在MSTest中向类的方法输入数据。我有以下代码,但在进行单元测试时无法输入GetData() public class MyData { private string _name; public void GetData() { Console.WriteLine("Please Enter your Name(only Alphabet)"); _name = Console.ReadLine(); Console
GetData()
public class MyData
{
private string _name;
public void GetData()
{
Console.WriteLine("Please Enter your Name(only Alphabet)");
_name = Console.ReadLine();
Console.WriteLine(_name);
}
}
测试班
我要做的是通过接口抽象出控制台方法。您的方法不应该关心控制台的工作方式。以后可能需要更改写或读一行的方式。大概是这样的:
public interface IConsoleMethods
{
void WriteLine(string message);
string ReadLine();
}
您可以按照您喜欢的方式实施它:
public class ConsoleMethods : IConsoleMethods
{
public void WriteLine(string message)
{
Console.WriteLine(message);
}
public string ReadLine()
{
return Console.ReadLine();
}
}
您必须为myData创建一个构造函数,该构造函数接受IConsoleMethods来初始化它。理想情况下,您希望根据使用情况注入它
public class MyData
{
public MyData(IConsoleMethods consoleMethods) { this.console = consoleMethods; }
public IConsoleMethods console;
private string _name;
public void GetData()
{
console.WriteLine("Please Enter your Name(only Alphabet)");
_name = console.ReadLine();
console.WriteLine(_name);
}
}
您可以通过以下方式获得原始功能:
var myData = new MyData(new ConsoleMethods());
myData.GetData();
最后,您可以使用Mock测试它,并设置新的控制台类的期望值,就像这样(我使用Moq进行模拟):
[TestClass]
公共类MyDataTests
{
[测试方法]
public void GetDataTest()
{
const string expectedDisplayMessage=“请输入您的姓名(仅字母表)”;
const string readString=“test”;
var consoleMock=new Mock();
Setup(c=>c.ReadLine())。返回(readString);
var dd=新的MyData(consoleMock.Object);
dd.GetData();
//检查writeline是否被调用了两次
Verify(c=>c.WriteLine(It.IsAny()),Times.justice(2));
//检查是否在显示消息时调用了writeline
consoleMock.Verify(c=>c.WriteLine(expectedDisplayMessage),Times.Once);
//检查Readline是否被调用过一次
consoleMock.Verify(c=>c.ReadLine(),Times.Once);
//检查是否使用测试字符串调用了writeline一次
consoleMock.Verify(c=>c.WriteLine(readString),Times.Once);
}
}
我使用了(而不是MSTest)和(来模拟单元测试)
解释在代码中的注释中
using System;
using AlternativesCommon;
using NUnit.Framework;
using Moq;
/// <summary>
/// This demostrates how to use all approaches.
/// </summary>
namespace Usage
{
public class Program
{
public static void Main()
{
var myData = new Original.MyData();
myData.GetData();
var myData1 = new Alternative1.MyData(new StandardConsole());
myData1.GetData();
var myData2 = new Alternative2.MyDataController(
new StandardConsole(),
new Alternative2.MyData());
myData2.GetData();
}
}
}
/// <summary>
/// This demonstrates how to test each approach.
/// </summary>
namespace Tests
{
public class Alternative1Tests
{
/// <summary>
/// The original apporach is too tightly coupled to be unit tested.
/// </summary>
[Test]
public void Original_MyDataCannotBeAutomatedTested()
{
}
/// <summary>
/// The first alternative abstracts the console out.
/// This is better, but still requires a lot of mocking and syntax.
/// </summary>
[Test]
public void Alternative1_MyDataShouldWork()
{
// Arrange
var mockConsole = new Mock<IConsole>();
mockConsole.Setup(c => c.WriteLine(
"Please Enter your Name(only Alphabet)"));
mockConsole.Setup(c => c.ReadLine()).Returns("John");
mockConsole.Setup(c => c.WriteLine("John"));
var myData = new Alternative1.MyData(mockConsole.Object);
// Act
myData.GetData();
// Assert
mockConsole.VerifyAll();
}
/// <summary>
/// The second alternative abstracts the data model out.
/// This allows us to unit test just our domain logic.
/// We can test the controller in a larger boundary or
/// integration test if we want (not shown).
/// </summary>
[Test]
public void Alternative2_MyDataShouldWork()
{
// Arrange
var name = "John";
var myData = new Alternative2.MyData();
var initialValue = myData.Name;
// Act
myData.Name = name;
// Assert
Assert.That(initialValue, Is.Null);
Assert.That(myData.Name, Is.EqualTo(name));
}
/// <summary>
/// This shows how one would unit test the controller.
/// Lots of mocking (ew!).
/// </summary>
[Test]
public void Alternative2_MyDataControllerShouldWork()
{
// Arrange
var mockConsole = new Mock<IConsole>();
mockConsole.Setup(c => c.WriteLine(
"Please Enter your Name(only Alphabet)"));
mockConsole.Setup(c => c.ReadLine()).Returns("John");
mockConsole.Setup(c => c.WriteLine("John"));
string name = null;
var mockData = new Mock<Alternative2.IMyData>();
mockData.SetupGet(d => d.Name).Returns(() => name);
mockData.
SetupSet(d => d.Name = It.IsAny<string>()).
Callback((string value) => name = value);
var controller = new Alternative2.MyDataController(
mockConsole.Object,
mockData.Object);
// Act
controller.GetData();
// Assert
mockConsole.VerifyAll();
mockData.VerifyAll();
}
}
}
namespace Original
{
public class MyData
{
private string _name;
public void GetData()
{
Console.WriteLine("Please Enter your Name(only Alphabet)");
_name = Console.ReadLine();
Console.WriteLine(_name);
}
}
}
namespace Alternative1
{
public class MyData
{
private string _name;
private IConsole _console;
public MyData(IConsole console)
{
this._console = console;
}
public void GetData()
{
this._console.WriteLine("Please Enter your Name(only Alphabet)");
this._name = this._console.ReadLine();
this._console.WriteLine(this._name);
}
}
}
namespace Alternative2
{
public interface IMyData
{
string Name
{
set;
get;
}
}
public class MyData : IMyData
{
private string _name;
public string Name
{
set
{
// Do any validation here.
// For example, uncomment out the following
// (but don't forget to test!):
//if (string.IsNullOrEmpty(value))
//{
// throw new Exception(
// @"Name cannot be empty or null.");
//}
//if (value.Length > 100)
//{
// throw new Exception(
// @"Name cannot be longer than 100 characters.");
//}
this._name = value;
}
get
{
return this._name;
}
}
}
public class MyDataController
{
private IConsole _console;
private IMyData _data;
public MyDataController(IConsole console, IMyData data)
{
this._console = console;
this._data = data;
}
public void GetData()
{
this._console.WriteLine("Please Enter your Name(only Alphabet)");
this._data.Name = this._console.ReadLine();
this._console.WriteLine(this._data.Name);
}
}
}
/// <summary>
/// Defines the console abstraction used by both alternatives.
/// </summary>
namespace AlternativesCommon
{
public interface IConsole
{
string ReadLine();
void WriteLine(string line);
}
public class StandardConsole : IConsole
{
public string ReadLine()
{
return Console.ReadLine();
}
public void WriteLine(string line)
{
Console.WriteLine(line);
}
}
}
使用系统;
使用交替的通用程序;
使用NUnit.Framework;
使用最小起订量;
///
///这演示了如何使用所有方法。
///
命名空间使用
{
公共课程
{
公共静态void Main()
{
var myData=new Original.myData();
myData.GetData();
var myData1=new Alternative1.MyData(new StandardConsole());
myData1.GetData();
var myData2=new Alternative2.MyDataController(
新的StandardConsole(),
新的备选方案2.MyData());
myData2.GetData();
}
}
}
///
///这演示了如何测试每种方法。
///
命名空间测试
{
公开课备选测验
{
///
///最初的apporach耦合太紧密,无法进行单元测试。
///
[测试]
公共无效原件\u MyDataCannotBeAutomatedTested()
{
}
///
///第一个备选方案将控制台抽象出来。
///这更好,但仍然需要大量的模拟和语法。
///
[测试]
public void alternative 1_MyDataShouldWork()
{
//安排
var mockConsole=new Mock();
mockConsole.Setup(c=>c.WriteLine(
“请输入您的姓名(仅字母)”;
mockConsole.Setup(c=>c.ReadLine()).Returns(“John”);
mockConsole.Setup(c=>c.WriteLine(“John”);
var myData=new Alternative1.myData(mockConsole.Object);
//表演
myData.GetData();
//断言
mockConsole.VerifyAll();
}
///
///第二个备选方案将数据模型抽象出来。
///这允许我们只对域逻辑进行单元测试。
///我们可以在更大的边界或更大的范围内测试控制器
///如果需要,集成测试(未显示)。
///
[测试]
public void alternative 2_MyDataShouldWork()
{
//安排
var name=“John”;
var myData=new Alternative2.myData();
var initialValue=myData.Name;
//表演
myData.Name=Name;
//断言
Assert.That(initialValue,Is.Null);
Assert.That(myData.Name,Is.EqualTo(Name));
}
///
///这显示了如何对控制器进行单元测试。
///很多嘲弄(哎哟!)。
///
[测试]
public void alternative 2_MyDataControllerShouldWork()
{
//安排
var mockConsole=new Mock();
mockConsole.Setup(c=>c.WriteLine(
“请输入您的姓名(仅字母)”;
mockConsole.Setup(c=>c.ReadLine()).Returns(“John”);
mockConsole.Setup(c=>c.WriteLine(“John”);
字符串名称=null;
var mockData=new Mock();
SetupGet(d=>d.Name).返回(()=>Name);
模拟数据。
SetupSet(d=>d.Name=It.IsAny()。
回调((字符串值)=>name=value);
var controller=new Alternative2.MyDataController(
mockConsole.Object,
mockData.Object);
//表演
controller.GetData();
//断言
mockConsole.VerifyAll();
mockData.VerifyAll();
}
}
}
名称空间原始
{
公共类MyData
{
私有字符串\u名称;
public void GetData()
{
Console.WriteLine(“请输入您的姓名(仅字母表)”);
_name=Console.ReadLine();
Console.WriteLine(_名称);
}
}
}
名称空间可选1
{
公共类MyData
{
私有字符串\u名称;
专用IConsole_控制台;
公共MyData(IConsole控制台)
{
这个。_console=控制台;
}
public void GetData()
{
这个._console.WriteLine(“请输入您的姓名(仅字母表)”);
this._name=this._console.ReadLine();
这个。控制台。写线(这个
[TestClass]
public class MyDataTests
{
[TestMethod]
public void GetDataTest()
{
const string expectedDisplayMessage = "Please Enter your Name(only Alphabet)";
const string readString = "test";
var consoleMock = new Mock<IConsoleMethods>();
consoleMock.Setup(c => c.ReadLine()).Returns(readString);
var dd = new MyData(consoleMock.Object);
dd.GetData();
//Check that writeline was called twice
consoleMock.Verify(c => c.WriteLine(It.IsAny<string>()), Times.Exactly(2));
//Check that writeline was called with you display message
consoleMock.Verify(c=>c.WriteLine(expectedDisplayMessage), Times.Once);
//check that Readline was called once
consoleMock.Verify(c=>c.ReadLine(),Times.Once);
//Check that writeline was called with your test string once
consoleMock.Verify(c=>c.WriteLine(readString), Times.Once);
}
}
using System;
using AlternativesCommon;
using NUnit.Framework;
using Moq;
/// <summary>
/// This demostrates how to use all approaches.
/// </summary>
namespace Usage
{
public class Program
{
public static void Main()
{
var myData = new Original.MyData();
myData.GetData();
var myData1 = new Alternative1.MyData(new StandardConsole());
myData1.GetData();
var myData2 = new Alternative2.MyDataController(
new StandardConsole(),
new Alternative2.MyData());
myData2.GetData();
}
}
}
/// <summary>
/// This demonstrates how to test each approach.
/// </summary>
namespace Tests
{
public class Alternative1Tests
{
/// <summary>
/// The original apporach is too tightly coupled to be unit tested.
/// </summary>
[Test]
public void Original_MyDataCannotBeAutomatedTested()
{
}
/// <summary>
/// The first alternative abstracts the console out.
/// This is better, but still requires a lot of mocking and syntax.
/// </summary>
[Test]
public void Alternative1_MyDataShouldWork()
{
// Arrange
var mockConsole = new Mock<IConsole>();
mockConsole.Setup(c => c.WriteLine(
"Please Enter your Name(only Alphabet)"));
mockConsole.Setup(c => c.ReadLine()).Returns("John");
mockConsole.Setup(c => c.WriteLine("John"));
var myData = new Alternative1.MyData(mockConsole.Object);
// Act
myData.GetData();
// Assert
mockConsole.VerifyAll();
}
/// <summary>
/// The second alternative abstracts the data model out.
/// This allows us to unit test just our domain logic.
/// We can test the controller in a larger boundary or
/// integration test if we want (not shown).
/// </summary>
[Test]
public void Alternative2_MyDataShouldWork()
{
// Arrange
var name = "John";
var myData = new Alternative2.MyData();
var initialValue = myData.Name;
// Act
myData.Name = name;
// Assert
Assert.That(initialValue, Is.Null);
Assert.That(myData.Name, Is.EqualTo(name));
}
/// <summary>
/// This shows how one would unit test the controller.
/// Lots of mocking (ew!).
/// </summary>
[Test]
public void Alternative2_MyDataControllerShouldWork()
{
// Arrange
var mockConsole = new Mock<IConsole>();
mockConsole.Setup(c => c.WriteLine(
"Please Enter your Name(only Alphabet)"));
mockConsole.Setup(c => c.ReadLine()).Returns("John");
mockConsole.Setup(c => c.WriteLine("John"));
string name = null;
var mockData = new Mock<Alternative2.IMyData>();
mockData.SetupGet(d => d.Name).Returns(() => name);
mockData.
SetupSet(d => d.Name = It.IsAny<string>()).
Callback((string value) => name = value);
var controller = new Alternative2.MyDataController(
mockConsole.Object,
mockData.Object);
// Act
controller.GetData();
// Assert
mockConsole.VerifyAll();
mockData.VerifyAll();
}
}
}
namespace Original
{
public class MyData
{
private string _name;
public void GetData()
{
Console.WriteLine("Please Enter your Name(only Alphabet)");
_name = Console.ReadLine();
Console.WriteLine(_name);
}
}
}
namespace Alternative1
{
public class MyData
{
private string _name;
private IConsole _console;
public MyData(IConsole console)
{
this._console = console;
}
public void GetData()
{
this._console.WriteLine("Please Enter your Name(only Alphabet)");
this._name = this._console.ReadLine();
this._console.WriteLine(this._name);
}
}
}
namespace Alternative2
{
public interface IMyData
{
string Name
{
set;
get;
}
}
public class MyData : IMyData
{
private string _name;
public string Name
{
set
{
// Do any validation here.
// For example, uncomment out the following
// (but don't forget to test!):
//if (string.IsNullOrEmpty(value))
//{
// throw new Exception(
// @"Name cannot be empty or null.");
//}
//if (value.Length > 100)
//{
// throw new Exception(
// @"Name cannot be longer than 100 characters.");
//}
this._name = value;
}
get
{
return this._name;
}
}
}
public class MyDataController
{
private IConsole _console;
private IMyData _data;
public MyDataController(IConsole console, IMyData data)
{
this._console = console;
this._data = data;
}
public void GetData()
{
this._console.WriteLine("Please Enter your Name(only Alphabet)");
this._data.Name = this._console.ReadLine();
this._console.WriteLine(this._data.Name);
}
}
}
/// <summary>
/// Defines the console abstraction used by both alternatives.
/// </summary>
namespace AlternativesCommon
{
public interface IConsole
{
string ReadLine();
void WriteLine(string line);
}
public class StandardConsole : IConsole
{
public string ReadLine()
{
return Console.ReadLine();
}
public void WriteLine(string line)
{
Console.WriteLine(line);
}
}
}