简体   繁体   中英

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. I have no idea what method and object properly mock in order to unit test getMark()

Here is my MarkServiceClass containing 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 MarkVM

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

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

Not knowing what RClass is, I define it with minimal requirements

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

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. Mocking getMark itself will only allow you to check it is called, but not its behavior which is the purpose of unit testing.

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. For any reason you use 2 instances of IStdService, I kelpt that but then you need to inject it too.

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. 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

[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

And call the method Init and then call the getMark

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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