如何模拟和设置局部变量
How to mock and setup local variable
我正在使用 mock 和 autoFixture 库进行 .NET CORE 3.1 nUnit 测试。我需要模拟遥测变量不知道如何实现它?
var telemetry = telemetryInit as TelemetryInitializerStandard;
我正在尝试模拟这个变量,否则它将为空,我无法分配该值。遥测需要是 TelemetryInitializerStandard 实例。
public class 字母表
{
私有 ILogger 日志;
private readonly ITelemetryInitializer telemetryInit;
public ABC(ILogger logger, ITelemetryInitializer telemetryInit)
{
log = logger;
this.telemetryInit = telemetryInit;
}
public async Task<List<Customer>> RunAsync()
{
var telemetry = telemetryInit as TelemetryInitializerStandard;
// remaining code
}
}
Test
[TestFixture]
public class AbcTest
{
private readonly ABC _sut;
private readonly Mock<ILogger> _loggerMoq;
private readonly Mock<ITelemetryInitializer> _telemetryInitializerMoq;
public AbcTest()
{
this._loggerMoq = new Mock<ILogger>();
this._telemetryInitializerMoq = new Mock<ITelemetryInitializer>();
this._sut = new DiscoverFA(_loggerMoq.Object, _telemetryInitializerMoq.Object);
[Test]
public void Test1()
{
//Arrange
var fixture = new Fixture();
var telemetryMoq = fixture.Create<TelemetryInitializerStandard>();
}
无论是将ITelemetryInitializer
作为参数传入RunAsync()
,还是通过构造函数传入class,在方法中使用,问题都是一样的。我们已经通过声明参数的类型 ITelemetryInitializer
告诉任何使用该代码的人(包括我们自己)任何实现该接口的东西都可以在这里使用。
然后我们将传入的实例转换为具体的 class TelemetryInitializerStandard
并使用一些 属性 (TenantId?) 在具体的 class 但不包括在内在界面中。这打破了 'contract',即 ITelemetryInitializer
是一个足够的参数。
在理想情况下,解决方案是扩展 ITelemetryInitializer
接口以包含我们需要的来自 TelemetryInitializerStandard
的属性,或者,将 ITelemetryInitializer
参数替换为 TelemetryInitializerStandard
一个。 (问题不是我们使用的是 TelemetryInitializerStandard
,而是 ITelemetryInitializer
和 TelemetryInitializerStandard
之间的不匹配)。
这往往不是一个完美的世界,我们无法完全控制我们使用的代码(例如,其他人拥有界面,我们无法更改它)
可以模拟具体的 class(至少对于 Moq 4.17.2),但我们只能模拟 properties/methods 是虚拟的
internal class Program
{
static void Main()
{
var mockClass = new Mock<LeakyConcrete>();
mockClass.Setup(mk => mk.Foo()).Returns("Foo (Mocked)");
mockClass.Setup(mk => mk.Bar()).Returns("Bar (Mocked)");
var driver = new Driver(new Concrete()); //A
//var driver = new Driver(new LeakyConcrete()); //B
//var driver = new Driver(mockClass.Object); //C
driver.Run();
driver.RunWithLeak();
}
}
public interface IAbstraction
{
string Foo();
}
class Driver
{
private readonly IAbstraction _abstraction;
public Driver(IAbstraction abstraction)
{
_abstraction = abstraction;
}
public void Run()
{
var value = _abstraction.Foo();
System.Console.WriteLine(value);
}
public void RunWithLeak()
{
var value = (_abstraction as LeakyConcrete)?.Bar() ?? "!!Abstraction Leak!!";
System.Console.Write(value);
}
}
public class Concrete : IAbstraction
{
public string Foo()
{
return "Foo";
}
}
public class LeakyConcrete : IAbstraction
{
public virtual string Foo()
{
return "Foo (leaky)";
}
public virtual string Bar()
{
return "Bar (leaky)";
}
}
用上面的代码我得到
一个
富
!!抽象泄漏!!
B
Foo(泄漏)
酒吧(漏水)
C
Foo(嘲笑)
栏(模拟)
底线,如果你很幸运并且你在 TelemetryInitializerStandard
上使用的属性是虚拟的,那么你可以模拟它们,但是,如果你可以控制代码和时间来做,我将扩展 ITelemetryInitializer
以包含所需的属性。
可以传入一个 TelemetryInitializerStandard
,但这意味着对于单元测试,您需要让 class 上的所有内容都是虚拟的,这就像尾巴摇狗一样.
将接口转换为特定 class 以利用该 class 上的功能是一种相当 strong/bad 的代码味道,应该避免。如果我们没有 ITelemetryInitializer
接口的控制权,也许我们可以 sub-class 它
public interface ITelemetryInitializerStandard : ITelemetryInitializer
{
string TenantId { get; set; }
}
并将其传递给 ABC
。
我正在使用 mock 和 autoFixture 库进行 .NET CORE 3.1 nUnit 测试。我需要模拟遥测变量不知道如何实现它?
var telemetry = telemetryInit as TelemetryInitializerStandard;
我正在尝试模拟这个变量,否则它将为空,我无法分配该值。遥测需要是 TelemetryInitializerStandard 实例。
public class 字母表 { 私有 ILogger 日志; private readonly ITelemetryInitializer telemetryInit;
public ABC(ILogger logger, ITelemetryInitializer telemetryInit)
{
log = logger;
this.telemetryInit = telemetryInit;
}
public async Task<List<Customer>> RunAsync()
{
var telemetry = telemetryInit as TelemetryInitializerStandard;
// remaining code
}
}
Test
[TestFixture]
public class AbcTest
{
private readonly ABC _sut;
private readonly Mock<ILogger> _loggerMoq;
private readonly Mock<ITelemetryInitializer> _telemetryInitializerMoq;
public AbcTest()
{
this._loggerMoq = new Mock<ILogger>();
this._telemetryInitializerMoq = new Mock<ITelemetryInitializer>();
this._sut = new DiscoverFA(_loggerMoq.Object, _telemetryInitializerMoq.Object);
[Test]
public void Test1()
{
//Arrange
var fixture = new Fixture();
var telemetryMoq = fixture.Create<TelemetryInitializerStandard>();
}
无论是将ITelemetryInitializer
作为参数传入RunAsync()
,还是通过构造函数传入class,在方法中使用,问题都是一样的。我们已经通过声明参数的类型 ITelemetryInitializer
告诉任何使用该代码的人(包括我们自己)任何实现该接口的东西都可以在这里使用。
然后我们将传入的实例转换为具体的 class TelemetryInitializerStandard
并使用一些 属性 (TenantId?) 在具体的 class 但不包括在内在界面中。这打破了 'contract',即 ITelemetryInitializer
是一个足够的参数。
在理想情况下,解决方案是扩展 ITelemetryInitializer
接口以包含我们需要的来自 TelemetryInitializerStandard
的属性,或者,将 ITelemetryInitializer
参数替换为 TelemetryInitializerStandard
一个。 (问题不是我们使用的是 TelemetryInitializerStandard
,而是 ITelemetryInitializer
和 TelemetryInitializerStandard
之间的不匹配)。
这往往不是一个完美的世界,我们无法完全控制我们使用的代码(例如,其他人拥有界面,我们无法更改它)
可以模拟具体的 class(至少对于 Moq 4.17.2),但我们只能模拟 properties/methods 是虚拟的
internal class Program
{
static void Main()
{
var mockClass = new Mock<LeakyConcrete>();
mockClass.Setup(mk => mk.Foo()).Returns("Foo (Mocked)");
mockClass.Setup(mk => mk.Bar()).Returns("Bar (Mocked)");
var driver = new Driver(new Concrete()); //A
//var driver = new Driver(new LeakyConcrete()); //B
//var driver = new Driver(mockClass.Object); //C
driver.Run();
driver.RunWithLeak();
}
}
public interface IAbstraction
{
string Foo();
}
class Driver
{
private readonly IAbstraction _abstraction;
public Driver(IAbstraction abstraction)
{
_abstraction = abstraction;
}
public void Run()
{
var value = _abstraction.Foo();
System.Console.WriteLine(value);
}
public void RunWithLeak()
{
var value = (_abstraction as LeakyConcrete)?.Bar() ?? "!!Abstraction Leak!!";
System.Console.Write(value);
}
}
public class Concrete : IAbstraction
{
public string Foo()
{
return "Foo";
}
}
public class LeakyConcrete : IAbstraction
{
public virtual string Foo()
{
return "Foo (leaky)";
}
public virtual string Bar()
{
return "Bar (leaky)";
}
}
用上面的代码我得到
一个
富
!!抽象泄漏!!
B
Foo(泄漏)
酒吧(漏水)
C
Foo(嘲笑)
栏(模拟)
底线,如果你很幸运并且你在 TelemetryInitializerStandard
上使用的属性是虚拟的,那么你可以模拟它们,但是,如果你可以控制代码和时间来做,我将扩展 ITelemetryInitializer
以包含所需的属性。
可以传入一个 TelemetryInitializerStandard
,但这意味着对于单元测试,您需要让 class 上的所有内容都是虚拟的,这就像尾巴摇狗一样.
将接口转换为特定 class 以利用该 class 上的功能是一种相当 strong/bad 的代码味道,应该避免。如果我们没有 ITelemetryInitializer
接口的控制权,也许我们可以 sub-class 它
public interface ITelemetryInitializerStandard : ITelemetryInitializer
{
string TenantId { get; set; }
}
并将其传递给 ABC
。