简体   繁体   English

如何使用 Moq 对服务方法进行单元测试

[英]How to unit test service method with Moq

I tried to write unit test for getMark() and faced problem with Moq , with which I'm not familiar.我尝试为getMark()编写单元测试并遇到Moq问题,我对此并不熟悉。 I have no idea what method and object properly mock in order to unit test getMark()我不知道什么方法和 object 正确地模拟以便单元测试getMark()

Here is my MarkServiceClass containing getMark()这是我的MarkServiceClass包含getMark()

public class MarkService : IMarkService
{
    IMarkService _markService;
    IStdService _stdService;
    IStdService _stdMService;
    RClass cs;

public MarkService(IMarkService markService, IStdService stdService, IStdService stdMService)
    {
        _markService = markService;
        _stdService = stdService;
        _stdMService = stdMService;
    }

public bool Init(int sID, string pID, string year)
{
    try
    {
        cs = new RClass ();

        cs.sLevel = __stdService.GetAsIQueryable().FirstOrDefault(x => x.UID == pID);

        var mInfo = __stdMService.GetSTDM(sID, pID, year);
        cs.Type = mInfo.CalculateAmount;
        
        return true; 
    }
    catch 
    {
        return false; 
    }
}

public MarkVM getMark(int sID, string pID, string year)
{
    var output=Init(sID, pID, year);
    
    if (!output)
        return null; 
    
    int sGrade= 0;
    int sMark= 0;
    
    //here are conditions where sGrade and sMark used

    return new MarkVM
    {
        Grade = sGrade,
        Mark = sMark
    };
}
}

and MarkVMMarkVM

public class MarkVM
    {
        public int Grade { get; set; }
        public int Mark { get; set; }
    }

The code you shared is not complete so I had to make some assumptions to give you an example how to unit test getMark您共享的代码不完整,因此我不得不做出一些假设来举例说明如何对 getMark 进行单元测试

public class MarkVM
{
    public int Grade { get; set; }
    public int Mark { get; set; }
}

Not knowing what RClass is, I define it with minimal requirements不知道 RClass 是什么,我用最小的要求定义它

public class RClass {
    public String Uid { get; set; }
    public string sLevel { get; set; }
    public int Type { get; set; }
}

Same for this Info your service retrieves with GetSTDM您的服务使用 GetSTDM 检索的此信息相同

public class Info
{
    public int CalculateAmount { get; set; }
}

Now come the interfaces.现在来接口。 This is definitely required if you want to mock如果你想模拟,这绝对是必需的

public interface IStdService
{
    List<RClass> GetAsIQueryable();
    Info GetSTDM(int sID, string pID, string year);
}

Those 2 methods are the ones you'll want to mock if you unit test getMark.如果您对 getMark 进行单元测试,这 2 个方法就是您想要模拟的方法。 Mocking getMark itself will only allow you to check it is called, but not its behavior which is the purpose of unit testing. Mocking getMark 本身只会让你检查它被调用,而不是它的行为,这是单元测试的目的。

Now the main class. I removed the injection of IMarkService in the constructor because I really don't see why you would do that: Markservice implements IMarkService here.现在是主要的 class。我在构造函数中删除了 IMarkService 的注入,因为我真的不明白你为什么要这样做:Markservice 在这里实现 IMarkService。 For any reason you use 2 instances of IStdService, I kelpt that but then you need to inject it too.出于任何原因,您使用了 2 个 IStdService 实例,我认为是这样,但您也需要注入它。

public class MarkService : IMarkService
{
    private IStdService __stdService;
    private IStdService __stdMService;
    public RClass cs;

    public MarkService(IStdService stdMService, IStdService stdService)
    {
        __stdMService = stdMService;
        __stdService = stdService;
    }

    public bool Init(int sID, string pID, string year)
    {
        try
        {
            cs = new RClass();

            cs.sLevel = __stdService.GetAsIQueryable().FirstOrDefault(x => x.Uid == pID).sLevel;

            var mInfo = __stdMService.GetSTDM(sID, pID, year);
            cs.Type = mInfo.CalculateAmount;

            return true;
        }
        catch
        {
            return false;
        }
    }

    public MarkVM getMark(int sID, string pID, string year)
    {
        var output = Init(sID, pID, year);

        if (!output)
            return null;


        int sGrade = 0;
        int sMark = 0;

        //here are conditions where sGrade and sMark used

        return new MarkVM
        {
            Grade = sGrade,
            Mark = sMark
        };
    }
}

Now comes the test.考验来了。 If you want to unit test getMark you could either mock Init from IMarkService, or consider the behavior comes from this Init and then you want to mock GetAsIQueryable and GetSTDM.如果你想对 getMark 进行单元测试,你可以从 IMarkService 模拟 Init,或者考虑行为来自这个 Init,然后你想模拟 GetAsIQueryable 和 GetSTDM。 I made the assumption second option is what you want.我假设第二个选项是你想要的。

using System.Collections.Generic;
using MarkServiceNS;
using Moq;// Moq framework where you'll find everything you need
using NUnit.Framework;// Using NUnit for unit test. Because I like it :-)
using NUnit.Framework.Constraints;

namespace UnitTestWithMoqExample
{
    public class Tests
    {
        [SetUp]
        public void Setup()
        {
        }

        [Test]
        public void getMark()
        {
            var mockedStdService = new Mock<IStdService>();
            mockedStdService.Setup(x => x.GetAsIQueryable())
                .Returns(new List<RClass> { new RClass { Uid = "uid", sLevel = "expected", Type = 1 } }); //  Here you define what it the mocked result of GetAsIQueryable call. 

            var mockedStdMService = new Mock<IStdService>();
            mockedStdMService.Setup(x => x.GetSTDM(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
                .Returns(new Info { CalculateAmount = 1 });// Same here. You mock GetSTDM. The method parameters are not expected to change the behavior in my unit test, this is why I consider It.Any<T> so whatever you pass to the mock, the result will be the same.
            // Here is the assertion. This should do the job
            var service = new MarkServiceNS.MarkService(mockedStdMService.Object, mockedStdService.Object);
            Assert.IsNotNull(service.getMark(1, "", ""));
            Assert.IsInstanceOf(typeof(MarkVM), service.getMark(1, "", ""));
            Assert.AreEqual(0, service.getMark(1, "", "").Grade);
            Assert.AreEqual(0, service.getMark(1, "", "").Mark);

        }
    }
}

A basic Moq coding will be like this一个基本的Moq量编码将是这样的

[Test]
public void Test1()
{
    var mock = new Mock<IMarkService>();
    mock.Setup(p => p.getMark(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>())).Returns(new MarkVM());
    mock.Verify(p => p.getMark(1001, "P001", "2022"), Times.Once());
}

I am posting this as an example as I don't have your full code我将此作为示例发布,因为我没有您的完整代码

Use the above technique to moq your methods GetAsIQueryable and GetSTDM and CalculateAmount使用上述技术来最小化您的方法GetAsIQueryableGetSTDM以及CalculateAmount

And call the method Init and then call the getMark并调用Init方法,然后调用getMark

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM