【问题标题】:jest .each name access object keyjest .each 名称访问对象键
【发布时间】:2019-11-10 00:13:05
【问题描述】:

是否可以在.eachname 部分访问对象的键?

let accounts =
    [
        {
            details:
            {
                company_name:
                    "company_name",
                email,
                password:
                    "asdf",
            },
            find:
            [
                "_id",
                "company_name",
                "email",
                "type",
            ],
            type:
                "creator"
        },
        {
            details:
            {
                email,
                first_name:
                    "first_name",
                last_name:
                    "last_name",
                password:
                    "asdf",
            },
            find:
            [
                "_id",
                "email",
                "first_name",
                "last_name",
                "type",
            ],
            type:
                "user"
        },
    ]

describe.each(accounts)(
    "%s", // <-- access the 'type' key, e.g. account.type
    function (account)
    {
        // test code
    }
)

【问题讨论】:

  • 你是什么意思,“对象的密钥”?一个对象包含键。你是说索引吗?
  • @JackBashford 试图访问对象中的type
  • 哦。所以第一次迭代是creator,第二次迭代是user。对吗?
  • @JackBashford 是的
  • 因为describe.each 使用util.format 生成名称,所以我看不到实现目标的方法。 util.format does not provide a way 访问特定属性

标签: javascript arrays object ecmascript-6 jestjs


【解决方案1】:

Jest describe.each 在第一个参数中需要一个数组数组。如果您传入一维数组,则在内部它将被映射到数组数组(即,将[1, 2, 3] 作为第一个参数传递将被转换为[[1], [2], [3]])。

数组中的每个数组都用作测试套件的数据。因此,在前面的示例中,describe.each 将生成三个测试套件,第一个以1 作为数据,第二个以2 作为数据,第三个以3 作为数据。

现在,在测试套件名称中,您只能格式化您提供给它的参数。在您的情况下,您将 accounts 数组的每个对象中的数据传递给每个测试套件。因此,当您在测试套件名称中设置格式说明符时,它们将应用于整个帐户对象(即您的示例中的 %s 将对您的对象进行字符串化,从而产生 [object Object])。不幸的是,我认为您不能将格式说明符应用于对象的键。

一些想法来完成你想要的:

解决方案 1

如果您使用 %s 格式化程序来组成测试套件名称,则会调用 Object 的 toString 方法(默认返回 [object Object])。

如果您在每个帐户对象中定义toString 方法,则将使用该方法。因此,我们可以使用此代码将toString 方法添加到每个帐户对象(请注意,我们添加的toString 方法正在返回type 键的值):

const accounts = [{
    details: {
        company_name: "company_name",
        email: "aa",
        password: "asdf",
    },
    find: [ "_id", "company_name", "email", "type", ],
    type: "creator"
}, {
    details: {
        email: 'bb',
        first_name: "first_name",
        last_name: "last_name",
        password: "asdf",
    },
    find: [ "_id", "email", "first_name", "last_name", "type", ],
    type: "user"
}].map(account => Object.assign(account, { toString: function() { return this.type; } }));

现在,使用 %s 格式说明符,您应该可以在每个测试套件中看到帐户类型:

describe.each(accounts)(
    "%s", // <-- This will cause the toString method to be called.
    function (account)
    {
        // test code
    }
)

解决方案 2

您始终可以重新定义每个测试套件数据,以便第一个参数是帐户类型(请注意,现在 accounts 是一个二维数组):

let accounts = [
    [
        "creator",
        {
            details: {
                company_name: "company_name",
                email: "email",
                password: "asdf",
            },
            find: [ "_id", "company_name", "email", "type", ],
            type: "creator"
        }
    ], [
        "user", 
        {
            details: {
                email: "email",
                first_name: "first_name",
                last_name: "last_name",
                password: "asdf",
            },
            find: [ "_id", "email", "first_name", "last_name", "type", ],
            type: "user"
        },
    ]
]

您现在可以使用第一个参数(即帐户类型)为测试套件命名:

describe.each(accounts)(
    '%s',  // <-- This %s will format the first item in each test suite array.
    function (accountType, account) {
        // test code
    }
); 

请注意,现在您的测试函数接收两个参数,因为每个测试套件数组都有两个元素。第一个是账户类型,第二个是账户数据。

解决方案 3

您可以使用describe.each 的标记模板文字形式。使用此解决方案,您不必更改 accounts 数组的当前定义。

describe.each`
    account
    ${accounts[0]}
    ${accounts[1]}
`('$account.type', function (account) { 
    // test code
});

此解决方案的缺点是您必须手动将模板文字中的每个测试套件数据附加到新行中(即,如果您将新元素添加到 accounts 数组中,您必须记住将其添加到模板文字在新行中作为${accounts[2]})。

【讨论】:

    【解决方案2】:

    您可以映射您的初始帐户数组以将每个帐户转换为包含 2 个项目的数组:

    1. 帐户类型
    2. 初始帐户元素

    现在,您可以使用describe 名称中的第一个元素数组

    describe.each(accounts.map(account => [account.type, account]))(
        'testing %s', // %s replaced by account type
        (type, account) => { // note: 2 arguments now
            it('details should be defined ', () => {
                expect(account.details).toBeDefined();
            });
        },
    );
    
    

    【讨论】:

    • 如果您使用快照匹配,您需要为每个测试创建一个唯一的标题,因为这将是快照文件的名称。
    • 我喜欢这个最干净的答案!
    【解决方案3】:

    我对一个对象也有类似的问题。我想根据http错误代码测试错误消息,所以我写了一个这样的测试对象:

    const expectedElements = {
      error: {
        code: 500,
        title: "Problème avec l'API"
      },
      notFound:{
        code: 404,
        title: "Élement absent"
      },
      unauthorized:{
        code: 401,
        title: "Accès non autorisé"
      }
    };
    
    

    我使用Object.entries(obj) 来获取一个数组,其中包含这样编写的条目:['key','value']。我可以在测试中将这些作为两个参数访问。我是这样写的:

    test.each(Object.entries(expectedElements))("NoAccess show the right element for %s",(key,expectedElement)=>{
        const { getByRole } = render(<NoAccess apiStatusCode={expectedElement.code}/>);
        //test code
      });
    
    

    现在我可以根据需要添加任意数量的案例,而不必重写测试或创建数组。我只是在我的expectedElements 对象中写入一个新值。奖励,我还有一个描述性的测试名称!

    【讨论】:

      【解决方案4】:

      正如modern doc 所说,你可以

      通过使用 $variable 注入测试用例对象的属性来生成唯一的测试标题

      这么简单:

      describe.each(accounts)(
          "$type",
          function (account) {
              // tests
          }
      )
      

      您可以像这样访问嵌套对象值:$variable.path.to.value

      同样适用于test.each 级别。

      【讨论】:

      • 确保您为此安装了正确的版本。此功能仅支持27.0+
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多