访问moq对象时如何返回null?

时间:2012-07-16 10:32:53

标签: c# unit-testing moq

我正在使用Moq库进行单元测试。现在我想要的是当我第一次访问我的对象时它应该返回null,当我第二次访问它时它应该返回其他东西。

这是我的代码

var mock = new Mock<IMyClass>();
mock.Setup(?????);
mock.Setup(?????);

var actual = target.Method(mock.object);

在我的方法中,我首先检查mock对象是否为null,如果为null,则进行初始化,然后对其进行一些调用。

bool Method(IMyClass myObj)
{
    if (myObj != null)
        return true;
    else
    {
        myObj = new MyClass();
        bool result = myObj.SomeFunctionReturningBool();
        return result;
    }
}

如何为模拟对象设置,

此外,我需要知道如何模拟这一行

bool result = myObj.SomeFunctionReturningBool();

5 个答案:

答案 0 :(得分:2)

听起来你正试图用一种测试方法运行两个测试 - 也许最好将测试分成两个?

如果方法传递null,您还希望初始化一个新对象。为了测试这个,我建议创建一个负责创建MyClass实例的工厂对象。新代码如下:

interface IMyClassFactory
{
    IMyClass CreateMyClass();
}

bool Method(IMyClass myObj, IMyClassFactory myClassFactory)
{
    if (myObj != null)
    {
        return true;
    }

    myObj = myClassFactory.CreateMyClass();
    return myObj.SomeFunctionReturningBool();
}

然后测试看起来像:

[Test]
public void Method_ShouldReturnTrueIfNotPassedNull()
{
    Assert.That(target.Method(new MyClass()), Is.True);
}

[Test]
public void Method_ShouldCreateObjectAndReturnResultOfSomeFunctionIfPassedNull()
{
    // Arrange
    bool expectedResult = false;

    var mockMyClass = new Mock<IMyClass>();
    mockMyClass.Setup(x => x.SomeFunctionReturningBool()).Returns(expectedResult);

    var mockMyFactory = new Mock<IMyClassFactory>();
    mockMyFactory.Setup(x => x.CreateMyClass()).Returns(mockMyClass.Object);

    // Act
    var result = target.Method(null, mockMyFactory.Object);

    // Assert
    mockMyClass.Verify(x => x.SomeFunctionReturningBool(), Times.Once());
    mockMyFactory.Verify(x => x.CreateMyClass(), Times.Once());
    Assert.That(result, Is.EqualTo(expectedResult));
}

此处factory pattern已用于传入可创建IMyClass类型对象的对象,然后工厂本身已被模拟。

如果您不想更改方法的签名,则在类的构造函数中创建工厂,并通过类的公共属性访问它。然后它可以在模拟工厂的测试中被覆盖。这称为dependency injection

答案 1 :(得分:0)

要模拟结果值,您可以执行以下操作:

mock.Setup(foo => foo.SomeFunctionReturningBool()).Returns(true); // or false :)

对于另一个问题,只需在单元测试中传递null,而不是传递mock.object,你的单元测试也是如此。所以你基本上创建了两个单元测试:

var actual = target.Method(mock.object);

和另一个:

var actual = target.Method(null);

答案 2 :(得分:0)

您可以将TestFixture与参数一起使用。这个测试将运行两次和不同的类型值。

using NUnit.Framework;

namespace Project.Tests
{
    [TestFixture(1)] 
    [TestFixture(2)]
    public class MyTest
    {
        private int _intType;

         public MyTest(int type)
         {
             _intType = type;
         }

        [SetUp]
        public void Setup()
        {
            if (_intType==1)
            {
                //Mock Return false
            }
            else
            {
                //Mock Return Value
            }
        }
    }
}

答案 3 :(得分:0)

目前,您的SUT与MyClass实施紧密结合。您无法模拟在SUT中使用new关键字实例化的对象。因此,您无法单独测试您的SUT,您的测试不再是单元测试 。当MyClass.SomeFunctionReturningBool的实施发生变化时(它将返回true而不是false),您的SUT测试将失败。这不应该发生。因此,将创建委托给某个依赖项(工厂)并将该依赖项注入您的SUT:

[Test]
public void ShouldReturnTrueWhenMyClassIsNotNull()
{
    Mock<IMyClassFactory> factory = new Mock<IMyClassFactory>();
    Mock<IMyClass> myClass = new Mock<IMyClass>();
    var foo = new Foo(factory.Object);
    Assert.True(foo.Method(myClass.Object));
}

[Test]
public void ShouldCreateNewMyClassAndReturnSomeFunctionValue()
{
    bool expected = true;
    Mock<IMyClass> myClass = new Mock<IMyClass>();
    myClass.Setup(mc => mc.SomeFunctionReturningBool()).Returns(expected);
    Mock<IMyClassFactory> factory = new Mock<IMyClassFactory>();
    factory.Setup(f => f.CreateMyClass()).Returns(myClass.Object);

    var foo = new Foo(factory.Object);

    Assert.That(foo.Method(null), Is.EqualTo(expected));
    factory.VerifyAll();
    myClass.VerifyAll();
}

BTW赋值方法参数的新值不会影响您传递给方法的引用。

实现:

public class Foo
{
    private IMyClassFactory _factory;

    public Foo(IMyClassFactory factory)
    {
        _factory = factory;
    }

    public bool Method(IMyClass myObj)
    {
        if (myObj != null)
            return true;       

        return _factory.CreateMyClass().SomeFunctionReturningBool();
    }
}

答案 4 :(得分:0)

Moq - 返回null - 这个工作示例简单地说明了如何使用Moq返回null。虽然需要的代码行是下面的注释行,但下面提供了一个完整的工作示例。

// _mockShopService.Setup(x => x.GetProduct(It.IsAny<string>())).Returns(() => null);

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
}

public interface IShopService
{
    Product GetProduct(string productId);
}

public class ShopService : IShopService
{
    public Product GetProduct(string productId)
    {
        if (string.IsNullOrWhiteSpace(productId))
        {
            return new Product();
        }

        return new Product { Id = "8160807887984", Name = "How to return null in Moq" };
    }
}

public class Shop
{
    private static IShopService _shopService;

    public Shop(IShopService shopService)
    {
        _shopService = shopService;
    }

    public Product GetProduct(string productId)
    {
        Product product = _shopService.GetProduct(productId);

        return product;
    }
}

[TestClass]
public class ShopTests
{
    Mock<IShopService> _mockShopService;

    [TestInitialize]
    public void Setup()
    {
        _mockShopService = new Mock<IShopService>();
    }

    [TestMethod]
    public void ShopService_GetProduct_Returns_null()
    {
        //Arrange
        Shop shop = new Shop(_mockShopService.Object);

        //This is how we return null --- all other code above is to bring this line of code home
        _mockShopService.Setup(x => x.GetProduct(It.IsAny<string>())).Returns(() => null);

        //Act
        var actual = shop.GetProduct(It.IsAny<string>());

        //Assert

        Assert.IsNull(actual);
    }
}