如何在 C# 单元测试中手动存根一个函数
How to manually stub out a function in C# unit testing
我正在尝试创建自己的 Mock 函数。我知道最小起订量存在,但我真的很想完成它。
我不知道如何创建 Mock 函数来阻止 User#Foobar 对 Product#Price 的调用,以便对 User#Foobar 进行单元测试:
using System;
public class MyUnitTest
{
// CODE BEING TESTED
class Product {
public string Price() {
return "I WANT TO STUB THIS OUT, DONT WANT TO SEE THIS STRING";
}
}
class User {
public string Foobar() {
Product product = new Product();
return $"Foobar, Product#price returns: {product.Price()}";
}
}
//////////////////////
// CODE THAT DOES THE TEST
public void Mock(string mockedClass, string mockedFunction, string mockedReturn)
{
// no idea what to put here something to do with an interface
}
public void RunTests()
{
Mock("Product","Price","MOCKED DATA");
string expected = new User().Foobar();
Assert(expected, "Foobar, Product#price returns: MOCKED DATA");
}
public void Assert(string actual, string expected){
Console.WriteLine(actual == expected);
}
//////////////////////
// How do I stub out Product to return "MOCKED DATA"?
}
public class Program
{
public static void Main(){
new MyUnitTest().RunTests();
}
}
我知道如何在 Ruby 中执行此操作,但不知道如何在 C# 中执行。
这里有一个 NET fiddle 的问题:
https://dotnetfiddle.net/8Re2jb
这个 Mock 函数应该如何拦截 User#Foobar 对 Product#Price 的调用和return字符串“MOCKED DATA”?
public void Mock(string mockedClass, string mockedFunction, string mockedReturn)
{
}
您应该使用依赖注入来解决这个问题。 User
对象不应该知道 Product
的具体类型。相反,您应该将 Product
封装在 IProduct
接口后面,并将 User
构造函数传递给 IProduct
或某种 IProduct
工厂。
例如:
public interface IProduct
{
public string Price();
}
public interface IUser
{
public string FooBar();
}
public class User: IUser
{
public User(Func<IProduct> productFactory)
{
_productFactory = productFactory;
}
public string FooBar()
{
var product = _productFactory();
return $"Foobar, Product#price returns: {product.Price()}";
}
readonly Func<IProduct> _productFactory;
}
然后您可以创建自己的“模拟”产品(我们应该真正称之为存根):
public class MockProduct : IProduct
{
public MockProduct(string mockPrice)
{
_mockPrice = mockPrice;
}
public string Price()
{
return _mockPrice;
}
readonly string _mockPrice;
}
然后在你的测试中你可以创建 User
并传递给它一个工厂方法来创建一个模拟 IProduct
:
Func<IProduct> mockProductFactory = () => new MockProduct("MOCKED DATA");
User user = new User(mockProductFactory);
// Test user now.
尽管这实际上是存根(您已经使用过的术语)而不是模拟的一种形式 - 模拟通常使用模拟框架完成,并使用反射来避免必须手动实现接口中的所有方法。
我正在尝试创建自己的 Mock 函数。我知道最小起订量存在,但我真的很想完成它。
我不知道如何创建 Mock 函数来阻止 User#Foobar 对 Product#Price 的调用,以便对 User#Foobar 进行单元测试:
using System;
public class MyUnitTest
{
// CODE BEING TESTED
class Product {
public string Price() {
return "I WANT TO STUB THIS OUT, DONT WANT TO SEE THIS STRING";
}
}
class User {
public string Foobar() {
Product product = new Product();
return $"Foobar, Product#price returns: {product.Price()}";
}
}
//////////////////////
// CODE THAT DOES THE TEST
public void Mock(string mockedClass, string mockedFunction, string mockedReturn)
{
// no idea what to put here something to do with an interface
}
public void RunTests()
{
Mock("Product","Price","MOCKED DATA");
string expected = new User().Foobar();
Assert(expected, "Foobar, Product#price returns: MOCKED DATA");
}
public void Assert(string actual, string expected){
Console.WriteLine(actual == expected);
}
//////////////////////
// How do I stub out Product to return "MOCKED DATA"?
}
public class Program
{
public static void Main(){
new MyUnitTest().RunTests();
}
}
我知道如何在 Ruby 中执行此操作,但不知道如何在 C# 中执行。
这里有一个 NET fiddle 的问题:
https://dotnetfiddle.net/8Re2jb
这个 Mock 函数应该如何拦截 User#Foobar 对 Product#Price 的调用和return字符串“MOCKED DATA”?
public void Mock(string mockedClass, string mockedFunction, string mockedReturn)
{
}
您应该使用依赖注入来解决这个问题。 User
对象不应该知道 Product
的具体类型。相反,您应该将 Product
封装在 IProduct
接口后面,并将 User
构造函数传递给 IProduct
或某种 IProduct
工厂。
例如:
public interface IProduct
{
public string Price();
}
public interface IUser
{
public string FooBar();
}
public class User: IUser
{
public User(Func<IProduct> productFactory)
{
_productFactory = productFactory;
}
public string FooBar()
{
var product = _productFactory();
return $"Foobar, Product#price returns: {product.Price()}";
}
readonly Func<IProduct> _productFactory;
}
然后您可以创建自己的“模拟”产品(我们应该真正称之为存根):
public class MockProduct : IProduct
{
public MockProduct(string mockPrice)
{
_mockPrice = mockPrice;
}
public string Price()
{
return _mockPrice;
}
readonly string _mockPrice;
}
然后在你的测试中你可以创建 User
并传递给它一个工厂方法来创建一个模拟 IProduct
:
Func<IProduct> mockProductFactory = () => new MockProduct("MOCKED DATA");
User user = new User(mockProductFactory);
// Test user now.
尽管这实际上是存根(您已经使用过的术语)而不是模拟的一种形式 - 模拟通常使用模拟框架完成,并使用反射来避免必须手动实现接口中的所有方法。