NSubstitute模拟一个没有out / ref参数的void方法

时间:2019-03-01 14:23:16

标签: c# nsubstitute

我试图模拟没有'out'或'ref'参数的void方法,但无法模拟它。 我想在模拟函数中修改参数

public interface IRandomNumberGenerator
{
    void NextBytes(byte[] buffer);
}

var randomImplementation = Substitute.For<IRandomNumberGenerator>();    
randomImplementation.When(x => x.NextBytes(Arg.Any<byte[]>())).Do(x =>
{
    x[0] = new byte[] {0x4d, 0x65, 0x64, 0x76};
});

但是当我运行此测试时,出现了错误:

  

NSubstitute.Exceptions.ArgumentIsNotOutOrRefException:'无法设置   参数0(Byte []),因为它不是out或ref参数。'

还有其他方法可以更改void方法内的参数吗?

2 个答案:

答案 0 :(得分:0)

x[0]指的是传递给NextBytes的第一个参数,在您的情况下是buffer参数。由于参数不是refout,因此在模拟成员中更改数组引用将不会对调用代码产生任何影响。这样有效地做到了:

class TheMoc : IRandomNumberGenerator
{
    public void NextBytes(byte[] bytes)
    {
        bytes = new byte[] {0x4d, 0x65, 0x64, 0x76};
    }
}

哪个当然不会反映在调用代码中。这就是NSubsitute给您例外的原因。

这已经说不清楚,为什么要这么做了,因为调用代码将从不反映出接口的实际实现对该数组的作用。

因此,当您的调用代码如下:

theGenerator.NextBytes(bytes);

该代码仍将引用“旧”数组,而不是“新”数组(您尝试嘲笑)。

因此,您需要将参数提供为refout,以在您的调用代码中反映该修改。

如果您知道数组将始终包含四个元素,则可以修改数组内容,而不是其引用

randomImplementation.When(x => x.NextBytes(Arg.Any<byte[]>())).Do(x =>
{
    x[0][0] = 0x4d;
    x[0][1] = 0x65;
    x[0][2] = 0x64;
    x[0][3] = 0x76;
});

因此,您不想将任何调用与NextBytes匹配,而是只将提供四个字节的数组的调用与之匹配,从而使您的Arg.Any<byte[]()> 有效 strong> a Arg.Is<byte[]>(x => x.Lengh == 4)

答案 1 :(得分:0)

感谢 HimBromBeere ,我发现自己做错了。

所以我不能用新的填充字节数组传递数据,我需要替换数组中的每个项目

import 'package:flutter/material.dart';

class CalculateCGPAScreen extends StatelessWidget {
  final int noOfSubjectsCount;

  CalculateCGPAScreen(this.noOfSubjectsCount);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: noOfSubjects(noOfSubjectsCount),
      ),
    );
  }

  List<Widget> noOfSubjects(int subjectCount) {
    List<Widget> list = [];
    for (int i = 1; i <= subjectCount; i++) {
      list.add(GradeScreen("Subject $i", 1));
    }
    list.add(RaisedButton(
      onPressed: () {},
      color: Colors.amber,
      splashColor: Colors.amberAccent,
      child: Text("Calculate CGPA"),
    ));
    return list;
  }
}

class GradeScreen extends StatefulWidget {
  final String subjectName;
  final int gradeSystem;
  int _data;
  GradeScreen(this.subjectName, this.gradeSystem);

  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _GradeScreenState();
  }

  set data(int value) {
    _data = value;
  }

  get data => _data;
}

class _GradeScreenState extends State<GradeScreen> {
  String selectedGrade = "S";
  List<int> creditList = [1, 2, 3, 4, 5, 6];
  int selectedCredit = 1;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        Text(widget.subjectName),
        DropdownButton(
          items: getGradeList(widget.gradeSystem).map((String loopValue) {
            return DropdownMenuItem(
              child: Text(loopValue),
              value: loopValue,
            );
          }).toList(),
          onChanged: (String value) {
            setState(() {
              selectedGrade = value;
            });
          },
          value: selectedGrade,
        ),
        DropdownButton(
          items: creditList.map((int credit) {
            return DropdownMenuItem(
              child: Text(credit.toString()),
              value: credit,
            );
          }).toList(),
          onChanged: (int value) {
            setState(() {
              selectedCredit = value;
              widget.data = selectedCredit;
            });
          },
          value: selectedCredit,
        )
      ],
    );
  }

  List<String> getGradeList(int option) {
    List<String> gradeSystem;
    if (option == 1) {
      gradeSystem = ['S', 'A', 'B', 'C', 'D', 'E'];
    } else {
      gradeSystem = ['O', 'A+', 'A', 'B+', 'B'];
    }
    return gradeSystem;
  }
}