I am trying to init a list of objects (it doesn't metter which object- they all give the same error), so for the example I am using Strings:
String str = 'str';
String str2 = 'str2';
List<String> strList = ["str", "str2"];
List<String> strList2 = [str, str2];
strList2.add(str);
strList is working fine, but I want to add objects from a server response, so I need to add vars, why does the strList2.add is telling me "he name of a constructor must match the name of the enclosing class." and "Try renaming one of the constructors."
I am new to flutter so its weird for me,
EDIT:
my test Class:
class test extends StatefulWidget {
#override
_testState createState() => _testState();
}
class _testState extends State<test> {
String str = 'str';
String str2 = 'str2';
List<String> strList = ["str", "str2"];
List<String> strList2 = [str, str2];
strList2.add(str);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('testClass'),
);
}
}
thanks!
You can't use add() in the attributes definition. You need to use initState() like this :
class _testState extends State<test> {
String str = 'str';
String str2 = 'str2';
List<String> strList = ["str", "str2"];
List<String> strList2 = [str, str2];
#override
initState() {
super.initState();
strList2.add(str);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('testClass'),
);
}
This code looks fine. The error message indicates that one of your class has a typo in it's constructor, hence the mismatch - for example:
class MySpecialWidget extends StatefulWidget {
const MySpecialWidgetXXX({Key key) : super(key: key); // <--- this produces the error you see
#override
_CreateMySpecialWidgetState createState() => _CreateMySpecialWidgetState();
}
If the error is really on the .add line, then my take is that you have a closing bracket problem somewhere maybe.
Related
I'm trying to create a graph and i don't know how to set values on this graph (I know how to set the values manually but i created a field in firestore that receives the value of x and y of the graph when you click on a button ), i'm using SyncFusion Flutter Chart (if you have any another idea to solve my problem feel free to say) and i'm trying to add the values ( String ) to the list and for that i Created in firestore when you click on the button (ChartData(x,y)) so all i need is add this string to the list of type ChartData, and for that i'm trying to use decode and split but now returned this error and i don't know how to solve. i'm sorry for the long text.
List<ChartData> convert(String input) {
List<ChartData> output;
try {
output = json.decode(input);
return output;
} catch (err) {
print('The input is not a string representation of a list');
return [ChartData(0, 0)];
}
}
List<ChartData> testim = testefinal.split(',');
final List<ChartData> list1 = convert(testefinal);
final List<ChartData> chartData = list1;
Error: A value of type 'List<String>' can't be assigned to a variable of type 'List<ChartData>'.
Full Code:
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:mood3/telas/animacao.dart';
import 'package:ntp/ntp.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import '../model/GraphMood.dart';
class Graph extends StatefulWidget {
#override
State<Graph> createState() => _GraphState();
}
class _GraphState extends State<Graph> {
TooltipBehavior? _tooltipBehavior;
DateTime ntpTime = DateTime.now();
String testefinal = '';
_loadNTPTime() async {
FirebaseAuth auth = FirebaseAuth.instance;
FirebaseFirestore db = FirebaseFirestore.instance;
setState(() async {
ntpTime = await NTP.now();
});
}
Future _recuperarNome() async {
FirebaseAuth auth2 = FirebaseAuth.instance;
User? user = auth2.currentUser;
DocumentSnapshot ds = await db.collection('usuarios')
.doc(user!.uid)
.get();
Map<String, dynamic> dss = ds.data() as Map<String, dynamic>;
setState((){
testefinal = dss["moodGraph"];
});
print(testefinal);
}
FirebaseFirestore db = FirebaseFirestore.instance;
#override
initState(){
_recuperarNome();
_tooltipBehavior = TooltipBehavior(enable: true);
super.initState();
_loadNTPTime();
}
#override
Widget build(BuildContext context) {
List<ChartData> convert(String input) {
List<ChartData> output;
try {
output = json.decode(input);
return output;
} catch (err) {
print('The input is not a string representation of a list');
return [ChartData(0, 0)];
}
}
List<ChartData> testim = testefinal.split(',');
final List<ChartData> list1 = convert(testefinal);
final List<ChartData> chartData = list1;
return Scaffold(
body: Center(
child: Container(
child: SfCartesianChart(
primaryXAxis: CategoryAxis(),
// Chart title
title: ChartTitle(text: 'Mood Chart'),
// Enable legend
legend: Legend(isVisible: true),
// Enable tooltip
tooltipBehavior: _tooltipBehavior,
series: <ChartSeries>[
// Renders line chart
LineSeries<ChartData, int>(
dataSource: chartData,
xValueMapper: (ChartData data, _) => data.x.toInt(),
yValueMapper: (ChartData data, _) => data.y
)
]
)
)
)
);
}
}
class ChartData {
ChartData(this.x, this.y);
final double x;
final double y;
}
I think, output = json.decode(input) as List();
I have the a reducer class that I wanted to write test cases:
Reduce class:
public class MyReducer extends Reducer<Text, Text, NullWritable, Text> {
private static final Logger LOG = LogManager.getLogger(MyReducer.class);
public static List<String> l1 = new ArrayList<String>();
String id = null;
private MultipleOutputs<NullWritable, Text> mos;
#Override
public void setup(final Context context) throws IOException, InterruptedException {
mos = new MultipleOutputs<NullWritable, Text>(context);
final Path[] uris = DistributedCache.getLocalCacheFiles(context.getConfiguration());
try {
final BufferedReader readBuffer1 = new BufferedReader(new FileReader(uris[0].toString()));
String line;
while ((line = readBuffer1.readLine()) != null) {
l1.add(line);
}
readBuffer1.close();
} catch (Exception e) {
LOG.error(e);
}
}
public void reduce(final Text key, final Iterable<Text> values, final Context context)
throws IOException, InterruptedException {
final String[] key1 = key.toString().split("-");
final String keyA = key1[10];
final String date = key1[1];
/* Some condition check */
mos.write(NullWritable.get(), new Text(inputEventValue), keyA + "//date=" +
date.substring(0, 4) + "-" + date.substring(4, 6));
}
#Override
public void cleanup(final Context context) throws IOException, InterruptedException {
mos.close();
}
}
Test Case looks like :
#RunWith(MockitoJUnitRunner.class)
public class MyTest {
#Mock
private MyReducer.Context mockContext;
MyReducer reducer;
MultipleOutputs<NullWritable, Text> mos;
#Before
public void setUp() {
reducer = new MyReducer();
}
#Test
public void myReducerTest() throws Exception {
MyReducer spy = PowerMockito.spy(new MyReducer());
doNothing().when(spy).setup(mockContext);
mos = new MultipleOutputs<NullWritable, Text>(mockContext);
List<Text> sline = new ArrayList<>() ;
List<String> l1 = new ArrayList<String>();
l1.add(“1234”);
sline.add(new Text(“xyz”));
Whitebox.setInternalState(MyReducer.class,”l1", l1);
Whitebox.setInternalState(MyReducer.class,"mos",mos);
reducer.reduce(new Text(“xyz-20200101-1234),sline,mockContext);
}
#After
public void tearDown() throws Exception {
/*
* this will do the clean up part
*/
verifyNoMoreInteractions(mockContext);
}
When running in Debug mode it goes to the reducer's reduce method and fails with NullPointerException where mos write statement is?
Complete Stack trace:
java.lang.NullPointerException
at org.apache.hadoop.mapreduce.lib.output.MultipleOutputs.getNamedOutputsList(MultipleOutputs.java:196)
at org.apache.hadoop.mapreduce.lib.output.MultipleOutputs.<init>(MultipleOutputs.java:324)
at MyTest.myeducerTest
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:118)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:53)
Mocking mos errors as mos is not a static.
Any suggestion.
Junit - ReduceDriver, withInput, withOutput,testRun doesn't work.
Thanks.
I tried mocking Multiple outputs as suggested:
import org.apache.hadoop.mapreduce.lib.output.MultipleOutputs;
#Mock
private MyReducer.Context mockContext;
List<String> namedOut = new ArrayList<>();
namedOut.add("NM1");
namedOut.add("NM2");
MultipleOutputs spy = PowerMockito.spy(new MultipleOutputs<>(mockContext));
when(spy, "getNamedOutputsList(mockContext)").thenReturn(namedOut);
But this gives me error : org.powermock.reflect.exceptions.MethodNotFoundException: no method found with name 'getNamedOutputsList(() anyObject())' with parameter types : [] in class org.apache.hadoop.mapreduce.lib.output.MultipleOutputs.
Looks like you did not define what mockContext.getContext() should return for your test, so it returns null and fails.
Based on this sourcecode the methods looks like this (so you might use a different version):
private static List<String> getNamedOutputsList(JobContext job) {
List<String> names = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(
job.getConfiguration().get(MULTIPLE_OUTPUTS, ""), " ");
while (st.hasMoreTokens()) {
names.add(st.nextToken());
}
return names;
}
JobContext seems to refer to your mock Reducer.Context mockContext, so you need to define the appropriate behaviour so that it returns what it is supposed to return.
Note that this call originates from the constructor of MultipleOutputs.
Also take note of the static getCountersEnabled method that is invoked from the constructor and interacts with the context.
Mocking mos errors as mos is not a static.
You could probably use reflections to put a mocked version of mos into your MyReducer class.
Check here for some example on how to mock a private static field.
Edit:
If you try to mock the conig do it like this:
Configuration config = Mockito.mock(Configuration.class);
when(mockContext.getConfiguration()).thenReturn(config);
As far as I see the get that are invoked on the configuration object always provide a default value, so it shouldn't matter if the key/value pair is in there or not.
I'm trying to compare faces with AWS rekognition API. but somehow I'm getting "broken pipe" error all the time. There is no problem on aws keys and photos. I'm trying to get more info from http.post but It just says "broken pipe", it doesn't give any detail, unfortunately.
Scenario;
User takes 2 photos (working)
on second taken, I will parse images to bytes (working)
send bytes with standard request to aws API (doesn't work)
I changed the image quality to the lowest as well, but It didn't help.
Main.dart code
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'testa.dart';
import 'package:path_provider/path_provider.dart';
List<CameraDescription> cameras;
Future<void> main() async {
cameras = await availableCameras();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
CameraController controller;
#override
void initState() {
super.initState();
controller = CameraController(cameras[0], ResolutionPreset.low);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
Future<String> _checkImage(String filePath, String secondPath) async {
File sourceImagefile, targetImagefile; //load source and target images in those File objects
String accessKey, secretKey, region ; //load your aws account info in those variables
print(filePath);
print(secondPath);
targetImagefile = File(filePath);
sourceImagefile = File(secondPath);
print(targetImagefile.existsSync());
print(sourceImagefile.existsSync());
accessKey = '';
secretKey = '';
region = 'eu-west-1';
RekognitionHandler rekognition = new RekognitionHandler(accessKey, secretKey, region);
String labelsArray = await rekognition.compareFaces(sourceImagefile, targetImagefile);
return labelsArray;
}
Widget cameraPart() {
if (!controller.value.isInitialized) {
return Container();
}
return AspectRatio(
aspectRatio:
controller.value.aspectRatio,
child: CameraPreview(controller));
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: cameraPart(),
floatingActionButton: FloatingActionButton(
onPressed: takePhoto,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Future<String> get360PhotoFolder() async {
final Directory appFolder = await getAppFolder();
final String dirPath = '${appFolder.path}/photos360';
await Directory(dirPath).create(recursive: true);
return dirPath;
}
String firstPath = '';
String secondPath = '';
Future<bool> takePhoto() async {
final String dirPath = await get360PhotoFolder();
final String filePath = '$dirPath/${timestamp()}_test.jpg';
try {
debugPrint('photo taken - $filePath');
await controller.takePicture(filePath);
setState(() {
if (firstPath == '') {
print('a');
firstPath = filePath;
}else if (secondPath == '') {
print('b');
secondPath = filePath;
_checkImage(firstPath, secondPath).then((value) {
print(value);
}).catchError((error) {
print(error);
});
firstPath = '';
secondPath = '';
}
});
} on CameraException catch (e) {
print([e.code, e.description]);
return true;
}
return false;
}
String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
Future<Directory> getAppFolder() async =>
await getApplicationDocumentsDirectory();
}
AWS rekognition code
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:intl/intl.dart';
import 'testb.dart';
class RekognitionHandler {
final String _accessKey, _secretKey, _region;
RekognitionHandler(this._accessKey, this._secretKey, this._region);
Future<String> _rekognitionHttp(String amzTarget, String body) async {
String endpoint = "https://rekognition.$_region.amazonaws.com/";
String host = "rekognition.$_region.amazonaws.com";
String httpMethod = "POST";
String service = "rekognition";
var now = new DateTime.now().toUtc();
var amzFormatter = new DateFormat("yyyyMMdd'T'HHmmss'Z'");
String amzDate =
amzFormatter.format(now); // format should be '20170104T233405Z"
var dateFormatter = new DateFormat('yyyyMMdd');
String dateStamp = dateFormatter.format(
now); // Date w/o time, used in credential scope. format should be "20170104"
int bodyLength = body.length;
String queryStringParamters = "";
Map<String, String> headerParamters = {
"content-length": bodyLength.toString(),
"content-type": "application/x-amz-json-1.1",
"host": host,
"x-amz-date": amzDate,
"x-amz-target": amzTarget
};
String signature = Signature.generateSignature(
endpoint,
service,
_region,
_secretKey,
httpMethod,
now,
queryStringParamters,
headerParamters,
body);
String authorization =
"AWS4-HMAC-SHA256 Credential=$_accessKey/$dateStamp/$_region/$service/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-target, Signature=$signature";
headerParamters.putIfAbsent('Authorization', () => authorization);
//String labelsArray = "";
StringBuffer builder = new StringBuffer();
try {
HttpClient httpClient = new HttpClient();
httpClient.connectionTimeout = Duration(minutes: 10);
HttpClientRequest request = await httpClient.postUrl(Uri.parse(endpoint));
request.headers.set('content-length', headerParamters['content-length']);
request.headers.set('content-type', headerParamters['content-type']);
request.headers.set('host', headerParamters['host']);
request.headers.set('x-amz-date', headerParamters['x-amz-date']);
request.headers.set('x-amz-target', headerParamters['x-amz-target']);
request.headers.set('Authorization', headerParamters['Authorization']);
request.write(body);
HttpClientResponse response = await request.close();
await for (String a in response.transform(utf8.decoder)) {
builder.write(a);
}
} catch (e) {
print(e);
}
return Future.value(builder.toString());
}
Future<String> compareFaces(
File sourceImagefile, File targetImagefile) async {
try {
List<int> sourceImageBytes = sourceImagefile.readAsBytesSync();
String base64SourceImage = base64Encode(sourceImageBytes);
List<int> targetImageBytes = targetImagefile.readAsBytesSync();
String base64TargetImage = base64Encode(targetImageBytes);
String body =
'{"SourceImage":{"Bytes": "$base64SourceImage"},"TargetImage":{"Bytes": "$base64TargetImage"}}';
String amzTarget = "RekognitionService.CompareFaces";
String response = await _rekognitionHttp(amzTarget, body);
return response;
} catch (e) {
print(e);
return "{}";
}
}
}
I have the following interfaces
public interface IRibbonCommandsProvider
{
IEnumerable<IRibbonCommand> GetRibbonCommands();
}
public interface IRibbonCommand
{
string Group { get; }
string Tab { get; }
string Name { get; }
string Image { get; }
void Execute();
}
And the follwing substitution code:
public class TabsViewModelTests
{
[Fact]
public void Initialize_BuildsCorrectRibbonTree()
{
var commands = Substitute.For<IRibbonCommandsProvider>();
commands.GetRibbonCommands().Returns(
new[]
{
new RibbonCommand { Tab = "Tab1", Group = "Group1", Name = "Name1" },
new RibbonCommand { Tab = "Tab1", Group = "Group1", Name = "Name2" },
new RibbonCommand { Tab = "Tab2", Group = "Group1", Name = "Name3" },
new RibbonCommand { Tab = "Tab2", Group = "Group2", Name = "Name3" }
});
...
}
private class RibbonCommand : IRibbonCommand
{
public string Group { get; set; }
public string Tab { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public void Execute() {}
}
}
Using NSubstitute, is there a clever way to get rid of the stub RibbonCommand class (that is nothing but a fake IRibbonCommand implementation - and that's NSubstitute's job) and still have list of fake ribbon commands that is as easily readable as the above?.
I can't come up with a readable way using NSubsitute's .Returns() fluent method without ending with a lot more (and unreadable) code.
Update:
A cool NSubstitute extension method could look like this. I just don't know if and how this can be built:
public static ConfiguredCall ReturnsMany<T>(
this IEnumerable<T> value,
Action<T> configureThis,
params Action<T>[] configureThese)
{
...
}
It would be used like this:
commands.GetRibbonCommands().ReturnsMany(
subst =>
{
subst.Tab.Returns("Tab1");
subst.Group.Returns("Group1");
subst.Name.Returns("Name1");
},
subst =>
{
subst.Tab.Returns("Tab1");
subst.Group.Returns("Group1");
subst.Name.Returns("Name2");
},
subst =>
{
subst.Tab.Returns("Tab2");
subst.Group.Returns("Group1");
subst.Name.Returns("Name3");
},
subst =>
{
subst.Tab.Returns("Tab2");
subst.Group.Returns("Group1");
subst.Name.Returns("Name3");
});
I think what you've got is very good — quite succinct and clear.
If you really want to get rid of the class you can use a substitute creation method for IRibbonCommand:
private IRibbonCommand Create(string tab, string group, string name)
{
var cmd = Substitute.For<IRibbonCommand>();
cmd.Tab.Returns(tab);
cmd.Group.Returns(group);
cmd.Name.Returns(name);
return cmd;
}
[Fact]
public void Initialize_BuildsCorrectRibbonTree()
{
var ribbonCommands = new[] {
Create("tab1", "group1", "name1"),
Create("tab1", "group1", "name2"),
Create("tab2", "group1", "name3"),
Create("tab2", "group1", "name4")
};
var commands = Substitute.For<IRibbonCommandsProvider>();
commands.GetRibbonCommands().Returns(ribbonCommands);
// ...
}
This doesn't buy you much, although it does mean your test code will be more protected from changes to the IRibbonCommand interface (e.g. an additional property will not require changing your test code), and means you can check received calls and stub other calls on individual items.
Aside: Can use argument names if you want to more closely match the original code:
Create(tab: "tab1", group: "group1", name: "name1"),
As alternative you may setup Command inside test. Then move config func out of the test and optionally generalize for other types as you go. Yagni it.
UPDATED to working test
[Test]
public void Test()
{
Func<Action<IRibbonCommand>, IRibbonCommand> cmd = config =>
{
var c = Substitute.For<IRibbonCommand>();
config(c);
return c;
};
var ribbonCommands = new[]
{
cmd(c => { c.Tab.Returns("Tab1"); c.Group.Returns("Group1"); c.Name.Returns("Name1"); }),
cmd(c => { c.Tab.Returns("Tab1"); c.Group.Returns("Group1"); c.Name.Returns("Name2"); }),
cmd(c => { c.Tab.Returns("Tab2"); c.Group.Returns("Group1"); c.Name.Returns("Name3"); }),
cmd(c => { c.Tab.Returns("Tab2"); c.Group.Returns("Group1"); c.Name.Returns("Name4"); })
};
var commandsProvider = Substitute.For<IRibbonCommandsProvider>();
commandsProvider.GetRibbonCommands().Returns(ribbonCommands);
}
I don't see anything out of the box that's going to do what you're after. One option might be for you to write your own extension method to make the construction easier. So, something like this:
public static class ReadOnlySubstitute {
static public T For<T>(object source) where T : class {
var sub = Substitute.For<T>();
foreach (var prop in source.GetType().GetProperties()) {
sub.GetType().GetProperty(prop.Name).GetValue(sub).Returns(prop.GetValue(source));
}
return sub;
}
}
The above code essentially creates a substitute for the given interface and then sets up a return on each of properties specified in the supplied object.
This could then be used in your test like this to supply anonymous objects with the parameters:
[Test]
public void Initialize_BuildsCorrectRibbonTree() {
var ribbonCommands = new[]
{
ReadOnlySubstitute.For<IRibbonCommand>(new {Tab="Tab1", Group="Grp1", Name="Nam1"}),
ReadOnlySubstitute.For<IRibbonCommand>(new {Tab="Tab1", Group="Grp1", Name="Nam2"}),
ReadOnlySubstitute.For<IRibbonCommand>(new {Tab="Tab2", Group="Grp1", Name="Nam3"}),
ReadOnlySubstitute.For<IRibbonCommand>(new {Tab="Tab2", Group="Grp2", Name="Nam3"})
};
var commands = Substitute.For<IRibbonCommandsProvider>();
commands.GetRibbonCommands().Returns(ribbonCommands);
....
}
It's not quite as concise as using the RibbonCommand class, since you have to construct the array before passing it into the Returns method because NSubstitute gets confused if you try to setup the Returns on the elements at the same time as on the GetRibbonCommands, but I think it's fairly close.
This is really an enhancement (subjective) of #dadhi's answer, combined with an answer from #David Tchepak to a different question.
So, rather than having to create a new Func for each interface your want to use, as described by #dadhi, you can instead create a generic method that takes an Action. You could be this in a shared class, something like this:
static class ConfiguredSub {
public static T For<T>(Action<T> config) where T : class {
var c = Substitute.For<T>();
config(c);
return c;
}
}
The problem that I encountered with my other answer was that if you have nested Returns, NSubstitute gets confused and starts throwing exceptions. It turns out that as described by #David here, you can pass a Func to defer the execution and get round this issue. If you combine these two things, then you get something pretty close to what you're after.
[Test]
public void Initialize_BuildsCorrectRibbonTree() {
var commands = Substitute.For<IRibbonCommandsProvider>();
commands.GetRibbonCommands().Returns(x => new[] {
ConfiguredSub.For<IRibbonCommand>(subst =>
{
subst.Tab.Returns("Tab1");
subst.Group.Returns("Group1");
subst.Name.Returns("Name1");
}),
ConfiguredSub.For<IRibbonCommand>(subst =>
{
subst.Tab.Returns("Tab1");
subst.Group.Returns("Group1");
subst.Name.Returns("Name2");
}),
ConfiguredSub.For<IRibbonCommand>(subst =>
{
subst.Tab.Returns("Tab2");
subst.Group.Returns("Group1");
subst.Name.Returns("Name3");
}),
ConfiguredSub.For<IRibbonCommand>(subst =>
{
subst.Tab.Returns("Tab2");
subst.Group.Returns("Group1");
subst.Name.Returns("Name4");
})
});
// ...
}
In unit test, i passed the assembly of Custom Activity to the ctor as the local assembly
var xamlInjector = new XamlInjector("CreditAtRenewalFlow.xaml", typeof(CreateFollowUp).Assembly);
CreateFollowUp is a AsynCodeActivity
I got error "'Unexpected 'PROPERTYELEMENT' in parse rule 'Element ::= . EmptyElement | ( StartElement ElementBody ).'.' Line number '2' and line position '4'." at execution of the following line
var host = WorkflowInvokerTest.Create(xamlInjector.GetActivity());
The sample of the unit test is [TestMethod]
[DeploymentItem(#"src\ProcessFlows\Activity1.xaml")]
public void Activity1Test()
{
var xamlInjector = new XamlInjector("Activity1.xaml", typeof(CreateFollowUp).Assembly);
xamlInjector.ReplaceAll(typeof(CreateFollowUp), typeof (MockCreateFollowUp));
var mockExternalServiceManager = new Mock<IExternalServices>();
mockExternalServiceManager.Setup(x => x.CreateFollowUp()).Verifiable();
var host = WorkflowInvokerTest.Create(xamlInjector.GetActivity());
dynamic parameterValues1 = new WorkflowArguments();
parameterValues1.value1 = mockExternalServiceManager.Object;
IDictionary<string, object> dictionary = host.TestActivity();
}
And the CreateFollowUp is given below
public sealed class CreateFollowUp : AsyncCodeActivity
{
[RequiredArgument]
public InArgument ExternalServices { get; set; }
protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback,
object state)
{
Action createFollowUp = this.ExternalServices.Get(context).CreateFollowUp;
context.UserState = createFollowUp;
return createFollowUp.BeginInvoke(callback, state);
}
protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
{
var createFollowUp = context.UserState as Action;
if (createFollowUp == null)
{
throw new ArgumentNullException("The AsyncState of the IAsyncResult was not of the type
ExternalServices.AsyncCreateFollowUp.", (Exception)null);
}
createFollowUp.EndInvoke(result);
}
}
try to modify source code of the activity. Change "xmlns:local="clr-namespace:Activity1" to xmlns:local="clr-namespace:Activity1;assembly=Activity1".
include the assembly in namespaces references (use the correct assembly name)