I'm working with some legacy code which I require to write some unit tests for. There is data access method with the following signature.
Task ExecuteReaderAsync(string procedureName, Parameters procedureParameters,
params Action<System.Data.IDataReader>[] actions);
which there is an implementation in the class i'm testing similar to this
private async Task<CustomObject> GetCustomObject(int id)
{
CustomObject obj = null;
await db.ExecuteReaderAsync("nameOfProcedure", some parameters,
dr =>
{
obj = new CustomObject()
{
Prop1 = dr["Col1"],
Prop2 = dr["Col2"]
}
}
return obj;
}
What I'm struggling with is being able to control the values returned by GetCustomObject. If ExecuteReaderAsync actually returned something I could have a set up like this.
mockDataAccess.Setup(x => x.ExecuteReaderAsync("nameOfProcedure", It.IsAny<Parameters>()))
.Returns(Task.FromResult(new CustomeObject() { prop1 = "abc", prop2 = "def"};));
But the logic for specifying the values is an Action<IDataReader> which I don't control. I'm wondering if there are any tricks I could employ to do what I want,
i.e. control the value of the object returned by GetCustomObject.
Take a look at the following example
[TestClass]
public class LegacyCodeTest {
[TestMethod]
public async Task TestExecuteReaderAsync() {
//Arrange
var mapping = new Dictionary<string, string> {
{ "Col1", "abc" },
{ "Col2", "def" }
};
var mockDataReader = new Mock<IDataReader>();
mockDataReader
.Setup(m => m[It.IsAny<string>()])
.Returns<string>(col => mapping[col])
.Verifiable();
var mockDataAccess = new Mock<IDataAccess>();
mockDataAccess
.Setup(m => m.ExecuteReaderAsync("nameOfProcedure", It.IsAny<Parameters>(), It.IsAny<Action<System.Data.IDataReader>[]>()))
.Returns(Task.FromResult<object>(null))
.Callback((string s, Parameters p, Action<System.Data.IDataReader>[] a) => {
if (a != null && a.Length > 0) {
a.ToList().ForEach(callback => callback(mockDataReader.Object));
}
})
.Verifiable();
var sut = new SUT(mockDataAccess.Object);
//Act
var actual = await sut.MUT(2);
//Assert
mockDataAccess.Verify();
mockDataReader.Verify(m => m["Col1"]);
mockDataReader.Verify(m => m["Col2"]);
actual.Should()
.NotBeNull()
.And
.Match<CustomObject>(c => c.Prop1 == mapping["Col1"] && c.Prop2 == mapping["Col2"]);
}
public interface IDataAccess {
Task ExecuteReaderAsync(string procedureName, Parameters procedureParameters, params Action<System.Data.IDataReader>[] actions);
}
public class Parameters { }
public class CustomObject {
public object Prop1 { get; set; }
public object Prop2 { get; set; }
}
public class SUT {
IDataAccess db;
public SUT(IDataAccess dataAccess) {
this.db = dataAccess;
}
public async Task<CustomObject> MUT(int id) {
var result = await GetCustomObject(id);
return result;
}
private async Task<CustomObject> GetCustomObject(int id) {
CustomObject obj = null;
await db.ExecuteReaderAsync("nameOfProcedure", null,
dr => {
obj = new CustomObject() {
Prop1 = dr["Col1"],
Prop2 = dr["Col2"]
};
});
return obj;
}
}
}
Since you don't have control of the Action<IDataReader>, in this case the most that can be done is to make sure that the action does not fail. So that would mean passing an a mock reader that performs as expected for the action.
var mapping = new Dictionary<string, string> {
{ "Col1", "abc" },
{ "Col2", "def" }
};
var mockDataReader = new Mock<IDataReader>();
mockDataReader
.Setup(m => m[It.IsAny<string>()])
.Returns<string>(col => mapping[col])
.Verifiable();
by using the call back to get access to the passed in parameters
.Callback((string s, Parameters p, Action<System.Data.IDataReader>[] a) => {
if (a != null && a.Length > 0) {
a.ToList().ForEach(callback => callback(mockDataReader.Object));
}
})
the mocked reader can be passed to the actions called within the method under test.
This answer is tailored to the provided example in th OP so some modification may need to be made for it to apply to your specific scenario. This should be enough to get you going for situations like this.
Related
I'm pretty new to xUnit and here's what I'd like to achieve:
[Theory]
[InlineData((Config y) => y.Param1)]
[InlineData((Config y) => y.Param2)]
public void HasConfiguration(Func<Config, string> item)
{
var configuration = serviceProvider.GetService<GenericConfig>();
var x = item(configuration.Config1); // Config1 is of type Config
Assert.True(!string.IsNullOrEmpty(x));
}
Basically, I have a GenericConfig object which contains Config and other kind of configurations, but I need to check that every single parameter is valid. Since they're all string, I wanted to simplify using [InlineData] attribute instead of writing N equals tests.
Unfortunately the error I'm getting is "Cannot convert lambda expression to type 'object[]' because it's not a delegate type", which is pretty much clear.
Do you have any idea on how to overcome this?
In addition to the already posted answers. The test cases can be simplified by directly yielding the lambdas.
public class ConfigTestDataProvider
{
public static IEnumerable<object[]> TestCases
{
get
{
yield return new object [] { (Func<Config, object>)((x) => x.Param1) };
yield return new object [] { (Func<Config, object>)((x) => x.Param2) };
}
}
}
This test ConfigTestDataProvider can then directly inject the lambdas.
[Theory]
[MemberData(nameof(ConfigTestCase.TestCases), MemberType = typeof(ConfigTestCase))]
public void Test(Func<Config, object> func)
{
var config = serviceProvider.GetService<GenericConfig>();
var result = func(config.Config1);
Assert.True(!string.IsNullOrEmpty(result));
}
Actually, I was able to find a solution which is a bit better than the one provided by Iqon (thank you!).
Apparently, the InlineData attribute only supports primitive data types. If you need more complex types, you can use the MemberData attribute to feed the unit test with data from a custom data provider.
Here's how I solved the problem:
public class ConfigTestCase
{
public static readonly IReadOnlyDictionary<string, Func<Config, string>> testCases = new Dictionary<string, Func<Config, string>>
{
{ nameof(Config.Param1), (Config x) => x.Param1 },
{ nameof(Config.Param2), (Config x) => x.Param2 }
}
.ToImmutableDictionary();
public static IEnumerable<object[]> TestCases
{
get
{
var items = new List<object[]>();
foreach (var item in testCases)
items.Add(new object[] { item.Key });
return items;
}
}
}
And here's the test method:
[Theory]
[MemberData(nameof(ConfigTestCase.TestCases), MemberType = typeof(ConfigTestCase))]
public void Test(string currentField)
{
var func = ConfigTestCase.testCases.FirstOrDefault(x => x.Key == currentField).Value;
var config = serviceProvider.GetService<GenericConfig>();
var result = func(config.Config1);
Assert.True(!string.IsNullOrEmpty(result));
}
I could maybe come up with something a bit better or cleaner, but for now it works and the code is not duplicated.
I have the problem the same to you, and I found the solution that using TheoryData class and MemberData attribute. Here is the example and I hope the code usefully:
public class FooServiceTest
{
private IFooService _fooService;
private Mock<IFooRepository> _fooRepository;
//dummy data expression
//first parameter is expression
//second parameter is expected
public static TheoryData<Expression<Func<Foo, bool>>, object> dataExpression = new TheoryData<Expression<Func<Foo, bool>>, object>()
{
{ (p) => p.FooName == "Helios", "Helios" },
{ (p) => p.FooDescription == "Helios" && p.FooId == 1, "Helios" },
{ (p) => p.FooId == 2, "Poseidon" },
};
//dummy data source
public static List<Foo> DataTest = new List<Foo>
{
new Foo() { FooId = 1, FooName = "Helios", FooDescription = "Helios Description" },
new Foo() { FooId = 2, FooName = "Poseidon", FooDescription = "Poseidon Description" },
};
//constructor
public FooServiceTest()
{
this._fooRepository = new Mock<IFooRepository>();
this._fooService = new FooService(this._fooRepository.Object);
}
[Theory]
[MemberData(nameof(dataExpression))]
public void Find_Test(Expression<Func<Foo, bool>> expression, object expected)
{
this._fooRepository.Setup(setup => setup.FindAsync(It.IsAny<Expression<Func<Foo, bool>>>()))
.ReturnsAsync(DataTest.Where(expression.Compile()));
var actual = this._fooService.FindAsync(expression).Result;
Assert.Equal(expected, actual.FooName);
}
}
Oddly delegates are not objects, but Actions or Funcs are. To do this, you have to cast the lambda to one of these types.
object o = (Func<Config, string>)((Config y) => y.Param1)
But doing this, your expression is not constant anymore. So this will prevent usage in an Attribute.
There is no way of passing lambdas as attributes.
One possible solution would be to use function calls, instead of attributes. Not as pretty, but could solve your problem without duplicate code:
private void HasConfiguration(Func<Config, string> item)
{
var configuration = serviceProvider.GetService<GenericConfig>();
var x = item(configuration.Config1); // Config1 is of type Config
Assert.True(!string.IsNullOrEmpty(x));
}
[Theory]
public Test1()
{
HasConfiguration((Config y) => y.Param1);
}
[Theory]
public Test2()
{
HasConfiguration((Config y) => y.Param2);
}
public class HrcpDbTests
{
[Theory]
[MemberData(nameof(TestData))]
public void Test(Expression<Func<bool>> exp)
{
// Arrange
// Act
// Assert
}
public static IEnumerable<object[]> TestData
{
get
{
Expression<Func<bool>> mockExp1 = () => 1 == 0;
Expression<Func<bool>> mockExp2 = () => 1 != 2;
return new List<object[]>
{
new object[]
{
mockExp1
},
new object[]
{
mockExp2
}
}
}
}
}
I have a class that receives an ICacheManager via injection:
public ClientIdToClientNameResolver(IDataManager<Client> clientManager, ICacheManager cacheManager, ILogUtils logUtils)
{
this.clientManager = clientManager;
this.cacheManager = cacheManager;
this.logUtils = logUtils;
}
There is a line of code that then executes the Get method on it:
var client = cacheManager
.Get(CacheKeys.Clients, () => clientManager.Get())
.FirstOrDefault(x => x.Id == clientId);
The signature for that method is:
TResult Get<TResult>(string key, Func<TResult> defaultValue = null)
That specific execution of Get has a TResult of List<Client> and so I'm mocking it up like this:
var cacheManagerMock = new Mock<ICacheManager>();
cacheManagerMock.Setup(m => m.Get(It.IsAny<string>(), It.IsAny<Func<List<Client>>>()))
.Returns(new List<Client>());
However, when executing the test, the call to:
var client = cacheManager
.Get(CacheKeys.Clients, () => clientManager.Get())
is returning null. I know that because in a previous iteration I broke that statement out on its own. What did I do wrong with the Mock?
The following [mcve] was used to try and reproduce the issue based on the original example provided.
[TestClass]
public class UnitTest4 {
[TestMethod]
public void TestMethod1() {
//Arrange
var clients = new List<Client>(){
new Client { Id = 1 }
};
var cacheManagerMock = new Mock<ICacheManager>();
cacheManagerMock
.Setup(m => m.Get(It.IsAny<string>(), It.IsAny<Func<List<Client>>>()))
.Returns(clients);
var clientId = 1;
var clientManager = Mock.Of<IDataManager<Client>>();
var cacheManager = cacheManagerMock.Object;
//Act
var client = cacheManager
.Get(CacheKeys.Clients, () => clientManager.Get())
.FirstOrDefault(x => x.Id == clientId);
//Assert
Assert.IsNotNull(client);
}
public class Client { public int Id { get; set; } }
public interface ICacheManager {
TResult Get<TResult>(string key, Func<TResult> defaultValue = null);
}
public interface IDataManager<T> {
List<T> Get();
}
public class CacheKeys {
public const string Clients = "FakeKey";
}
}
However the test passes as expected.
I am trying to write code that receives an argument list and gets the ConstructorInfo of a matching Ctor.
The method signature is ConstructorInfo GetConstructorInfo(Type type, object[] args).
I have created a class to work with:
public class ClassWithParamsInCtor
{
public ClassWithParamsInCtor(params int[] parameters)
{
}
}
Using the Activator class I can create instances of this object:
ClassWithParamsInCtor myclass = Activator.CreateInstance(typeof(ClassWithParamsInCtor), new object[] { 1,2 }) as ClassWithParamsInCtor; \\returns a valid instance of the class;
But when I try to get the ConstructorInfo there is an issue, the following returns null:
ConstructorInfo ctorInfo = typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int), typeof(int) }); \\returns null
How can I obtain the ConstructorInfo in such a case ?
params int[] is a syntactic sugar for int[], you need to use typeof(int[])
typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int[]) });
Try this,
ConstructorInfo ctorInfo = typeof(ClassWithParamsInCtor).GetConstructor(new Type[] { typeof(int[])});
Hope helps,
I think that I have a solution for your problem if I understood it right.
Assuming you have the following class :
public class ClassWithParamsInCtor
{
public ClassWithParamsInCtor(params int[] parameters)
{
}
public ClassWithParamsInCtor(int[] parameters, double y)
{
}
public ClassWithParamsInCtor(int[] parameters, float y)
{
}
}
Here are some Unit Tests that describe the solution that I've implemented :
[TestClass]
public class TestClassWithParamsInCtorClass
{
[TestMethod]
//Wrong data type test
public void WrongInputDataTypesTest()
{
//Arrange
var inputData = new object[] { new[] { 1, 2 }, "" };
var inputDataTypes = inputData.Select(_ => _.GetType());
//Act
var matchedCtorParams = typeof(ClassWithParamsInCtor)
.GetConstructors()
.Select(_ => _.GetParameters().Select(a => a.ParameterType))
.Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();
//Assert
Assert.AreEqual(null, matchedCtorParams.FirstOrDefault());
}
[TestMethod]
//Test used to invoke first constructor
public void InputDataTypesTest1()
{
//Arrange
var inputData = new object[] { new[] { 1, 2 } , 1 };
var inputDataTypes = inputData.Select(_ => _.GetType());
//Act
var matchedCtorParams = typeof(ClassWithParamsInCtor)
.GetConstructors()
.Select(_ => _.GetParameters().Select(a => a.ParameterType))
.Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();
var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
.Invoke(inputData);
//Assert
Assert.AreNotEqual(null, result);
}
[TestMethod]
//Test used to invoke second constructor
public void InputDataTypesTest2()
{
//Arrange
var inputData = new object[] { new[] { 1, 2 }, 1.2 };
var inputDataTypes = inputData.Select(_ => _.GetType());
//Act
var matchedCtorParams = typeof(ClassWithParamsInCtor)
.GetConstructors()
.Select(_ => _.GetParameters().Select(a => a.ParameterType))
.Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();
var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
.Invoke(inputData);
//Assert
Assert.AreNotEqual(null, result);
}
[TestMethod]
//Test used to invoke third constructor
public void InputDataTypesTest3()
{
//Arrange
var inputData = new object[] { new[] { 1, 2 }, 3.5F };
var inputDataTypes = inputData.Select(_ => _.GetType());
//Act
var matchedCtorParams = typeof(ClassWithParamsInCtor)
.GetConstructors()
.Select(_ => _.GetParameters().Select(a => a.ParameterType))
.Where(_ => _.HaveSameItems(inputDataTypes)).ToArray();
var result = typeof(ClassWithParamsInCtor).GetConstructor(matchedCtorParams.FirstOrDefault().ToArray())
.Invoke(inputData);
//Assert
Assert.AreNotEqual(null, result);
}
}
And the extension method that I used :
public static bool HaveSameItems<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
var dictionary = a.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
foreach (var item in b)
{
int value;
if (!dictionary.TryGetValue(item, out value))
{
return false;
}
if (value == 0)
{
return false;
}
dictionary[item] -= 1;
}
return dictionary.All(x => x.Value == 0);
}
I'm pretty sure that the Unit tests can be implemented to be more granular specific, but considering this as a proofe of concept, I find them decent.
I'm trying to mock this method
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc)
like this
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, object>>())).ReturnsAsync(new { isPair = false });
The method to test doing the call passing an anonymous type to the generic parameter like this
instance.GetResultAsync(u => new {isPair = u == "something" }) //dont look at the function return because as generic could have diferent implementations in many case
Moq never matches my GetResultAsync method with the parameters sent.
I'm using Moq 4
The anonymous type is going to cause you problems. You need a concrete type for this to work.
The following example worked when I changed
instance.GetResultAsync(u => new {isPair = u == "something" })
to
instance.GetResultAsync(u => (object) new {isPair = u == "something" })
Moq is unable to match the anonymous type and that is why you get null when called.
[TestClass]
public class MoqUnitTest {
[TestMethod]
public async Task Moq_Function_With_Anonymous_Type() {
//Arrange
var expected = new { isPair = false };
var iMock = new Mock<IService>();
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, object>>()))
.ReturnsAsync(expected);
var consumer = new Consumer(iMock.Object);
//Act
var actual = await consumer.Act();
//Assert
Assert.AreEqual(expected, actual);
}
public interface IService {
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc);
}
public class Consumer {
private IService instance;
public Consumer(IService service) {
this.instance = service;
}
public async Task<object> Act() {
var result = await instance.GetResultAsync(u => (object)new { isPair = u == "something" });
return result;
}
}
}
if the code calling the GetResultAsync is dependent on using the anonymous type then what you are trying to do with your test wont work with your current setup. You would probably need to provide a concrete type to the method.
[TestClass]
public class MoqUnitTest {
[TestMethod]
public async Task Moq_Function_With_Concrete_Type() {
//Arrange
var expected = new ConcreteType { isPair = false };
var iMock = new Mock<IService>();
iMock.Setup(m => m.GetResultAsync(It.IsAny<Func<string, ConcreteType>>()))
.ReturnsAsync(expected);
var sut = new SystemUnderTest(iMock.Object);
//Act
var actual = await sut.MethodUnderTest();
//Assert
Assert.AreEqual(expected, actual);
}
class ConcreteType {
public bool isPair { get; set; }
}
public interface IService {
Task<TResult> GetResultAsync<TResult>(Func<string, TResult> transformFunc);
}
public class SystemUnderTest {
private IService instance;
public SystemUnderTest(IService service) {
this.instance = service;
}
public async Task<object> MethodUnderTest() {
var result = await instance.GetResultAsync(u => new ConcreteType { isPair = u == "something" });
return result;
}
}
}
I am trying to unit test a method that returns a Task>:
void Main()
{
var mockRepo = new Mock<IRepository>();
mockRepo.Setup(x => x.GetAll()).Returns(new List<MyModel>() { new MyModel { Name = "Test" } }); // works
mockRepo.Setup(x => x.GetAllAsync()).Returns(Task.FromResult(new List<MyModel>() { new MyModel { Name = "Test" } })); // error
var result = mockRepo.Object.GetAll();
result.Dump();
}
public interface IRepository
{
Task<IList<MyModel>> GetAllAsync();
IList<MyModel> GetAll();
}
public class MyModel
{
public string Name { get; set; }
}
But the Task returning method generates a compiler error:
CS1503 Argument 1: cannot convert from
'System.Threading.Tasks.Task<System.Collections.Generic.List<UserQuery.MyModel>'
to
'System.Threading.Tasks.Task<System.Collections.Generic.IList<UserQuery.MyModel>'
What am I doing wrong?
You can use ReturnsAync method:
IList<MyModel> expected = new List<MyModel>() { new MyModel { Name = "Test" }};
mockRepo.Setup(x => x.GetAll()).ReturnsAsync(expected);
Posted to soon, found out that Moq has the ReturnsAsync method.
mockRepo.Setup(x => x.GetAllAsync()).ReturnsAsync((new List<MyModel>() { new MyModel { Name = "Test" } });
Works fine.
Just cast your list to IList:
Task.FromResult((IList<MyModel>) new List<MyModel>() { new MyModel { Name = "Test" } })
Or define the generic param:
Task.FromResult<IList<MyModel>>(new List<MyModel>() { new MyModel { Name = "Test" } })
Cast the result to IList
Task.FromResult((IList)new List() { new MyModel { Name = "Test" } });
Well your problem is that your method should be returning Task<IList> and You are returning Task<List> you can't do that - they are of different types and they do not support contravariance or covariance. You need to cast your return type
mockRepo.Setup(x => x.GetAllAsync()).Returns(Task.FromResult((IList<MyModel>)new List<MyModel>() { new MyModel { Name = "Test" } }));